1 # SPDX-FileCopyrightText: 2017-2022 Blender Foundation
3 # SPDX-License-Identifier: GPL-2.0-or-later
6 "name": "Material Library",
10 "location": "Properties > Material",
11 "description": "Material Library VX",
13 "doc_url": "{BLENDER_MANUAL_URL}/addons/materials/material_library.html",
15 "category": "Material",
22 from pathlib
import Path
24 from bpy
.app
.handlers
import persistent
25 from bpy
.props
import (
26 StringProperty
, IntProperty
, BoolProperty
,
27 PointerProperty
, CollectionProperty
29 from bpy
.types
import (
30 Panel
, Menu
, AddonPreferences
, Operator
,
31 PropertyGroup
, UIList
,
35 from rna_prop_ui
import PropertyPanel
39 user_path
= Path(bpy
.utils
.resource_path('USER')).parent
40 matlib_path
= os
.path
.join(user_path
, "matlib")
42 if not os
.path
.exists(matlib_path
):
45 addon_path
= os
.path
.dirname(__file__
)
46 shutil
.copy2(os
.path
.join(addon_path
, "categories.txt"), matlib_path
)
47 shutil
.copy2(os
.path
.join(addon_path
, "templates.blend"), matlib_path
)
48 shutil
.copy2(os
.path
.join(addon_path
, "sample_materials.blend"), matlib_path
)
50 ##debug print variables
51 def dd(*args
, dodir
=False):
59 return path
.replace("\\", "\\\\")
61 def update_search_index(self
, context
):
63 for i
, it
in enumerate(self
.materials
):
69 #isabs sometimes returns true on relpaths
70 if path
and os
.path
.exists(path
) and os
.path
.isfile(path
) and os
.path
.isabs(path
):
72 if bpy
.data
.filepath
and bpy
.path
.relpath(bpy
.data
.filepath
) == bpy
.path
.relpath(path
):
76 #paths are on different drives. No problem then
80 def update_lib_index(self
, context
):
83 def update_cat_index(self
, context
):
84 dd("cat index:", self
.current_category
, self
.filter)
90 def update_filter(self
, context
):
92 dd("filter:", self
.filter, self
.cat_index
, self
.current_category
)
93 # index = self.cat_index
96 # cat = self.current_category
100 # self.current_library.filter = cat
103 def check_index(collection
, index
):
104 count
= len(collection
)
105 return count
>0 and index
<count
and index
>=0
107 def send_command(cmd
, output
="sendmat.py"):
108 bin
= winpath(bpy
.app
.binary_path
)
109 scriptpath
= winpath(os
.path
.join(bpy
.app
.tempdir
, output
))
111 with
open(scriptpath
, "w") as f
:
116 if output
== "createlib.py":
117 code
= subprocess
.call([bin
, "-b", "-P", scriptpath
])
119 libpath
= winpath(bpy
.context
.scene
.matlib
.current_library
.path
)
120 code
= subprocess
.call([bin
, "-b", libpath
, "-P", scriptpath
])
122 #code returns 0 if ok, 1 if not
125 def list_materials(path
, sort
=False):
127 with bpy
.data
.libraries
.load(path
) as (data_from
, data_to
):
128 for mat
in data_from
.materials
:
131 if sort
: list = sorted(list)
134 #category properties (none atm)
135 class EmptyGroup(PropertyGroup
):
137 # bpy.utils.register_class(EmptyGroup)
139 class matlibMaterials(PropertyGroup
):
140 category
: StringProperty()
141 # bpy.utils.register_class(matlibMaterials)
143 #bpy.types.Scene.matlib_categories = CollectionProperty(type=EmptyGroup)
148 #cats = bpy.context.scene.matlib.categories
150 def __init__(self
, cats
):
154 scn
= bpy
.context
.scene
155 cats
= set([cat
.name
for cat
in self
.cats
])
156 libpath
= bpy
.context
.scene
.matlib
.current_library
.path
161 if not hasattr(bpy.context.scene, "matlib_categories"):
162 class EmptyProps(bpy.types.PropertyGroup):
164 bpy.utils.register_class(EmptyProps)
165 bpy.types.Scene.matlib_categories = bpy.props.CollectionProperty(type=EmptyProps)
166 cats = bpy.context.scene.matlib_categories
173 cat.name = "%s" """ % cat
.capitalize()
175 bpy.ops.wm.save_mainfile(filepath="%s", check_existing=False, compress=True)''' % winpath(libpath
)
177 return send_command(cmd
, "save_categories.py")
179 def read(self
, pull
=True):
180 #mandar a imprimir el listado
181 catfile
= winpath(os
.path
.join(matlib_path
, "categories.txt"))
184 class EmptyProps(bpy.types.PropertyGroup):
186 bpy.utils.register_class(EmptyProps)
187 bpy.types.Scene.matlib_categories = bpy.props.CollectionProperty(type=EmptyProps)
189 for cat in bpy.context.scene.matlib_categories:
191 for mat in bpy.data.materials:
192 if "category" in mat.keys() and mat['category'] == cat.name:
193 materials.append(mat.name)
194 cats.append([cat.name, materials])
195 with open("%s", "w") as f:
196 f.write(json.dumps(cats, sort_keys=True, indent=4))
198 if pull
: send_command(cmd
)
201 with
open(catfile
, "r") as f
:
202 cats
= json
.loads(f
.read())
206 # #refrescar categorias
207 # for cat in self.cats:
208 # self.cats.remove(0)
211 # item = self.cats.add()
217 for cat
in self
.cats
:
221 if name
and name
not in [item
.name
for item
in self
.cats
]:
222 name
= name
.strip().capitalize()
223 item
= self
.cats
.add()
231 def remove(self
, index
):
232 self
.cats
.remove(index
)
237 def __init__(self
, matlib_path
, name
):
239 self
.path
= os
.path
.join(matlib_path
, name
)
242 # return self.name == default_library
247 # return "Default Library"
248 return bpy
.path
.display_name(self
.name
).title()
252 return str(type(self
).__name
__) + "('" + self
.name
+ "')"
254 #bpy.utils.register_class(Library)
257 libs
= [Library(matlib_path
, f
) for f
in os
.listdir(matlib_path
) if f
[-5::] == "blend"]
258 return sorted(libs
, key
=lambda x
: bpy
.path
.display_name(x
.name
))
264 class matlibProperties(PropertyGroup
):
268 #libraries are read from the xml
269 lib_index
: IntProperty(min = -1, default
= 2, update
=update_lib_index
)
270 all_materials
: CollectionProperty(type = matlibMaterials
)
271 materials
: CollectionProperty(type = matlibMaterials
)
272 mat_index
: IntProperty(min = -1, default
= -1)
273 categories
: CollectionProperty(type = EmptyGroup
)
274 cat_index
: IntProperty(min = -1, default
= -1, update
=update_cat_index
)
275 search
: StringProperty(name
="Search", description
="Find By Name", update
=update_search_index
)
278 #link: import material linked
280 # if disable it wont import a material if its present in the scene,(avoid duplicates)
281 # instead it will apply the scene material rather than importing the same one from the library
282 #filter: enable or disable category filter
283 #last selected: store the last selected object to regain focus when apply a material.
284 #hide_search: Hides Search Field
285 link
: BoolProperty(name
= "Linked", description
="Link the material", default
= False)
286 force_import
: BoolProperty(name
= "Force Import", description
="Use Scene Materials by default", default
= False)
287 filter: BoolProperty(name
= "Filter",description
="Filter Categories", default
= True, update
=update_filter
)
288 show_prefs
: BoolProperty(name
= "show_prefs", description
="Preferences", default
= False)
289 last_selected
: StringProperty(name
="Last Selected")
290 hide_search
: BoolProperty(name
="Hide Search", description
="Use Blender Search Only")
291 #import_file = StringProperty("Import File", subtype="FILE_PATH")
292 #path = os.path.dirname(path)
301 def current_library(self
):
302 if check_index(libraries
, self
.lib_index
):
303 return libraries
[self
.lib_index
]
306 def active_material(self
):
307 if check_index(self
.materials
, self
.mat_index
):
308 return self
.materials
[self
.mat_index
]
311 dd("loading libraries")
312 if self
.current_library
:
314 elif self
.lib_index
== -1 and len(libraries
):
318 def add_library(self
, path
, setEnabled
= False):
320 ext
= os
.path
.extsep
+ "blend"
321 if not path
.endswith(ext
):
325 # if path == default_library:
326 # return 'ERROR', "Cannot add default library."
327 #if path in [lib.path for lib in self.libraries]:
328 return 'ERROR', "Library already exists."
330 dd("Can't find " + path
)
334 bpy.ops.wm.save_mainfile(filepath="%s", check_existing=False, compress=True)''' % winpath(path
)
335 if not (send_command(cmd
, "createlib.py")):
336 return 'ERROR', "There was an error creating the file. Make sure you run Blender with admin rights."
338 #self.libraries = sorted(self.libraries, key=lambda lib: sortlibs(lib))
339 dd("adding library", path
)
341 libraries
= get_libraries()
342 return "INFO", "Library added"
344 def load_library(self
):
345 self
.empty_list(True)
346 if not self
.current_library
:
347 return 'ERROR', "Library not found!."
349 path
= self
.current_library
.path
351 dd("loading library", self
.lib_index
, path
)
357 categories
= Categories(self
.categories
)
358 self
.cats
= categories
.read(True)
359 self
.load_categories()
361 for mat
in self
.all_materials
:
362 self
.all_materials
.remove(0)
364 for mat
in list_materials(self
.current_library
.path
, True):
365 item
= self
.all_materials
.add()
367 for cat
in self
.cats
:
369 item
.category
= cat
[0]
374 return 'ERROR', "Library not found!."
376 def update_list(self
):
379 if self
.current_library
:
380 current_category
= self
.current_category
381 #sorteditems = sorted(self.all_materials, key=lambda x: x.name)
382 for mat
in self
.all_materials
:
383 #print(current_category, mat.category)
384 if not self
.filter or (self
.filter and mat
.category
== current_category
) or current_category
== "":
385 item
= self
.materials
.add()
387 item
.category
= mat
.category
389 def empty_list(self
, cats
= False):
391 for it
in self
.materials
:
392 self
.materials
.remove(0)
395 for c
in self
.categories
:
396 self
.categories
.remove(0)
400 def current_category(self
):
401 #print(self.mat_index)
402 if check_index(self
.categories
, self
.cat_index
):
403 return self
.categories
[self
.cat_index
].name
406 def load_categories(self
):
408 for c
in self
.categories
:
409 self
.categories
.remove(0)
412 cat
= self
.categories
.add()
415 def add_category(self
, name
):
417 name
= name
.strip().title()
418 dd("add category", name
)
419 categories
= Categories(self
.categories
)
424 # cat = xml.find("category", name, lib, create = True)
425 # self.load_categories()
427 # return 'ERROR', "Library not found"
428 def remove_category(self
):
429 dd("removing category", self
.current_category
)
430 categories
= Categories(self
.categories
)
431 categories
.remove(self
.cat_index
)
433 def set_category(self
):
434 mat
= self
.active_material
435 #dd(lib, mat, self.current_category)
438 if self
.cat_index
>-1:
439 dd(self
.current_category
)
440 cat
= self
.current_category
441 if cat
== self
.all_materials
[self
.mat_index
].category
:
446 mat = bpy.data.materials['%s']
450 mat['category'] = "%s"
451 bpy.ops.wm.save_mainfile(filepath="%s", check_existing=False, compress=True)
452 """ % (mat
.name
, cat
, winpath(self
.current_library
.path
))
453 if send_command(cmd
):
454 self
.all_materials
[self
.mat_index
].category
= cat
457 return "WARNING", "There was an error."
459 # catnode = xml.find("category", self.current_category, lib, True)
460 # matnode = xml.find("material", mat.name, lib)
462 # catnode.appendChild(matnode)
464 # matnode = xml.find("material", mat.name, catnode, True)
467 # self.current_library.materials[self.mat_index].category = cat
468 #remove mat from any category
471 self
.all_materials
[self
.mat_index
].category
= ""
473 return "WARNING", "Select a material"
475 def get_material(self
, name
, link
=False):
476 with bpy
.data
.libraries
.load(self
.current_library
.path
, link
=link
, relative
=False) as (data_from
, data_to
):
477 data_to
.materials
= [name
]
479 print(name
+ " linked.")
481 print(name
+ " appended.")
483 def apply(self
, context
, preview
=False):
484 name
= self
.active_material
.name
485 if not name
: return "WARNING", "Select a material from the list."
487 linked
= self
.link
or preview
488 force
= self
.force_import
or linked
491 active
= context
.object
492 dummy
= self
.get_dummy(context
)
496 if context
.mode
== "EDIT_MESH":
497 return "WARNING", "Can't preview on EDIT MODE"
499 self
.last_selected
= context
.object.name
500 context
.view_layer
.objects
.active
= dummy
501 objects
.append(dummy
)
504 objects
= [obj
for obj
in context
.selected_objects
if hasattr(obj
.data
, "materials")]
507 return "INFO", "Please select an object"
509 if dummy
== context
.object and not preview
:
510 if (len(objects
)==1 and dummy
.select_get()):
511 return "ERROR", "Apply is disabled for the Material Preview Object"
513 last
= context
.scene
.objects
[self
.last_selected
]
514 if last
in context
.selected_objects
:
515 context
.view_layer
.objects
.active
= last
517 self
.last_selected
= ""
519 context
.view_layer
.objects
.active
= None
520 dummy
.select_set(False)
521 #objects = context.selected_objects
525 #mira si hay materiales linkados de la libreria actual
526 for mat
in bpy
.data
.materials
:
528 samelib
= bpy
.path
.relpath(mat
.library
.filepath
) == bpy
.path
.relpath(self
.current_library
.path
)
532 if mat
.name
== name
and mat
.library
and samelib
:
534 dd("encontre linked", name
, "no importo nada")
538 #busca materiales no linkados
539 for mat
in bpy
.data
.materials
:
540 if mat
.name
== name
and not mat
.library
:
542 dd("encontre no linkado", name
, "no importo nada")
548 nmats
= len(bpy
.data
.materials
)
550 self
.get_material(name
, linked
)
552 if not self
.force_import
:
554 material
= bpy
.data
.materials
[name
]
559 if nmats
== len(bpy
.data
.materials
) and not linked
:
560 return "ERROR", name
+ " doesn't exists at library " + str(linked
)
562 for mat
in reversed(bpy
.data
.materials
):
563 if mat
.name
[0:len(name
)] == name
:
564 #careful on how blender writes library paths
566 samelib
= bpy
.path
.relpath(mat
.library
.filepath
) == bpy
.path
.relpath(self
.current_library
.path
)
570 if linked
and mat
.library
and samelib
:
572 dd(name
, "importado con link")
576 dd(name
, "importado sin link")
580 material
.use_fake_user
= False
581 material
.user_clear()
583 print ("Material", material
, force
)
586 #maybe some test cases doesn't return a material, gotta take care of that
587 #i cannot think of any case like that right now
588 #maybe import linked when the database isn't sync
589 if context
.mode
== "EDIT_MESH":
593 for i
, mat
in enumerate(obj
.data
.materials
):
599 obj
.data
.materials
.append(material
)
600 index
= len(obj
.data
.materials
)-1
603 bm
= bmesh
.from_edit_mesh(obj
.data
)
606 f
.material_index
= index
610 index
= obj
.active_material_index
611 if index
< len(obj
.material_slots
):
612 obj
.material_slots
[index
].material
= None
613 obj
.material_slots
[index
].material
= material
615 obj
.data
.materials
.append(material
)
618 bpy
.ops
.object.make_local(type="SELECT_OBDATA_MATERIAL")
620 def add_material(self
, mat
):
623 return 'WARNING', "Select a material from the scene."
626 thispath
= winpath(bpy
.data
.filepath
)
627 libpath
= winpath(self
.current_library
.path
)
630 return 'WARNING', "Save this file before export."
633 return 'WARNING', "Library not found!."
635 elif bpy
.data
.is_dirty
:
636 bpy
.ops
.wm
.save_mainfile(check_existing
=True)
639 return 'WARNING', 'Cannot export linked materials.'
641 dd("adding material", name
, libpath
)
644 if name
in list_materials(libpath
):
646 mat = bpy.data.materials["%s"]
648 mat.use_fake_user = False
649 mat.user_clear()''' % name
653 with bpy.data.libraries.load("{1}") as (data_from, data_to):
654 data_to.materials = ["{2}"]
655 mat = bpy.data.materials["{2}"]
656 mat.use_fake_user=True
657 bpy.ops.file.pack_all()
658 bpy.ops.wm.save_mainfile(filepath="{3}", check_existing=False, compress=True)
659 '''.format(overwrite
, thispath
, name
, libpath
)
661 if send_command(cmd
):
664 item
= self
.all_materials
.add()
666 if "category" in mat
.keys():
667 item
.category
= mat
['category']
668 #reorder all_materials
669 items
= sorted([[item
.name
, item
.category
] for item
in self
.all_materials
], key
= lambda x
: x
[0])
671 self
.all_materials
.clear()
673 item
= self
.all_materials
.add()
675 item
.category
= it
[1]
679 return 'INFO', "Material added."
681 print("Save Material Error: Run Blender with administrative privileges.")
682 return 'WARNING', "There was an error saving the material"
684 def remove_material(self
):
685 name
= self
.active_material
.name
686 libpath
= winpath(self
.current_library
.path
)
687 if name
and libpath
and name
in list_materials(libpath
):
689 mat = bpy.data.materials["%s"]
690 mat.use_fake_user = False
692 bpy.ops.wm.save_mainfile(filepath="%s", check_existing=False, compress=True)''' % (name
, libpath
)
693 if send_command(cmd
, "removemat.py"):
694 self
.all_materials
.remove(self
.mat_index
)
697 return 'ERROR', "There was an error."
698 return "INFO", name
+ " removed."
700 def get_dummy(self
, context
):
701 dummy_name
= "Material_Preview_Dummy"
702 dummy_mesh
= "Material_Preview_Mesh"
705 dummy
= scn
.objects
[dummy_name
]
709 me
= bpy
.data
.meshes(dummy_mesh
)
711 me
= bpy
.data
.meshes
.new(dummy_mesh
)
712 dummy
= bpy
.data
.objects
.new(dummy_name
, me
)
713 context
.collection
.objects
.link(dummy
)
716 dummy
.hide_render
= True
717 dummy
.hide_select
= True
720 # bpy.utils.register_class(matlibProperties)
721 # Scene.matlib = PointerProperty(type = matlibProperties)
724 class MATLIB_MT_LibsMenu(Menu
):
725 bl_label
= "Libraries Menu"
727 def draw(self
, context
):
730 #layout.operator("matlib.operator", text="Default Library").cmd="lib-1"
731 for i
, lib
in enumerate(libs
):
732 layout
.operator("matlib.operator", text
=lib
.shortname
).cmd
="lib"+str(i
)
734 class MATLIB_MT_CatsMenu(Menu
):
735 bl_label
= "Categories Menu"
737 def draw(self
, context
):
739 cats
= context
.scene
.matlib
.categories
740 layout
.operator("matlib.operator", text
="All").cmd
="cat-1"
741 for i
, cat
in enumerate(cats
):
742 layout
.operator("matlib.operator", text
=cat
.name
).cmd
="cat"+str(i
)
745 #class MATLIB_OT_add(Operator):
746 # """Add Active Material"""
748 # bl_idname = "matlib.add_material"
751 # def poll(cls, context):
752 # return context.active_object is not None
754 # def execute(self, context):
756 # return {"FINISHED"}
760 class MATLIB_OT_add(Operator
):
761 """Add active material to library"""
762 bl_idname
= "matlib.add"
763 bl_label
= "Add active material"
766 def poll(cls
, context
):
767 obj
= context
.active_object
768 return obj
is not None and obj
.active_material
is not None
770 def execute(self
, context
):
771 matlib
= context
.scene
.matlib
772 success
= matlib
.add_material(context
.object.active_material
)
773 if type(success
).__name
__ == "tuple":
775 self
.report({success
[0]}, success
[1])
778 class MATLIB_OT_remove(Operator
):
779 """Remove material from library"""
780 bl_idname
= "matlib.remove"
781 bl_label
= "Remove material from library"
784 def poll(cls
, context
):
785 matlib
= context
.scene
.matlib
786 return check_index(matlib
.materials
, matlib
.mat_index
)
788 def execute(self
, context
):
789 matlib
= context
.scene
.matlib
790 success
= matlib
.remove_material()
791 if type(success
).__name
__ == "tuple":
793 self
.report({success
[0]}, success
[1])
796 class MATLIB_OT_remove(Operator
):
798 bl_idname
= "matlib.reload"
799 bl_label
= "Reload library"
802 # def poll(cls, context):
803 # matlib = context.scene.matlib
804 # index = matlib.mat_index
805 # l = len(matlib.materials)
806 # return l>0 and index >=0 and index < l
808 def execute(self
, context
):
809 matlib
= context
.scene
.matlib
810 success
= matlib
.reload()
811 if type(success
).__name
__ == "tuple":
813 self
.report({success
[0]}, success
[1])
817 class MATLIB_OT_apply(Operator
):
818 """Apply selected material"""
819 bl_idname
= "matlib.apply"
820 bl_label
= "Apply material"
823 def poll(cls
, context
):
824 matlib
= context
.scene
.matlib
825 index
= matlib
.mat_index
826 l
= len(matlib
.materials
)
827 obj
= context
.active_object
828 return l
>0 and index
>=0 and index
< l
and obj
is not None
830 def execute(self
, context
):
831 matlib
= context
.scene
.matlib
832 success
= matlib
.apply(context
, False)
833 if type(success
).__name
__ == "tuple":
835 self
.report({success
[0]}, success
[1])
839 class MATLIB_OT_preview(Operator
):
840 """Preview selected material"""
841 bl_idname
= "matlib.preview"
842 bl_label
= "Preview selected material"
845 def poll(cls
, context
):
846 matlib
= context
.scene
.matlib
847 index
= matlib
.mat_index
848 l
= len(matlib
.materials
)
849 obj
= context
.active_object
850 return l
>0 and index
>=0 and index
< l
852 def execute(self
, context
):
853 matlib
= context
.scene
.matlib
854 success
= matlib
.apply(context
, True)
855 if type(success
).__name
__ == "tuple":
857 self
.report({success
[0]}, success
[1])
861 class MATLIB_OT_flush(Operator
):
862 """Flush unused materials"""
863 bl_idname
= "matlib.flush"
864 bl_label
= "Flush unused materials"
867 def poll(cls
, context
):
868 matlib
= context
.scene
.matlib
869 index
= matlib
.mat_index
870 l
= len(matlib
.materials
)
871 obj
= context
.active_object
872 return l
>0 and index
>=0 and index
< l
874 def execute(self
, context
):
875 matlib
= context
.scene
.matlib
876 dummy
= matlib
.get_dummy(context
)
877 if dummy
== context
.object:
879 context
.view_layer
.objects
.active
= context
.scene
.objects
[matlib
.last_selected
]
883 for slot
in dummy
.material_slots
:
886 for mat
in bpy
.data
.materials
:
889 print (mat
.name
, "removed.")
890 bpy
.data
.materials
.remove(mat
)
892 plural
= "" if i
== 1 else "s"
893 self
.report({'INFO'}, str(i
) + " material"+plural
+" removed.")
898 class MATLIB_OT_operator(Operator
):
899 """Add, Remove, Reload, Apply, Preview, Clean Material"""
901 bl_idname
= "matlib.operator"
902 __doc__
= "Add, Remove, Reload, Apply, Preview, Clean Material"
904 category
: StringProperty(name
="Category")
905 filepath
: StringProperty(options
={'HIDDEN'})
906 cmd
: bpy
.props
.StringProperty(name
="Command", options
={'HIDDEN'})
907 filter_glob
: StringProperty(default
="*.blend", options
={'HIDDEN'})
909 def poll(cls
, context
):
910 return context
.active_object
is not None
912 def draw(self
, context
):
915 if self
.cmd
== "LIBRARY_ADD":
916 #layout.label(text="Select a blend file as library or")
917 #layout.label(text="Type a name to create a new library.")
918 layout
.prop(self
, "category", text
="Library")
919 elif self
.cmd
== "FILTER_ADD":
920 layout
.prop(self
, "category")
922 def invoke(self
, context
, event
):
927 if cmd
== "LIBRARY_ADD":
928 self
.filepath
= matlib_path
+ os
.path
.sep
929 dd("filepath", self
.filepath
, matlib_path
)
930 #context.window_manager.fileselect_add(self)
931 context
.window_manager
.invoke_props_dialog(self
)
932 return {'RUNNING_MODAL'}
933 elif cmd
== "FILTER_ADD":
934 context
.window_manager
.invoke_props_dialog(self
)
935 return {'RUNNING_MODAL'}
936 return self
.execute(context
)
938 ### TODO: execute doesn't trigger remove
939 def execute(self
, context
):
942 matlib
= context
.scene
.matlib
944 if self
.cmd
== "init":
949 if self
.cmd
[0:3] == "lib":
950 index
= int(self
.cmd
[3::])
951 matlib
.lib_index
= index
952 #success = matlib.load_library()
953 elif self
.cmd
== "LIBRARY_ADD":
954 dd("execute lib add")
955 libname
= self
.category
956 if libname
[-6::] != ".blend": libname
+= ".blend"
957 libname
= os
.path
.join(matlib_path
, libname
)
960 success
= matlib
.add_library(libname
, True)
961 for i
, l
in enumerate(libraries
):
962 if l
.name
== self
.category
:
966 elif self
.cmd
== "RELOAD":
967 success
= matlib
.reload()
969 if not matlib
.current_library
:
970 self
.report({'ERROR'}, "Select a Library")
973 if self
.cmd
== "FILTER_ADD":
974 success
= matlib
.add_category(self
.category
)
975 for i
, cat
in enumerate(matlib
.categories
):
976 if cat
.name
== self
.category
:
980 elif self
.cmd
== "FILTER_REMOVE":
981 matlib
.remove_category()
983 elif self
.cmd
== "FILTER_SET":
984 success
= matlib
.set_category()
986 elif self
.cmd
[0:3] == "cat":
987 index
= int(self
.cmd
[3::])
988 matlib
.cat_index
= index
991 elif self
.cmd
== "ADD":
992 success
= matlib
.add_material(context
.object.active_material
)
994 elif self
.cmd
== "REMOVE":
995 success
= matlib
.remove_material()
998 elif self
.cmd
== "APPLY":
999 success
= matlib
.apply(context
)
1001 elif self
.cmd
== "PREVIEW":
1002 success
= matlib
.apply(context
, True)
1004 elif self
.cmd
=="FLUSH":
1005 #release dummy materials
1006 dummy
= matlib
.get_dummy(context
)
1007 if dummy
== context
.object:
1009 context
.view_layer
.objects
.active
= context
.scene
.objects
[matlib
.last_selected
]
1013 for slot
in dummy
.material_slots
:
1014 slot
.material
= None
1016 for mat
in bpy
.data
.materials
:
1019 print (mat
.name
, "removed.")
1020 bpy
.data
.materials
.remove(mat
)
1026 self
.report({'INFO'}, str(i
) + " material"+plural
+" removed.")
1029 elif self
.cmd
== "CONVERT":
1031 lib
= matlib
.current_library
1034 path
= os
.path
.join(matlib_path
, "www")
1035 if not os
.path
.exists(path
):
1037 path
= os
.path
.join(path
, lib
.shortname
)
1038 if not os
.path
.exists(path
):
1041 path
= winpath(path
)
1042 libpath
= winpath(lib
.name
)
1047 #decirle a la libreria que cree un fichero blend por cada material que tenga.
1051 def list_materials():
1053 with bpy.data.libraries.load("{0}") as (data_from, data_to):
1054 for mat in data_from.materials:
1058 def get_material(name, link=False):
1059 with bpy.data.libraries.load("{0}", link=link, relative=False) as (data_from, data_to):
1060 data_to.materials = [name]
1062 print(name + " linked.")
1064 print(name + " appended.")
1066 for scn in bpy.data.scenes:
1067 for obj in scn.objects:
1068 scn.objects.unlink(obj)
1070 bpy.data.objects.remove(obj)
1072 def clean_materials():
1073 for mat in bpy.data.materials:
1075 bpy.data.materials.remove(mat)
1077 bin = bpy.app.binary_path
1078 mats = list_materials()
1079 bpy.context.preferences.filepaths.save_version = 0
1082 matpath = os.path.join("{1}", mat + ".blend")
1085 material = bpy.data.materials[0]
1086 material.use_fake_user = True
1087 bpy.ops.wm.save_mainfile(filepath = matpath, compress=True, check_existing=False)
1088 """.format(libpath
, path
)
1090 send_command(cmd
, "createlib.py")
1092 if type(success
).__name
__ == "tuple":
1094 self
.report({success
[0]}, success
[1])
1099 class MATLIB_PT_vxPanel(Panel
):
1100 bl_label
= "Material Library VX"
1101 bl_space_type
= "PROPERTIES"
1102 bl_region_type
= "WINDOW"
1103 bl_context
= "material"
1104 bl_options
= {'DEFAULT_CLOSED'}
1107 def poll(self
, context
):
1108 return context
.active_object
.active_material
!=None
1110 def draw(self
, context
):
1111 layout
= self
.layout
1112 matlib
= context
.scene
.matlib
1114 #hyper ugly trick but i dont know how to init classes at register time
1115 # if matlibProperties.init:
1116 # matlibProperties.init = False
1120 col
= layout
.column(align
=True)
1121 if matlib
.current_library
:
1122 text
= matlib
.current_library
.shortname
1124 text
= "Select a Library"
1125 split
= col
.split(factor
=0.55, align
=True)
1126 split
.menu("MATLIB_MT_LibsMenu",text
=text
)
1127 split
.operator("matlib.operator", icon
="ADD", text
="New Library").cmd
= "LIBRARY_ADD"
1131 row
.template_list("UI_UL_list", " ", matlib
, "materials", matlib
, "mat_index", rows
=6)
1133 # row = layout.row()
1135 col
.operator("matlib.operator", icon
="ADD", text
="Add To Library").cmd
="ADD"
1136 col
.operator("matlib.operator", icon
="MATERIAL", text
="Apply To Selected").cmd
="APPLY"
1137 col
.operator("matlib.operator", icon
="FILE_REFRESH", text
="Reload Material").cmd
="RELOAD"
1138 col
.operator("matlib.operator", icon
="COLOR", text
="Preview Material").cmd
="PREVIEW"
1139 col
.operator("matlib.operator", icon
="GHOST_DISABLED", text
="Remove Preview").cmd
="FLUSH"
1140 col
.operator("matlib.operator", icon
="REMOVE", text
="Remove Material").cmd
="REMOVE"
1141 col
.prop(matlib
, "show_prefs", icon
="MODIFIER", text
="Settings")
1144 if not matlib
.hide_search
:
1145 row
= layout
.row(align
=True)
1146 row
.prop_search(matlib
, "search", matlib
, "materials", text
="", icon
="VIEWZOOM")
1150 if matlib
.active_material
:
1151 row
.label(text
="Category:")
1152 row
.label(text
= matlib
.active_material
.category
)
1154 row
.label(text
="Category Tools:")
1155 row
= layout
.row(align
=True)
1157 if matlib
.current_category
: text
= matlib
.current_category
1158 row
.menu("MATLIB_MT_CatsMenu",text
=text
)
1159 row
= layout
.row(align
=True)
1160 row
.prop(matlib
, "filter", icon
="FILTER", text
="Filter")
1161 row
.operator("matlib.operator", icon
="FILE_PARENT", text
="Set Type").cmd
="FILTER_SET"
1162 row
.operator("matlib.operator", icon
="ADD", text
="New").cmd
="FILTER_ADD"
1163 row
.operator("matlib.operator", icon
="REMOVE", text
="Remove").cmd
="FILTER_REMOVE"
1166 if matlib
.show_prefs
:
1167 row
= layout
.row(align
=True)
1168 row
.prop(matlib
, "force_import")
1169 row
.prop(matlib
, "link")
1170 row
.prop(matlib
, "hide_search")
1171 # row = layout.row(align=True)
1173 #row.operator("matlib.operator", icon="URL", text="Convert Library").cmd="CONVERT"
1175 # row = layout.row()
1176 # if (matlib.current_library):
1177 # row.label(matlib.current_library.name)
1179 # row.label(text="Library not found!.")
1190 #print(bpy.context.scene)
1194 def refresh_libs(dummy
=None):
1197 default_path
= bpy
.context
.preferences
.addons
[__name__
].preferences
.matlib_path
1198 if default_path
is not None and default_path
!= '':
1199 matlib_path
= default_path
1201 libraries
= get_libraries()
1204 def reload_library(self
, context
):
1205 bpy
.context
.preferences
.addons
[__name__
].preferences
.matlib_path
= bpy
.path
.abspath(bpy
.context
.preferences
.addons
[__name__
].preferences
.matlib_path
)
1209 class matlibvxPref(AddonPreferences
):
1210 bl_idname
= __name__
1211 matlib_path
: StringProperty(
1212 name
="Additional Path",
1213 description
="User defined path to .blend libraries files",
1216 update
=reload_library
1219 def draw(self
, context
):
1220 layout
= self
.layout
1221 layout
.prop(self
, "matlib_path")
1258 bpy
.utils
.register_class(c
)
1259 Scene
.matlib_categories
= CollectionProperty(type=EmptyGroup
)
1260 Scene
.matlib
= PointerProperty(type = matlibProperties
)
1261 bpy
.app
.handlers
.load_post
.append(refresh_libs
)
1269 # raise ValueError list.remove(x): x not in list
1270 del Scene
.matlib_categories
1275 bpy
.app
.handlers
.load_post
.remove(refresh_libs
)
1277 bpy
.utils
.unregister_class(c
)
1280 if __name__
== "__main__":