3 # #####BEGIN GPL LICENSE BLOCK #####
5 # This program is free software; you can redistribute it and/or
6 # modify it under the terms of the GNU General Public License
7 # as published by the Free Software Foundation; either version 2
8 # of the License, or (at your option) any later version.
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # GNU General Public License for more details.
15 # You should have received a copy of the GNU General Public License
16 # along with this program; if not, write to the Free Software Foundation,
17 # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 # #####END GPL LICENSE BLOCK #####
22 "name": "Material Library",
23 "author": "Mackraken",
25 "blender": (2, 80, 0),
26 "location": "Properties > Material",
27 "description": "Material Library VX",
29 "doc_url": "{BLENDER_MANUAL_URL}/addons/materials/material_library.html",
31 "category": "Material",
38 from pathlib
import Path
40 from bpy
.app
.handlers
import persistent
41 from bpy
.props
import (
42 StringProperty
, IntProperty
, BoolProperty
,
43 PointerProperty
, CollectionProperty
45 from bpy
.types
import (
46 Panel
, Menu
, AddonPreferences
, Operator
,
47 PropertyGroup
, UIList
,
51 from rna_prop_ui
import PropertyPanel
55 user_path
= Path(bpy
.utils
.resource_path('USER')).parent
56 matlib_path
= os
.path
.join(user_path
, "matlib")
58 if not os
.path
.exists(matlib_path
):
61 addon_path
= os
.path
.dirname(__file__
)
62 shutil
.copy2(os
.path
.join(addon_path
, "categories.txt"), matlib_path
)
63 shutil
.copy2(os
.path
.join(addon_path
, "templates.blend"), matlib_path
)
64 shutil
.copy2(os
.path
.join(addon_path
, "sample_materials.blend"), matlib_path
)
66 ##debug print variables
67 def dd(*args
, dodir
=False):
75 return path
.replace("\\", "\\\\")
77 def update_search_index(self
, context
):
79 for i
, it
in enumerate(self
.materials
):
85 #isabs sometimes returns true on relpaths
86 if path
and os
.path
.exists(path
) and os
.path
.isfile(path
) and os
.path
.isabs(path
):
88 if bpy
.data
.filepath
and bpy
.path
.relpath(bpy
.data
.filepath
) == bpy
.path
.relpath(path
):
92 #paths are on different drives. No problem then
96 def update_lib_index(self
, context
):
99 def update_cat_index(self
, context
):
100 dd("cat index:", self
.current_category
, self
.filter)
106 def update_filter(self
, context
):
108 dd("filter:", self
.filter, self
.cat_index
, self
.current_category
)
109 # index = self.cat_index
112 # cat = self.current_category
116 # self.current_library.filter = cat
119 def check_index(collection
, index
):
120 count
= len(collection
)
121 return count
>0 and index
<count
and index
>=0
123 def send_command(cmd
, output
="sendmat.py"):
124 bin
= winpath(bpy
.app
.binary_path
)
125 scriptpath
= winpath(os
.path
.join(bpy
.app
.tempdir
, output
))
127 with
open(scriptpath
, "w") as f
:
132 if output
== "createlib.py":
133 code
= subprocess
.call([bin
, "-b", "-P", scriptpath
])
135 libpath
= winpath(bpy
.context
.scene
.matlib
.current_library
.path
)
136 code
= subprocess
.call([bin
, "-b", libpath
, "-P", scriptpath
])
138 #code returns 0 if ok, 1 if not
141 def list_materials(path
, sort
=False):
143 with bpy
.data
.libraries
.load(path
) as (data_from
, data_to
):
144 for mat
in data_from
.materials
:
147 if sort
: list = sorted(list)
150 #category properties (none atm)
151 class EmptyGroup(PropertyGroup
):
153 # bpy.utils.register_class(EmptyGroup)
155 class matlibMaterials(PropertyGroup
):
156 category
: StringProperty()
157 # bpy.utils.register_class(matlibMaterials)
159 #bpy.types.Scene.matlib_categories = CollectionProperty(type=EmptyGroup)
164 #cats = bpy.context.scene.matlib.categories
166 def __init__(self
, cats
):
170 scn
= bpy
.context
.scene
171 cats
= set([cat
.name
for cat
in self
.cats
])
172 libpath
= bpy
.context
.scene
.matlib
.current_library
.path
177 if not hasattr(bpy.context.scene, "matlib_categories"):
178 class EmptyProps(bpy.types.PropertyGroup):
180 bpy.utils.register_class(EmptyProps)
181 bpy.types.Scene.matlib_categories = bpy.props.CollectionProperty(type=EmptyProps)
182 cats = bpy.context.scene.matlib_categories
189 cat.name = "%s" """ % cat
.capitalize()
191 bpy.ops.wm.save_mainfile(filepath="%s", check_existing=False, compress=True)''' % winpath(libpath
)
193 return send_command(cmd
, "save_categories.py")
195 def read(self
, pull
=True):
196 #mandar a imprimir el listado
197 catfile
= winpath(os
.path
.join(matlib_path
, "categories.txt"))
200 class EmptyProps(bpy.types.PropertyGroup):
202 bpy.utils.register_class(EmptyProps)
203 bpy.types.Scene.matlib_categories = bpy.props.CollectionProperty(type=EmptyProps)
205 for cat in bpy.context.scene.matlib_categories:
207 for mat in bpy.data.materials:
208 if "category" in mat.keys() and mat['category'] == cat.name:
209 materials.append(mat.name)
210 cats.append([cat.name, materials])
211 with open("%s", "w") as f:
212 f.write(json.dumps(cats, sort_keys=True, indent=4))
214 if pull
: send_command(cmd
)
217 with
open(catfile
, "r") as f
:
218 cats
= json
.loads(f
.read())
222 # #refrescar categorias
223 # for cat in self.cats:
224 # self.cats.remove(0)
227 # item = self.cats.add()
233 for cat
in self
.cats
:
237 if name
and name
not in [item
.name
for item
in self
.cats
]:
238 name
= name
.strip().capitalize()
239 item
= self
.cats
.add()
247 def remove(self
, index
):
248 self
.cats
.remove(index
)
253 def __init__(self
, matlib_path
, name
):
255 self
.path
= os
.path
.join(matlib_path
, name
)
258 # return self.name == default_library
263 # return "Default Library"
264 return bpy
.path
.display_name(self
.name
).title()
268 return str(type(self
).__name
__) + "('" + self
.name
+ "')"
270 #bpy.utils.register_class(Library)
273 libs
= [Library(matlib_path
, f
) for f
in os
.listdir(matlib_path
) if f
[-5::] == "blend"]
274 return sorted(libs
, key
=lambda x
: bpy
.path
.display_name(x
.name
))
280 class matlibProperties(PropertyGroup
):
284 #libraries are read from the xml
285 lib_index
: IntProperty(min = -1, default
= 2, update
=update_lib_index
)
286 all_materials
: CollectionProperty(type = matlibMaterials
)
287 materials
: CollectionProperty(type = matlibMaterials
)
288 mat_index
: IntProperty(min = -1, default
= -1)
289 categories
: CollectionProperty(type = EmptyGroup
)
290 cat_index
: IntProperty(min = -1, default
= -1, update
=update_cat_index
)
291 search
: StringProperty(name
="Search", description
="Find By Name", update
=update_search_index
)
294 #link: import material linked
296 # if disable it wont import a material if its present in the scene,(avoid duplicates)
297 # instead it will apply the scene material rather than importing the same one from the library
298 #filter: enable or disable category filter
299 #last selected: store the last selected object to regain focus when apply a material.
300 #hide_search: Hides Search Field
301 link
: BoolProperty(name
= "Linked", description
="Link the material", default
= False)
302 force_import
: BoolProperty(name
= "Force Import", description
="Use Scene Materials by default", default
= False)
303 filter: BoolProperty(name
= "Filter",description
="Filter Categories", default
= True, update
=update_filter
)
304 show_prefs
: BoolProperty(name
= "show_prefs", description
="Preferences", default
= False)
305 last_selected
: StringProperty(name
="Last Selected")
306 hide_search
: BoolProperty(name
="Hide Search", description
="Use Blender Search Only")
307 #import_file = StringProperty("Import File", subtype="FILE_PATH")
308 #path = os.path.dirname(path)
317 def current_library(self
):
318 if check_index(libraries
, self
.lib_index
):
319 return libraries
[self
.lib_index
]
322 def active_material(self
):
323 if check_index(self
.materials
, self
.mat_index
):
324 return self
.materials
[self
.mat_index
]
327 dd("loading libraries")
328 if self
.current_library
:
330 elif self
.lib_index
== -1 and len(libraries
):
334 def add_library(self
, path
, setEnabled
= False):
336 ext
= os
.path
.extsep
+ "blend"
337 if not path
.endswith(ext
):
341 # if path == default_library:
342 # return 'ERROR', "Cannot add default library."
343 #if path in [lib.path for lib in self.libraries]:
344 return 'ERROR', "Library already exists."
346 dd("Can't find " + path
)
350 bpy.ops.wm.save_mainfile(filepath="%s", check_existing=False, compress=True)''' % winpath(path
)
351 if not (send_command(cmd
, "createlib.py")):
352 return 'ERROR', "There was an error creating the file. Make sure you run Blender with admin rights."
354 #self.libraries = sorted(self.libraries, key=lambda lib: sortlibs(lib))
355 dd("adding library", path
)
357 libraries
= get_libraries()
358 return "INFO", "Library added"
360 def load_library(self
):
361 self
.empty_list(True)
362 if not self
.current_library
:
363 return 'ERROR', "Library not found!."
365 path
= self
.current_library
.path
367 dd("loading library", self
.lib_index
, path
)
373 categories
= Categories(self
.categories
)
374 self
.cats
= categories
.read(True)
375 self
.load_categories()
377 for mat
in self
.all_materials
:
378 self
.all_materials
.remove(0)
380 for mat
in list_materials(self
.current_library
.path
, True):
381 item
= self
.all_materials
.add()
383 for cat
in self
.cats
:
385 item
.category
= cat
[0]
390 return 'ERROR', "Library not found!."
392 def update_list(self
):
395 if self
.current_library
:
396 current_category
= self
.current_category
397 #sorteditems = sorted(self.all_materials, key=lambda x: x.name)
398 for mat
in self
.all_materials
:
399 #print(current_category, mat.category)
400 if not self
.filter or (self
.filter and mat
.category
== current_category
) or current_category
== "":
401 item
= self
.materials
.add()
403 item
.category
= mat
.category
405 def empty_list(self
, cats
= False):
407 for it
in self
.materials
:
408 self
.materials
.remove(0)
411 for c
in self
.categories
:
412 self
.categories
.remove(0)
416 def current_category(self
):
417 #print(self.mat_index)
418 if check_index(self
.categories
, self
.cat_index
):
419 return self
.categories
[self
.cat_index
].name
422 def load_categories(self
):
424 for c
in self
.categories
:
425 self
.categories
.remove(0)
428 cat
= self
.categories
.add()
431 def add_category(self
, name
):
433 name
= name
.strip().title()
434 dd("add category", name
)
435 categories
= Categories(self
.categories
)
440 # cat = xml.find("category", name, lib, create = True)
441 # self.load_categories()
443 # return 'ERROR', "Library not found"
444 def remove_category(self
):
445 dd("removing category", self
.current_category
)
446 categories
= Categories(self
.categories
)
447 categories
.remove(self
.cat_index
)
449 def set_category(self
):
450 mat
= self
.active_material
451 #dd(lib, mat, self.current_category)
454 if self
.cat_index
>-1:
455 dd(self
.current_category
)
456 cat
= self
.current_category
457 if cat
== self
.all_materials
[self
.mat_index
].category
:
462 mat = bpy.data.materials['%s']
466 mat['category'] = "%s"
467 bpy.ops.wm.save_mainfile(filepath="%s", check_existing=False, compress=True)
468 """ % (mat
.name
, cat
, winpath(self
.current_library
.path
))
469 if send_command(cmd
):
470 self
.all_materials
[self
.mat_index
].category
= cat
473 return "WARNING", "There was an error."
475 # catnode = xml.find("category", self.current_category, lib, True)
476 # matnode = xml.find("material", mat.name, lib)
478 # catnode.appendChild(matnode)
480 # matnode = xml.find("material", mat.name, catnode, True)
483 # self.current_library.materials[self.mat_index].category = cat
484 #remove mat from any category
487 self
.all_materials
[self
.mat_index
].category
= ""
489 return "WARNING", "Select a material"
491 def get_material(self
, name
, link
=False):
492 with bpy
.data
.libraries
.load(self
.current_library
.path
, link
, False) as (data_from
, data_to
):
493 data_to
.materials
= [name
]
495 print(name
+ " linked.")
497 print(name
+ " appended.")
499 def apply(self
, context
, preview
=False):
500 name
= self
.active_material
.name
501 if not name
: return "WARNING", "Select a material from the list."
503 linked
= self
.link
or preview
504 force
= self
.force_import
or linked
507 active
= context
.object
508 dummy
= self
.get_dummy(context
)
512 if context
.mode
== "EDIT_MESH":
513 return "WARNING", "Can't preview on EDIT MODE"
515 self
.last_selected
= context
.object.name
516 context
.view_layer
.objects
.active
= dummy
517 objects
.append(dummy
)
520 objects
= [obj
for obj
in context
.selected_objects
if hasattr(obj
.data
, "materials")]
523 return "INFO", "Please select an object"
525 if dummy
== context
.object and not preview
:
526 if (len(objects
)==1 and dummy
.select
):
527 return "ERROR", "Apply is disabled for the Material Preview Object"
529 last
= context
.scene
.objects
[self
.last_selected
]
530 if last
in context
.selected_objects
:
531 context
.view_layer
.objects
.active
= last
533 self
.last_selected
= ""
535 context
.view_layer
.objects
.active
= None
536 dummy
.select_set(False)
537 #objects = context.selected_objects
541 #mira si hay materiales linkados de la libreria actual
542 for mat
in bpy
.data
.materials
:
544 samelib
= bpy
.path
.relpath(mat
.library
.filepath
) == bpy
.path
.relpath(self
.current_library
.path
)
548 if mat
.name
== name
and mat
.library
and samelib
:
550 dd("encontre linked", name
, "no importo nada")
554 #busca materiales no linkados
555 for mat
in bpy
.data
.materials
:
556 if mat
.name
== name
and not mat
.library
:
558 dd("encontre no linkado", name
, "no importo nada")
564 nmats
= len(bpy
.data
.materials
)
566 self
.get_material(name
, linked
)
568 if not self
.force_import
:
570 material
= bpy
.data
.materials
[name
]
575 if nmats
== len(bpy
.data
.materials
) and not linked
:
576 return "ERROR", name
+ " doesn't exists at library " + str(linked
)
578 for mat
in reversed(bpy
.data
.materials
):
579 if mat
.name
[0:len(name
)] == name
:
580 #careful on how blender writes library paths
582 samelib
= bpy
.path
.relpath(mat
.library
.filepath
) == bpy
.path
.relpath(self
.current_library
.path
)
586 if linked
and mat
.library
and samelib
:
588 dd(name
, "importado con link")
592 dd(name
, "importado sin link")
596 material
.use_fake_user
= False
597 material
.user_clear()
599 print ("Material", material
, force
)
602 #maybe some test cases doesn't return a material, gotta take care of that
603 #i cannot think of any case like that right now
604 #maybe import linked when the database isnt sync
605 if context
.mode
== "EDIT_MESH":
609 for i
, mat
in enumerate(obj
.data
.materials
):
615 obj
.data
.materials
.append(material
)
616 index
= len(obj
.data
.materials
)-1
619 bm
= bmesh
.from_edit_mesh(obj
.data
)
622 f
.material_index
= index
626 index
= obj
.active_material_index
627 if index
< len(obj
.material_slots
):
628 obj
.material_slots
[index
].material
= None
629 obj
.material_slots
[index
].material
= material
631 obj
.data
.materials
.append(material
)
634 bpy
.ops
.object.make_local(type="SELECT_OBDATA_MATERIAL")
636 def add_material(self
, mat
):
639 return 'WARNING', "Select a material from the scene."
642 thispath
= winpath(bpy
.data
.filepath
)
643 libpath
= winpath(self
.current_library
.path
)
646 return 'WARNING', "Save this file before export."
649 return 'WARNING', "Library not found!."
651 elif bpy
.data
.is_dirty
:
652 bpy
.ops
.wm
.save_mainfile(check_existing
=True)
655 return 'WARNING', 'Cannot export linked materials.'
657 dd("adding material", name
, libpath
)
660 if name
in list_materials(libpath
):
662 mat = bpy.data.materials["%s"]
664 mat.use_fake_user = False
665 mat.user_clear()''' % name
669 with bpy.data.libraries.load("{1}") as (data_from, data_to):
670 data_to.materials = ["{2}"]
671 mat = bpy.data.materials["{2}"]
672 mat.use_fake_user=True
673 bpy.ops.file.pack_all()
674 bpy.ops.wm.save_mainfile(filepath="{3}", check_existing=False, compress=True)
675 '''.format(overwrite
, thispath
, name
, libpath
)
677 if send_command(cmd
):
680 item
= self
.all_materials
.add()
682 if "category" in mat
.keys():
683 item
.category
= mat
['category']
684 #reorder all_materials
685 items
= sorted([[item
.name
, item
.category
] for item
in self
.all_materials
], key
= lambda x
: x
[0])
687 self
.all_materials
.clear()
689 item
= self
.all_materials
.add()
691 item
.category
= it
[1]
695 return 'INFO', "Material added."
697 print("Save Material Error: Run Blender with administrative privileges.")
698 return 'WARNING', "There was an error saving the material"
700 def remove_material(self
):
701 name
= self
.active_material
.name
702 libpath
= winpath(self
.current_library
.path
)
703 if name
and libpath
and name
in list_materials(libpath
):
705 mat = bpy.data.materials["%s"]
706 mat.use_fake_user = False
708 bpy.ops.wm.save_mainfile(filepath="%s", check_existing=False, compress=True)''' % (name
, libpath
)
709 if send_command(cmd
, "removemat.py"):
710 self
.all_materials
.remove(self
.mat_index
)
713 return 'ERROR', "There was an error."
714 return "INFO", name
+ " removed."
716 def get_dummy(self
, context
):
717 dummy_name
= "Material_Preview_Dummy"
718 dummy_mesh
= "Material_Preview_Mesh"
721 dummy
= scn
.objects
[dummy_name
]
725 me
= bpy
.data
.meshes(dummy_mesh
)
727 me
= bpy
.data
.meshes
.new(dummy_mesh
)
728 dummy
= bpy
.data
.objects
.new(dummy_name
, me
)
729 context
.collection
.objects
.link(dummy
)
732 dummy
.hide_render
= True
733 dummy
.hide_select
= True
736 # bpy.utils.register_class(matlibProperties)
737 # Scene.matlib = PointerProperty(type = matlibProperties)
740 class MATLIB_MT_LibsMenu(Menu
):
741 bl_label
= "Libraries Menu"
743 def draw(self
, context
):
746 #layout.operator("matlib.operator", text="Default Library").cmd="lib-1"
747 for i
, lib
in enumerate(libs
):
748 layout
.operator("matlib.operator", text
=lib
.shortname
).cmd
="lib"+str(i
)
750 class MATLIB_MT_CatsMenu(Menu
):
751 bl_label
= "Categories Menu"
753 def draw(self
, context
):
755 cats
= context
.scene
.matlib
.categories
756 layout
.operator("matlib.operator", text
="All").cmd
="cat-1"
757 for i
, cat
in enumerate(cats
):
758 layout
.operator("matlib.operator", text
=cat
.name
).cmd
="cat"+str(i
)
761 #class MATLIB_OT_add(Operator):
762 # """Add Active Material"""
764 # bl_idname = "matlib.add_material"
767 # def poll(cls, context):
768 # return context.active_object is not None
770 # def exectute(self, context):
772 # return {"FINISHED"}
776 class MATLIB_OT_add(Operator
):
777 """Add active material to library"""
778 bl_idname
= "matlib.add"
779 bl_label
= "Add active material"
782 def poll(cls
, context
):
783 obj
= context
.active_object
784 return obj
is not None and obj
.active_material
is not None
786 def execute(self
, context
):
787 matlib
= context
.scene
.matlib
788 success
= matlib
.add_material(context
.object.active_material
)
789 if type(success
).__name
__ == "tuple":
791 self
.report({success
[0]}, success
[1])
794 class MATLIB_OT_remove(Operator
):
795 """Remove material from library"""
796 bl_idname
= "matlib.remove"
797 bl_label
= "Remove material from library"
800 def poll(cls
, context
):
801 matlib
= context
.scene
.matlib
802 return check_index(matlib
.materials
, matlib
.mat_index
)
804 def execute(self
, context
):
805 matlib
= context
.scene
.matlib
806 success
= matlib
.remove_material()
807 if type(success
).__name
__ == "tuple":
809 self
.report({success
[0]}, success
[1])
812 class MATLIB_OT_remove(Operator
):
814 bl_idname
= "matlib.reload"
815 bl_label
= "Reload library"
818 # def poll(cls, context):
819 # matlib = context.scene.matlib
820 # index = matlib.mat_index
821 # l = len(matlib.materials)
822 # return l>0 and index >=0 and index < l
824 def execute(self
, context
):
825 matlib
= context
.scene
.matlib
826 success
= matlib
.reload()
827 if type(success
).__name
__ == "tuple":
829 self
.report({success
[0]}, success
[1])
833 class MATLIB_OT_apply(Operator
):
834 """Apply selected material"""
835 bl_idname
= "matlib.apply"
836 bl_label
= "Apply material"
839 def poll(cls
, context
):
840 matlib
= context
.scene
.matlib
841 index
= matlib
.mat_index
842 l
= len(matlib
.materials
)
843 obj
= context
.active_object
844 return l
>0 and index
>=0 and index
< l
and obj
is not None
846 def execute(self
, context
):
847 matlib
= context
.scene
.matlib
848 success
= matlib
.apply(context
, False)
849 if type(success
).__name
__ == "tuple":
851 self
.report({success
[0]}, success
[1])
855 class MATLIB_OT_preview(Operator
):
856 """Preview selected material"""
857 bl_idname
= "matlib.preview"
858 bl_label
= "Preview selected material"
861 def poll(cls
, context
):
862 matlib
= context
.scene
.matlib
863 index
= matlib
.mat_index
864 l
= len(matlib
.materials
)
865 obj
= context
.active_object
866 return l
>0 and index
>=0 and index
< l
868 def execute(self
, context
):
869 matlib
= context
.scene
.matlib
870 success
= matlib
.apply(context
, True)
871 if type(success
).__name
__ == "tuple":
873 self
.report({success
[0]}, success
[1])
877 class MATLIB_OT_flush(Operator
):
878 """Flush unused materials"""
879 bl_idname
= "matlib.flush"
880 bl_label
= "Flush unused materials"
883 def poll(cls
, context
):
884 matlib
= context
.scene
.matlib
885 index
= matlib
.mat_index
886 l
= len(matlib
.materials
)
887 obj
= context
.active_object
888 return l
>0 and index
>=0 and index
< l
890 def execute(self
, context
):
891 matlib
= context
.scene
.matlib
892 dummy
= matlib
.get_dummy(context
)
893 if dummy
== context
.object:
895 context
.view_layer
.objects
.active
= context
.scene
.objects
[matlib
.last_selected
]
899 for slot
in dummy
.material_slots
:
902 for mat
in bpy
.data
.materials
:
905 print (mat
.name
, "removed.")
906 bpy
.data
.materials
.remove(mat
)
908 plural
= "" if i
== 1 else "s"
909 self
.report({'INFO'}, str(i
) + " material"+plural
+" removed.")
914 class MATLIB_OT_operator(Operator
):
915 """Add, Remove, Reload, Apply, Preview, Clean Material"""
917 bl_idname
= "matlib.operator"
918 __doc__
= "Add, Remove, Reload, Apply, Preview, Clean Material"
920 category
: StringProperty(name
="Category")
921 filepath
: StringProperty(options
={'HIDDEN'})
922 cmd
: bpy
.props
.StringProperty(name
="Command", options
={'HIDDEN'})
923 filter_glob
: StringProperty(default
="*.blend", options
={'HIDDEN'})
925 def poll(cls
, context
):
926 return context
.active_object
is not None
928 def draw(self
, context
):
931 if self
.cmd
== "LIBRARY_ADD":
932 #layout.label(text="Select a blend file as library or")
933 #layout.label(text="Type a name to create a new library.")
934 layout
.prop(self
, "category", text
="Library")
935 elif self
.cmd
== "FILTER_ADD":
936 layout
.prop(self
, "category")
938 def invoke(self
, context
, event
):
943 if cmd
== "LIBRARY_ADD":
944 self
.filepath
= matlib_path
+ os
.path
.sep
945 dd("filepath", self
.filepath
, matlib_path
)
946 #context.window_manager.fileselect_add(self)
947 context
.window_manager
.invoke_props_dialog(self
)
948 return {'RUNNING_MODAL'}
949 elif cmd
== "FILTER_ADD":
950 context
.window_manager
.invoke_props_dialog(self
)
951 return {'RUNNING_MODAL'}
952 return self
.execute(context
)
954 ### TODO: execute doesn't trigger remove
955 def execute(self
, context
):
958 matlib
= context
.scene
.matlib
960 if self
.cmd
== "init":
965 if self
.cmd
[0:3] == "lib":
966 index
= int(self
.cmd
[3::])
967 matlib
.lib_index
= index
968 #success = matlib.load_library()
969 elif self
.cmd
== "LIBRARY_ADD":
970 dd("execute lib add")
971 libname
= self
.category
972 if libname
[-6::] != ".blend": libname
+= ".blend"
973 libname
= os
.path
.join(matlib_path
, libname
)
976 success
= matlib
.add_library(libname
, True)
977 for i
, l
in enumerate(libraries
):
978 if l
.name
== self
.category
:
982 elif self
.cmd
== "RELOAD":
983 success
= matlib
.reload()
985 if not matlib
.current_library
:
986 self
.report({'ERROR'}, "Select a Library")
989 if self
.cmd
== "FILTER_ADD":
990 success
= matlib
.add_category(self
.category
)
991 for i
, cat
in enumerate(matlib
.categories
):
992 if cat
.name
== self
.category
:
996 elif self
.cmd
== "FILTER_REMOVE":
997 matlib
.remove_category()
999 elif self
.cmd
== "FILTER_SET":
1000 success
= matlib
.set_category()
1002 elif self
.cmd
[0:3] == "cat":
1003 index
= int(self
.cmd
[3::])
1004 matlib
.cat_index
= index
1007 elif self
.cmd
== "ADD":
1008 success
= matlib
.add_material(context
.object.active_material
)
1010 elif self
.cmd
== "REMOVE":
1011 success
= matlib
.remove_material()
1014 elif self
.cmd
== "APPLY":
1015 success
= matlib
.apply(context
)
1017 elif self
.cmd
== "PREVIEW":
1018 success
= matlib
.apply(context
, True)
1020 elif self
.cmd
=="FLUSH":
1021 #release dummy materials
1022 dummy
= matlib
.get_dummy(context
)
1023 if dummy
== context
.object:
1025 context
.view_layer
.objects
.active
= context
.scene
.objects
[matlib
.last_selected
]
1029 for slot
in dummy
.material_slots
:
1030 slot
.material
= None
1032 for mat
in bpy
.data
.materials
:
1035 print (mat
.name
, "removed.")
1036 bpy
.data
.materials
.remove(mat
)
1042 self
.report({'INFO'}, str(i
) + " material"+plural
+" removed.")
1045 elif self
.cmd
== "CONVERT":
1047 lib
= matlib
.current_library
1050 path
= os
.path
.join(matlib_path
, "www")
1051 if not os
.path
.exists(path
):
1053 path
= os
.path
.join(path
, lib
.shortname
)
1054 if not os
.path
.exists(path
):
1057 path
= winpath(path
)
1058 libpath
= winpath(lib
.name
)
1063 #decirle a la libreria que cree un fichero blend por cada material que tenga.
1067 def list_materials():
1069 with bpy.data.libraries.load("{0}") as (data_from, data_to):
1070 for mat in data_from.materials:
1074 def get_material(name, link=False):
1075 with bpy.data.libraries.load("{0}", link, False) as (data_from, data_to):
1076 data_to.materials = [name]
1078 print(name + " linked.")
1080 print(name + " appended.")
1082 for scn in bpy.data.scenes:
1083 for obj in scn.objects:
1084 scn.objects.unlink(obj)
1086 bpy.data.objects.remove(obj)
1088 def clean_materials():
1089 for mat in bpy.data.materials:
1091 bpy.data.materials.remove(mat)
1093 bin = bpy.app.binary_path
1094 mats = list_materials()
1095 bpy.context.preferences.filepaths.save_version = 0
1098 matpath = os.path.join("{1}", mat + ".blend")
1101 material = bpy.data.materials[0]
1102 material.use_fake_user = True
1103 bpy.ops.wm.save_mainfile(filepath = matpath, compress=True, check_existing=False)
1104 """.format(libpath
, path
)
1106 send_command(cmd
, "createlib.py")
1108 if type(success
).__name
__ == "tuple":
1110 self
.report({success
[0]}, success
[1])
1115 class MATLIB_PT_vxPanel(Panel
):
1116 bl_label
= "Material Library VX"
1117 bl_space_type
= "PROPERTIES"
1118 bl_region_type
= "WINDOW"
1119 bl_context
= "material"
1120 bl_options
= {'DEFAULT_CLOSED'}
1123 def poll(self
, context
):
1124 return context
.active_object
.active_material
!=None
1126 def draw(self
, context
):
1127 layout
= self
.layout
1128 matlib
= context
.scene
.matlib
1130 #hyper ugly trick but i dont know how to init classes at register time
1131 # if matlibProperties.init:
1132 # matlibProperties.init = False
1136 col
= layout
.column(align
=True)
1137 if matlib
.current_library
:
1138 text
= matlib
.current_library
.shortname
1140 text
= "Select a Library"
1141 split
= col
.split(factor
=0.55, align
=True)
1142 split
.menu("MATLIB_MT_LibsMenu",text
=text
)
1143 split
.operator("matlib.operator", icon
="ADD", text
="New Library").cmd
= "LIBRARY_ADD"
1147 row
.template_list("UI_UL_list", " ", matlib
, "materials", matlib
, "mat_index", rows
=6)
1149 # row = layout.row()
1151 col
.operator("matlib.operator", icon
="ADD", text
="Add To Library").cmd
="ADD"
1152 col
.operator("matlib.operator", icon
="MATERIAL", text
="Apply To Selected").cmd
="APPLY"
1153 col
.operator("matlib.operator", icon
="FILE_REFRESH", text
="Reload Material").cmd
="RELOAD"
1154 col
.operator("matlib.operator", icon
="COLOR", text
="Preview Material").cmd
="PREVIEW"
1155 col
.operator("matlib.operator", icon
="GHOST_DISABLED", text
="Remove Preview").cmd
="FLUSH"
1156 col
.operator("matlib.operator", icon
="REMOVE", text
="Remove Material").cmd
="REMOVE"
1157 col
.prop(matlib
, "show_prefs", icon
="MODIFIER", text
="Settings")
1160 if not matlib
.hide_search
:
1161 row
= layout
.row(align
=True)
1162 row
.prop_search(matlib
, "search", matlib
, "materials", text
="", icon
="VIEWZOOM")
1166 if matlib
.active_material
:
1167 row
.label(text
="Category:")
1168 row
.label(text
= matlib
.active_material
.category
)
1170 row
.label(text
="Category Tools:")
1171 row
= layout
.row(align
=True)
1173 if matlib
.current_category
: text
= matlib
.current_category
1174 row
.menu("MATLIB_MT_CatsMenu",text
=text
)
1175 row
= layout
.row(align
=True)
1176 row
.prop(matlib
, "filter", icon
="FILTER", text
="Filter")
1177 row
.operator("matlib.operator", icon
="FILE_PARENT", text
="Set Type").cmd
="FILTER_SET"
1178 row
.operator("matlib.operator", icon
="ADD", text
="New").cmd
="FILTER_ADD"
1179 row
.operator("matlib.operator", icon
="REMOVE", text
="Remove").cmd
="FILTER_REMOVE"
1182 if matlib
.show_prefs
:
1183 row
= layout
.row(align
=True)
1184 row
.prop(matlib
, "force_import")
1185 row
.prop(matlib
, "link")
1186 row
.prop(matlib
, "hide_search")
1187 # row = layout.row(align=True)
1189 #row.operator("matlib.operator", icon="URL", text="Convert Library").cmd="CONVERT"
1191 # row = layout.row()
1192 # if (matlib.current_library):
1193 # row.label(matlib.current_library.name)
1195 # row.label(text="Library not found!.")
1206 #print(bpy.context.scene)
1210 def refresh_libs(dummy
=None):
1213 default_path
= bpy
.context
.preferences
.addons
[__name__
].preferences
.matlib_path
1214 if default_path
is not None and default_path
!= '':
1215 matlib_path
= default_path
1217 libraries
= get_libraries()
1220 def reload_library(self
, context
):
1221 bpy
.context
.preferences
.addons
[__name__
].preferences
.matlib_path
= bpy
.path
.abspath(bpy
.context
.preferences
.addons
[__name__
].preferences
.matlib_path
)
1225 class matlibvxPref(AddonPreferences
):
1226 bl_idname
= __name__
1227 matlib_path
: StringProperty(
1228 name
="Additional Path",
1229 description
="User defined path to .blend libraries files",
1232 update
=reload_library
1235 def draw(self
, context
):
1236 layout
= self
.layout
1237 layout
.prop(self
, "matlib_path")
1274 bpy
.utils
.register_class(c
)
1275 Scene
.matlib_categories
= CollectionProperty(type=EmptyGroup
)
1276 Scene
.matlib
= PointerProperty(type = matlibProperties
)
1277 bpy
.app
.handlers
.load_post
.append(refresh_libs
)
1285 # raise ValueError list.remove(x): x not in list
1286 del Scene
.matlib_categories
1291 bpy
.app
.handlers
.load_post
.remove(refresh_libs
)
1293 bpy
.utils
.unregister_class(c
)
1296 if __name__
== "__main__":