1 # SPDX-License-Identifier: GPL-2.0-or-later
4 "name": "Material Library",
8 "location": "Properties > Material",
9 "description": "Material Library VX",
11 "doc_url": "{BLENDER_MANUAL_URL}/addons/materials/material_library.html",
13 "category": "Material",
20 from pathlib
import Path
22 from bpy
.app
.handlers
import persistent
23 from bpy
.props
import (
24 StringProperty
, IntProperty
, BoolProperty
,
25 PointerProperty
, CollectionProperty
27 from bpy
.types
import (
28 Panel
, Menu
, AddonPreferences
, Operator
,
29 PropertyGroup
, UIList
,
33 from rna_prop_ui
import PropertyPanel
37 user_path
= Path(bpy
.utils
.resource_path('USER')).parent
38 matlib_path
= os
.path
.join(user_path
, "matlib")
40 if not os
.path
.exists(matlib_path
):
43 addon_path
= os
.path
.dirname(__file__
)
44 shutil
.copy2(os
.path
.join(addon_path
, "categories.txt"), matlib_path
)
45 shutil
.copy2(os
.path
.join(addon_path
, "templates.blend"), matlib_path
)
46 shutil
.copy2(os
.path
.join(addon_path
, "sample_materials.blend"), matlib_path
)
48 ##debug print variables
49 def dd(*args
, dodir
=False):
57 return path
.replace("\\", "\\\\")
59 def update_search_index(self
, context
):
61 for i
, it
in enumerate(self
.materials
):
67 #isabs sometimes returns true on relpaths
68 if path
and os
.path
.exists(path
) and os
.path
.isfile(path
) and os
.path
.isabs(path
):
70 if bpy
.data
.filepath
and bpy
.path
.relpath(bpy
.data
.filepath
) == bpy
.path
.relpath(path
):
74 #paths are on different drives. No problem then
78 def update_lib_index(self
, context
):
81 def update_cat_index(self
, context
):
82 dd("cat index:", self
.current_category
, self
.filter)
88 def update_filter(self
, context
):
90 dd("filter:", self
.filter, self
.cat_index
, self
.current_category
)
91 # index = self.cat_index
94 # cat = self.current_category
98 # self.current_library.filter = cat
101 def check_index(collection
, index
):
102 count
= len(collection
)
103 return count
>0 and index
<count
and index
>=0
105 def send_command(cmd
, output
="sendmat.py"):
106 bin
= winpath(bpy
.app
.binary_path
)
107 scriptpath
= winpath(os
.path
.join(bpy
.app
.tempdir
, output
))
109 with
open(scriptpath
, "w") as f
:
114 if output
== "createlib.py":
115 code
= subprocess
.call([bin
, "-b", "-P", scriptpath
])
117 libpath
= winpath(bpy
.context
.scene
.matlib
.current_library
.path
)
118 code
= subprocess
.call([bin
, "-b", libpath
, "-P", scriptpath
])
120 #code returns 0 if ok, 1 if not
123 def list_materials(path
, sort
=False):
125 with bpy
.data
.libraries
.load(path
) as (data_from
, data_to
):
126 for mat
in data_from
.materials
:
129 if sort
: list = sorted(list)
132 #category properties (none atm)
133 class EmptyGroup(PropertyGroup
):
135 # bpy.utils.register_class(EmptyGroup)
137 class matlibMaterials(PropertyGroup
):
138 category
: StringProperty()
139 # bpy.utils.register_class(matlibMaterials)
141 #bpy.types.Scene.matlib_categories = CollectionProperty(type=EmptyGroup)
146 #cats = bpy.context.scene.matlib.categories
148 def __init__(self
, cats
):
152 scn
= bpy
.context
.scene
153 cats
= set([cat
.name
for cat
in self
.cats
])
154 libpath
= bpy
.context
.scene
.matlib
.current_library
.path
159 if not hasattr(bpy.context.scene, "matlib_categories"):
160 class EmptyProps(bpy.types.PropertyGroup):
162 bpy.utils.register_class(EmptyProps)
163 bpy.types.Scene.matlib_categories = bpy.props.CollectionProperty(type=EmptyProps)
164 cats = bpy.context.scene.matlib_categories
171 cat.name = "%s" """ % cat
.capitalize()
173 bpy.ops.wm.save_mainfile(filepath="%s", check_existing=False, compress=True)''' % winpath(libpath
)
175 return send_command(cmd
, "save_categories.py")
177 def read(self
, pull
=True):
178 #mandar a imprimir el listado
179 catfile
= winpath(os
.path
.join(matlib_path
, "categories.txt"))
182 class EmptyProps(bpy.types.PropertyGroup):
184 bpy.utils.register_class(EmptyProps)
185 bpy.types.Scene.matlib_categories = bpy.props.CollectionProperty(type=EmptyProps)
187 for cat in bpy.context.scene.matlib_categories:
189 for mat in bpy.data.materials:
190 if "category" in mat.keys() and mat['category'] == cat.name:
191 materials.append(mat.name)
192 cats.append([cat.name, materials])
193 with open("%s", "w") as f:
194 f.write(json.dumps(cats, sort_keys=True, indent=4))
196 if pull
: send_command(cmd
)
199 with
open(catfile
, "r") as f
:
200 cats
= json
.loads(f
.read())
204 # #refrescar categorias
205 # for cat in self.cats:
206 # self.cats.remove(0)
209 # item = self.cats.add()
215 for cat
in self
.cats
:
219 if name
and name
not in [item
.name
for item
in self
.cats
]:
220 name
= name
.strip().capitalize()
221 item
= self
.cats
.add()
229 def remove(self
, index
):
230 self
.cats
.remove(index
)
235 def __init__(self
, matlib_path
, name
):
237 self
.path
= os
.path
.join(matlib_path
, name
)
240 # return self.name == default_library
245 # return "Default Library"
246 return bpy
.path
.display_name(self
.name
).title()
250 return str(type(self
).__name
__) + "('" + self
.name
+ "')"
252 #bpy.utils.register_class(Library)
255 libs
= [Library(matlib_path
, f
) for f
in os
.listdir(matlib_path
) if f
[-5::] == "blend"]
256 return sorted(libs
, key
=lambda x
: bpy
.path
.display_name(x
.name
))
262 class matlibProperties(PropertyGroup
):
266 #libraries are read from the xml
267 lib_index
: IntProperty(min = -1, default
= 2, update
=update_lib_index
)
268 all_materials
: CollectionProperty(type = matlibMaterials
)
269 materials
: CollectionProperty(type = matlibMaterials
)
270 mat_index
: IntProperty(min = -1, default
= -1)
271 categories
: CollectionProperty(type = EmptyGroup
)
272 cat_index
: IntProperty(min = -1, default
= -1, update
=update_cat_index
)
273 search
: StringProperty(name
="Search", description
="Find By Name", update
=update_search_index
)
276 #link: import material linked
278 # if disable it wont import a material if its present in the scene,(avoid duplicates)
279 # instead it will apply the scene material rather than importing the same one from the library
280 #filter: enable or disable category filter
281 #last selected: store the last selected object to regain focus when apply a material.
282 #hide_search: Hides Search Field
283 link
: BoolProperty(name
= "Linked", description
="Link the material", default
= False)
284 force_import
: BoolProperty(name
= "Force Import", description
="Use Scene Materials by default", default
= False)
285 filter: BoolProperty(name
= "Filter",description
="Filter Categories", default
= True, update
=update_filter
)
286 show_prefs
: BoolProperty(name
= "show_prefs", description
="Preferences", default
= False)
287 last_selected
: StringProperty(name
="Last Selected")
288 hide_search
: BoolProperty(name
="Hide Search", description
="Use Blender Search Only")
289 #import_file = StringProperty("Import File", subtype="FILE_PATH")
290 #path = os.path.dirname(path)
299 def current_library(self
):
300 if check_index(libraries
, self
.lib_index
):
301 return libraries
[self
.lib_index
]
304 def active_material(self
):
305 if check_index(self
.materials
, self
.mat_index
):
306 return self
.materials
[self
.mat_index
]
309 dd("loading libraries")
310 if self
.current_library
:
312 elif self
.lib_index
== -1 and len(libraries
):
316 def add_library(self
, path
, setEnabled
= False):
318 ext
= os
.path
.extsep
+ "blend"
319 if not path
.endswith(ext
):
323 # if path == default_library:
324 # return 'ERROR', "Cannot add default library."
325 #if path in [lib.path for lib in self.libraries]:
326 return 'ERROR', "Library already exists."
328 dd("Can't find " + path
)
332 bpy.ops.wm.save_mainfile(filepath="%s", check_existing=False, compress=True)''' % winpath(path
)
333 if not (send_command(cmd
, "createlib.py")):
334 return 'ERROR', "There was an error creating the file. Make sure you run Blender with admin rights."
336 #self.libraries = sorted(self.libraries, key=lambda lib: sortlibs(lib))
337 dd("adding library", path
)
339 libraries
= get_libraries()
340 return "INFO", "Library added"
342 def load_library(self
):
343 self
.empty_list(True)
344 if not self
.current_library
:
345 return 'ERROR', "Library not found!."
347 path
= self
.current_library
.path
349 dd("loading library", self
.lib_index
, path
)
355 categories
= Categories(self
.categories
)
356 self
.cats
= categories
.read(True)
357 self
.load_categories()
359 for mat
in self
.all_materials
:
360 self
.all_materials
.remove(0)
362 for mat
in list_materials(self
.current_library
.path
, True):
363 item
= self
.all_materials
.add()
365 for cat
in self
.cats
:
367 item
.category
= cat
[0]
372 return 'ERROR', "Library not found!."
374 def update_list(self
):
377 if self
.current_library
:
378 current_category
= self
.current_category
379 #sorteditems = sorted(self.all_materials, key=lambda x: x.name)
380 for mat
in self
.all_materials
:
381 #print(current_category, mat.category)
382 if not self
.filter or (self
.filter and mat
.category
== current_category
) or current_category
== "":
383 item
= self
.materials
.add()
385 item
.category
= mat
.category
387 def empty_list(self
, cats
= False):
389 for it
in self
.materials
:
390 self
.materials
.remove(0)
393 for c
in self
.categories
:
394 self
.categories
.remove(0)
398 def current_category(self
):
399 #print(self.mat_index)
400 if check_index(self
.categories
, self
.cat_index
):
401 return self
.categories
[self
.cat_index
].name
404 def load_categories(self
):
406 for c
in self
.categories
:
407 self
.categories
.remove(0)
410 cat
= self
.categories
.add()
413 def add_category(self
, name
):
415 name
= name
.strip().title()
416 dd("add category", name
)
417 categories
= Categories(self
.categories
)
422 # cat = xml.find("category", name, lib, create = True)
423 # self.load_categories()
425 # return 'ERROR', "Library not found"
426 def remove_category(self
):
427 dd("removing category", self
.current_category
)
428 categories
= Categories(self
.categories
)
429 categories
.remove(self
.cat_index
)
431 def set_category(self
):
432 mat
= self
.active_material
433 #dd(lib, mat, self.current_category)
436 if self
.cat_index
>-1:
437 dd(self
.current_category
)
438 cat
= self
.current_category
439 if cat
== self
.all_materials
[self
.mat_index
].category
:
444 mat = bpy.data.materials['%s']
448 mat['category'] = "%s"
449 bpy.ops.wm.save_mainfile(filepath="%s", check_existing=False, compress=True)
450 """ % (mat
.name
, cat
, winpath(self
.current_library
.path
))
451 if send_command(cmd
):
452 self
.all_materials
[self
.mat_index
].category
= cat
455 return "WARNING", "There was an error."
457 # catnode = xml.find("category", self.current_category, lib, True)
458 # matnode = xml.find("material", mat.name, lib)
460 # catnode.appendChild(matnode)
462 # matnode = xml.find("material", mat.name, catnode, True)
465 # self.current_library.materials[self.mat_index].category = cat
466 #remove mat from any category
469 self
.all_materials
[self
.mat_index
].category
= ""
471 return "WARNING", "Select a material"
473 def get_material(self
, name
, link
=False):
474 with bpy
.data
.libraries
.load(self
.current_library
.path
, link
=link
, relative
=False) as (data_from
, data_to
):
475 data_to
.materials
= [name
]
477 print(name
+ " linked.")
479 print(name
+ " appended.")
481 def apply(self
, context
, preview
=False):
482 name
= self
.active_material
.name
483 if not name
: return "WARNING", "Select a material from the list."
485 linked
= self
.link
or preview
486 force
= self
.force_import
or linked
489 active
= context
.object
490 dummy
= self
.get_dummy(context
)
494 if context
.mode
== "EDIT_MESH":
495 return "WARNING", "Can't preview on EDIT MODE"
497 self
.last_selected
= context
.object.name
498 context
.view_layer
.objects
.active
= dummy
499 objects
.append(dummy
)
502 objects
= [obj
for obj
in context
.selected_objects
if hasattr(obj
.data
, "materials")]
505 return "INFO", "Please select an object"
507 if dummy
== context
.object and not preview
:
508 if (len(objects
)==1 and dummy
.select_get()):
509 return "ERROR", "Apply is disabled for the Material Preview Object"
511 last
= context
.scene
.objects
[self
.last_selected
]
512 if last
in context
.selected_objects
:
513 context
.view_layer
.objects
.active
= last
515 self
.last_selected
= ""
517 context
.view_layer
.objects
.active
= None
518 dummy
.select_set(False)
519 #objects = context.selected_objects
523 #mira si hay materiales linkados de la libreria actual
524 for mat
in bpy
.data
.materials
:
526 samelib
= bpy
.path
.relpath(mat
.library
.filepath
) == bpy
.path
.relpath(self
.current_library
.path
)
530 if mat
.name
== name
and mat
.library
and samelib
:
532 dd("encontre linked", name
, "no importo nada")
536 #busca materiales no linkados
537 for mat
in bpy
.data
.materials
:
538 if mat
.name
== name
and not mat
.library
:
540 dd("encontre no linkado", name
, "no importo nada")
546 nmats
= len(bpy
.data
.materials
)
548 self
.get_material(name
, linked
)
550 if not self
.force_import
:
552 material
= bpy
.data
.materials
[name
]
557 if nmats
== len(bpy
.data
.materials
) and not linked
:
558 return "ERROR", name
+ " doesn't exists at library " + str(linked
)
560 for mat
in reversed(bpy
.data
.materials
):
561 if mat
.name
[0:len(name
)] == name
:
562 #careful on how blender writes library paths
564 samelib
= bpy
.path
.relpath(mat
.library
.filepath
) == bpy
.path
.relpath(self
.current_library
.path
)
568 if linked
and mat
.library
and samelib
:
570 dd(name
, "importado con link")
574 dd(name
, "importado sin link")
578 material
.use_fake_user
= False
579 material
.user_clear()
581 print ("Material", material
, force
)
584 #maybe some test cases doesn't return a material, gotta take care of that
585 #i cannot think of any case like that right now
586 #maybe import linked when the database isn't sync
587 if context
.mode
== "EDIT_MESH":
591 for i
, mat
in enumerate(obj
.data
.materials
):
597 obj
.data
.materials
.append(material
)
598 index
= len(obj
.data
.materials
)-1
601 bm
= bmesh
.from_edit_mesh(obj
.data
)
604 f
.material_index
= index
608 index
= obj
.active_material_index
609 if index
< len(obj
.material_slots
):
610 obj
.material_slots
[index
].material
= None
611 obj
.material_slots
[index
].material
= material
613 obj
.data
.materials
.append(material
)
616 bpy
.ops
.object.make_local(type="SELECT_OBDATA_MATERIAL")
618 def add_material(self
, mat
):
621 return 'WARNING', "Select a material from the scene."
624 thispath
= winpath(bpy
.data
.filepath
)
625 libpath
= winpath(self
.current_library
.path
)
628 return 'WARNING', "Save this file before export."
631 return 'WARNING', "Library not found!."
633 elif bpy
.data
.is_dirty
:
634 bpy
.ops
.wm
.save_mainfile(check_existing
=True)
637 return 'WARNING', 'Cannot export linked materials.'
639 dd("adding material", name
, libpath
)
642 if name
in list_materials(libpath
):
644 mat = bpy.data.materials["%s"]
646 mat.use_fake_user = False
647 mat.user_clear()''' % name
651 with bpy.data.libraries.load("{1}") as (data_from, data_to):
652 data_to.materials = ["{2}"]
653 mat = bpy.data.materials["{2}"]
654 mat.use_fake_user=True
655 bpy.ops.file.pack_all()
656 bpy.ops.wm.save_mainfile(filepath="{3}", check_existing=False, compress=True)
657 '''.format(overwrite
, thispath
, name
, libpath
)
659 if send_command(cmd
):
662 item
= self
.all_materials
.add()
664 if "category" in mat
.keys():
665 item
.category
= mat
['category']
666 #reorder all_materials
667 items
= sorted([[item
.name
, item
.category
] for item
in self
.all_materials
], key
= lambda x
: x
[0])
669 self
.all_materials
.clear()
671 item
= self
.all_materials
.add()
673 item
.category
= it
[1]
677 return 'INFO', "Material added."
679 print("Save Material Error: Run Blender with administrative privileges.")
680 return 'WARNING', "There was an error saving the material"
682 def remove_material(self
):
683 name
= self
.active_material
.name
684 libpath
= winpath(self
.current_library
.path
)
685 if name
and libpath
and name
in list_materials(libpath
):
687 mat = bpy.data.materials["%s"]
688 mat.use_fake_user = False
690 bpy.ops.wm.save_mainfile(filepath="%s", check_existing=False, compress=True)''' % (name
, libpath
)
691 if send_command(cmd
, "removemat.py"):
692 self
.all_materials
.remove(self
.mat_index
)
695 return 'ERROR', "There was an error."
696 return "INFO", name
+ " removed."
698 def get_dummy(self
, context
):
699 dummy_name
= "Material_Preview_Dummy"
700 dummy_mesh
= "Material_Preview_Mesh"
703 dummy
= scn
.objects
[dummy_name
]
707 me
= bpy
.data
.meshes(dummy_mesh
)
709 me
= bpy
.data
.meshes
.new(dummy_mesh
)
710 dummy
= bpy
.data
.objects
.new(dummy_name
, me
)
711 context
.collection
.objects
.link(dummy
)
714 dummy
.hide_render
= True
715 dummy
.hide_select
= True
718 # bpy.utils.register_class(matlibProperties)
719 # Scene.matlib = PointerProperty(type = matlibProperties)
722 class MATLIB_MT_LibsMenu(Menu
):
723 bl_label
= "Libraries Menu"
725 def draw(self
, context
):
728 #layout.operator("matlib.operator", text="Default Library").cmd="lib-1"
729 for i
, lib
in enumerate(libs
):
730 layout
.operator("matlib.operator", text
=lib
.shortname
).cmd
="lib"+str(i
)
732 class MATLIB_MT_CatsMenu(Menu
):
733 bl_label
= "Categories Menu"
735 def draw(self
, context
):
737 cats
= context
.scene
.matlib
.categories
738 layout
.operator("matlib.operator", text
="All").cmd
="cat-1"
739 for i
, cat
in enumerate(cats
):
740 layout
.operator("matlib.operator", text
=cat
.name
).cmd
="cat"+str(i
)
743 #class MATLIB_OT_add(Operator):
744 # """Add Active Material"""
746 # bl_idname = "matlib.add_material"
749 # def poll(cls, context):
750 # return context.active_object is not None
752 # def execute(self, context):
754 # return {"FINISHED"}
758 class MATLIB_OT_add(Operator
):
759 """Add active material to library"""
760 bl_idname
= "matlib.add"
761 bl_label
= "Add active material"
764 def poll(cls
, context
):
765 obj
= context
.active_object
766 return obj
is not None and obj
.active_material
is not None
768 def execute(self
, context
):
769 matlib
= context
.scene
.matlib
770 success
= matlib
.add_material(context
.object.active_material
)
771 if type(success
).__name
__ == "tuple":
773 self
.report({success
[0]}, success
[1])
776 class MATLIB_OT_remove(Operator
):
777 """Remove material from library"""
778 bl_idname
= "matlib.remove"
779 bl_label
= "Remove material from library"
782 def poll(cls
, context
):
783 matlib
= context
.scene
.matlib
784 return check_index(matlib
.materials
, matlib
.mat_index
)
786 def execute(self
, context
):
787 matlib
= context
.scene
.matlib
788 success
= matlib
.remove_material()
789 if type(success
).__name
__ == "tuple":
791 self
.report({success
[0]}, success
[1])
794 class MATLIB_OT_remove(Operator
):
796 bl_idname
= "matlib.reload"
797 bl_label
= "Reload library"
800 # def poll(cls, context):
801 # matlib = context.scene.matlib
802 # index = matlib.mat_index
803 # l = len(matlib.materials)
804 # return l>0 and index >=0 and index < l
806 def execute(self
, context
):
807 matlib
= context
.scene
.matlib
808 success
= matlib
.reload()
809 if type(success
).__name
__ == "tuple":
811 self
.report({success
[0]}, success
[1])
815 class MATLIB_OT_apply(Operator
):
816 """Apply selected material"""
817 bl_idname
= "matlib.apply"
818 bl_label
= "Apply material"
821 def poll(cls
, context
):
822 matlib
= context
.scene
.matlib
823 index
= matlib
.mat_index
824 l
= len(matlib
.materials
)
825 obj
= context
.active_object
826 return l
>0 and index
>=0 and index
< l
and obj
is not None
828 def execute(self
, context
):
829 matlib
= context
.scene
.matlib
830 success
= matlib
.apply(context
, False)
831 if type(success
).__name
__ == "tuple":
833 self
.report({success
[0]}, success
[1])
837 class MATLIB_OT_preview(Operator
):
838 """Preview selected material"""
839 bl_idname
= "matlib.preview"
840 bl_label
= "Preview selected material"
843 def poll(cls
, context
):
844 matlib
= context
.scene
.matlib
845 index
= matlib
.mat_index
846 l
= len(matlib
.materials
)
847 obj
= context
.active_object
848 return l
>0 and index
>=0 and index
< l
850 def execute(self
, context
):
851 matlib
= context
.scene
.matlib
852 success
= matlib
.apply(context
, True)
853 if type(success
).__name
__ == "tuple":
855 self
.report({success
[0]}, success
[1])
859 class MATLIB_OT_flush(Operator
):
860 """Flush unused materials"""
861 bl_idname
= "matlib.flush"
862 bl_label
= "Flush unused materials"
865 def poll(cls
, context
):
866 matlib
= context
.scene
.matlib
867 index
= matlib
.mat_index
868 l
= len(matlib
.materials
)
869 obj
= context
.active_object
870 return l
>0 and index
>=0 and index
< l
872 def execute(self
, context
):
873 matlib
= context
.scene
.matlib
874 dummy
= matlib
.get_dummy(context
)
875 if dummy
== context
.object:
877 context
.view_layer
.objects
.active
= context
.scene
.objects
[matlib
.last_selected
]
881 for slot
in dummy
.material_slots
:
884 for mat
in bpy
.data
.materials
:
887 print (mat
.name
, "removed.")
888 bpy
.data
.materials
.remove(mat
)
890 plural
= "" if i
== 1 else "s"
891 self
.report({'INFO'}, str(i
) + " material"+plural
+" removed.")
896 class MATLIB_OT_operator(Operator
):
897 """Add, Remove, Reload, Apply, Preview, Clean Material"""
899 bl_idname
= "matlib.operator"
900 __doc__
= "Add, Remove, Reload, Apply, Preview, Clean Material"
902 category
: StringProperty(name
="Category")
903 filepath
: StringProperty(options
={'HIDDEN'})
904 cmd
: bpy
.props
.StringProperty(name
="Command", options
={'HIDDEN'})
905 filter_glob
: StringProperty(default
="*.blend", options
={'HIDDEN'})
907 def poll(cls
, context
):
908 return context
.active_object
is not None
910 def draw(self
, context
):
913 if self
.cmd
== "LIBRARY_ADD":
914 #layout.label(text="Select a blend file as library or")
915 #layout.label(text="Type a name to create a new library.")
916 layout
.prop(self
, "category", text
="Library")
917 elif self
.cmd
== "FILTER_ADD":
918 layout
.prop(self
, "category")
920 def invoke(self
, context
, event
):
925 if cmd
== "LIBRARY_ADD":
926 self
.filepath
= matlib_path
+ os
.path
.sep
927 dd("filepath", self
.filepath
, matlib_path
)
928 #context.window_manager.fileselect_add(self)
929 context
.window_manager
.invoke_props_dialog(self
)
930 return {'RUNNING_MODAL'}
931 elif cmd
== "FILTER_ADD":
932 context
.window_manager
.invoke_props_dialog(self
)
933 return {'RUNNING_MODAL'}
934 return self
.execute(context
)
936 ### TODO: execute doesn't trigger remove
937 def execute(self
, context
):
940 matlib
= context
.scene
.matlib
942 if self
.cmd
== "init":
947 if self
.cmd
[0:3] == "lib":
948 index
= int(self
.cmd
[3::])
949 matlib
.lib_index
= index
950 #success = matlib.load_library()
951 elif self
.cmd
== "LIBRARY_ADD":
952 dd("execute lib add")
953 libname
= self
.category
954 if libname
[-6::] != ".blend": libname
+= ".blend"
955 libname
= os
.path
.join(matlib_path
, libname
)
958 success
= matlib
.add_library(libname
, True)
959 for i
, l
in enumerate(libraries
):
960 if l
.name
== self
.category
:
964 elif self
.cmd
== "RELOAD":
965 success
= matlib
.reload()
967 if not matlib
.current_library
:
968 self
.report({'ERROR'}, "Select a Library")
971 if self
.cmd
== "FILTER_ADD":
972 success
= matlib
.add_category(self
.category
)
973 for i
, cat
in enumerate(matlib
.categories
):
974 if cat
.name
== self
.category
:
978 elif self
.cmd
== "FILTER_REMOVE":
979 matlib
.remove_category()
981 elif self
.cmd
== "FILTER_SET":
982 success
= matlib
.set_category()
984 elif self
.cmd
[0:3] == "cat":
985 index
= int(self
.cmd
[3::])
986 matlib
.cat_index
= index
989 elif self
.cmd
== "ADD":
990 success
= matlib
.add_material(context
.object.active_material
)
992 elif self
.cmd
== "REMOVE":
993 success
= matlib
.remove_material()
996 elif self
.cmd
== "APPLY":
997 success
= matlib
.apply(context
)
999 elif self
.cmd
== "PREVIEW":
1000 success
= matlib
.apply(context
, True)
1002 elif self
.cmd
=="FLUSH":
1003 #release dummy materials
1004 dummy
= matlib
.get_dummy(context
)
1005 if dummy
== context
.object:
1007 context
.view_layer
.objects
.active
= context
.scene
.objects
[matlib
.last_selected
]
1011 for slot
in dummy
.material_slots
:
1012 slot
.material
= None
1014 for mat
in bpy
.data
.materials
:
1017 print (mat
.name
, "removed.")
1018 bpy
.data
.materials
.remove(mat
)
1024 self
.report({'INFO'}, str(i
) + " material"+plural
+" removed.")
1027 elif self
.cmd
== "CONVERT":
1029 lib
= matlib
.current_library
1032 path
= os
.path
.join(matlib_path
, "www")
1033 if not os
.path
.exists(path
):
1035 path
= os
.path
.join(path
, lib
.shortname
)
1036 if not os
.path
.exists(path
):
1039 path
= winpath(path
)
1040 libpath
= winpath(lib
.name
)
1045 #decirle a la libreria que cree un fichero blend por cada material que tenga.
1049 def list_materials():
1051 with bpy.data.libraries.load("{0}") as (data_from, data_to):
1052 for mat in data_from.materials:
1056 def get_material(name, link=False):
1057 with bpy.data.libraries.load("{0}", link=link, relative=False) as (data_from, data_to):
1058 data_to.materials = [name]
1060 print(name + " linked.")
1062 print(name + " appended.")
1064 for scn in bpy.data.scenes:
1065 for obj in scn.objects:
1066 scn.objects.unlink(obj)
1068 bpy.data.objects.remove(obj)
1070 def clean_materials():
1071 for mat in bpy.data.materials:
1073 bpy.data.materials.remove(mat)
1075 bin = bpy.app.binary_path
1076 mats = list_materials()
1077 bpy.context.preferences.filepaths.save_version = 0
1080 matpath = os.path.join("{1}", mat + ".blend")
1083 material = bpy.data.materials[0]
1084 material.use_fake_user = True
1085 bpy.ops.wm.save_mainfile(filepath = matpath, compress=True, check_existing=False)
1086 """.format(libpath
, path
)
1088 send_command(cmd
, "createlib.py")
1090 if type(success
).__name
__ == "tuple":
1092 self
.report({success
[0]}, success
[1])
1097 class MATLIB_PT_vxPanel(Panel
):
1098 bl_label
= "Material Library VX"
1099 bl_space_type
= "PROPERTIES"
1100 bl_region_type
= "WINDOW"
1101 bl_context
= "material"
1102 bl_options
= {'DEFAULT_CLOSED'}
1105 def poll(self
, context
):
1106 return context
.active_object
.active_material
!=None
1108 def draw(self
, context
):
1109 layout
= self
.layout
1110 matlib
= context
.scene
.matlib
1112 #hyper ugly trick but i dont know how to init classes at register time
1113 # if matlibProperties.init:
1114 # matlibProperties.init = False
1118 col
= layout
.column(align
=True)
1119 if matlib
.current_library
:
1120 text
= matlib
.current_library
.shortname
1122 text
= "Select a Library"
1123 split
= col
.split(factor
=0.55, align
=True)
1124 split
.menu("MATLIB_MT_LibsMenu",text
=text
)
1125 split
.operator("matlib.operator", icon
="ADD", text
="New Library").cmd
= "LIBRARY_ADD"
1129 row
.template_list("UI_UL_list", " ", matlib
, "materials", matlib
, "mat_index", rows
=6)
1131 # row = layout.row()
1133 col
.operator("matlib.operator", icon
="ADD", text
="Add To Library").cmd
="ADD"
1134 col
.operator("matlib.operator", icon
="MATERIAL", text
="Apply To Selected").cmd
="APPLY"
1135 col
.operator("matlib.operator", icon
="FILE_REFRESH", text
="Reload Material").cmd
="RELOAD"
1136 col
.operator("matlib.operator", icon
="COLOR", text
="Preview Material").cmd
="PREVIEW"
1137 col
.operator("matlib.operator", icon
="GHOST_DISABLED", text
="Remove Preview").cmd
="FLUSH"
1138 col
.operator("matlib.operator", icon
="REMOVE", text
="Remove Material").cmd
="REMOVE"
1139 col
.prop(matlib
, "show_prefs", icon
="MODIFIER", text
="Settings")
1142 if not matlib
.hide_search
:
1143 row
= layout
.row(align
=True)
1144 row
.prop_search(matlib
, "search", matlib
, "materials", text
="", icon
="VIEWZOOM")
1148 if matlib
.active_material
:
1149 row
.label(text
="Category:")
1150 row
.label(text
= matlib
.active_material
.category
)
1152 row
.label(text
="Category Tools:")
1153 row
= layout
.row(align
=True)
1155 if matlib
.current_category
: text
= matlib
.current_category
1156 row
.menu("MATLIB_MT_CatsMenu",text
=text
)
1157 row
= layout
.row(align
=True)
1158 row
.prop(matlib
, "filter", icon
="FILTER", text
="Filter")
1159 row
.operator("matlib.operator", icon
="FILE_PARENT", text
="Set Type").cmd
="FILTER_SET"
1160 row
.operator("matlib.operator", icon
="ADD", text
="New").cmd
="FILTER_ADD"
1161 row
.operator("matlib.operator", icon
="REMOVE", text
="Remove").cmd
="FILTER_REMOVE"
1164 if matlib
.show_prefs
:
1165 row
= layout
.row(align
=True)
1166 row
.prop(matlib
, "force_import")
1167 row
.prop(matlib
, "link")
1168 row
.prop(matlib
, "hide_search")
1169 # row = layout.row(align=True)
1171 #row.operator("matlib.operator", icon="URL", text="Convert Library").cmd="CONVERT"
1173 # row = layout.row()
1174 # if (matlib.current_library):
1175 # row.label(matlib.current_library.name)
1177 # row.label(text="Library not found!.")
1188 #print(bpy.context.scene)
1192 def refresh_libs(dummy
=None):
1195 default_path
= bpy
.context
.preferences
.addons
[__name__
].preferences
.matlib_path
1196 if default_path
is not None and default_path
!= '':
1197 matlib_path
= default_path
1199 libraries
= get_libraries()
1202 def reload_library(self
, context
):
1203 bpy
.context
.preferences
.addons
[__name__
].preferences
.matlib_path
= bpy
.path
.abspath(bpy
.context
.preferences
.addons
[__name__
].preferences
.matlib_path
)
1207 class matlibvxPref(AddonPreferences
):
1208 bl_idname
= __name__
1209 matlib_path
: StringProperty(
1210 name
="Additional Path",
1211 description
="User defined path to .blend libraries files",
1214 update
=reload_library
1217 def draw(self
, context
):
1218 layout
= self
.layout
1219 layout
.prop(self
, "matlib_path")
1256 bpy
.utils
.register_class(c
)
1257 Scene
.matlib_categories
= CollectionProperty(type=EmptyGroup
)
1258 Scene
.matlib
= PointerProperty(type = matlibProperties
)
1259 bpy
.app
.handlers
.load_post
.append(refresh_libs
)
1267 # raise ValueError list.remove(x): x not in list
1268 del Scene
.matlib_categories
1273 bpy
.app
.handlers
.load_post
.remove(refresh_libs
)
1275 bpy
.utils
.unregister_class(c
)
1278 if __name__
== "__main__":