diff options
Diffstat (limited to 'materials_utils/__init__.py')
-rw-r--r-- | materials_utils/__init__.py | 2741 |
1 files changed, 0 insertions, 2741 deletions
diff --git a/materials_utils/__init__.py b/materials_utils/__init__.py deleted file mode 100644 index 70ff4fc9..00000000 --- a/materials_utils/__init__.py +++ /dev/null @@ -1,2741 +0,0 @@ -# ##### BEGIN GPL LICENSE BLOCK ##### -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software Foundation, -# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -# -# ##### END GPL LICENSE BLOCK ##### - -# (c) 2016 meta-androcto, parts based on work by Saidenka, lijenstina -# Materials Utils: by MichaleW, lijenstina, -# (some code thanks to: CoDEmanX, SynaGl0w, ideasman42) -# Materials Conversion: Silvio Falcinelli, johnzero7#, -# fixes by angavrilov and others -# Link to base names: Sybren, Texture renamer: Yadoob - -bl_info = { - "name": "Materials Utils Specials", - "author": "Community", - "version": (1, 0, 6), - "blender": (2, 79, 0), - "location": "Materials Properties Specials > Shift Q", - "description": "Materials Utils and Convertors", - "warning": "", - "wiki_url": "https://wiki.blender.org/index.php/Extensions:2.6/Py/" - "Scripts/3D_interaction/Materials_Utils", - "category": "Material" -} - -if "bpy" in locals(): - import importlib - importlib.reload(texture_rename) - importlib.reload(warning_messages_utils) -else: - from . import texture_rename - from . import warning_messages_utils - -import bpy -import os -from os import ( - path as os_path, - access as os_access, - remove as os_remove, -) -from bpy.props import ( - BoolProperty, - CollectionProperty, - EnumProperty, - IntProperty, - StringProperty, - PointerProperty, -) -from bpy.types import ( - AddonPreferences, - Menu, - Operator, - Panel, - PropertyGroup, - UIList, -) -from .warning_messages_utils import ( - warning_messages, - c_data_has_materials, - c_obj_data_has_materials, -) - -# Globals -UNDO_MESSAGE = "*Only Undo is available*" -COLUMN_SPLIT = 20 - - -# Functions - -def fake_user_set(fake_user='ON', materials='UNUSED', operator=None): - warn_mesg, w_mesg = '', "" - if materials == 'ALL': - mats = (mat for mat in bpy.data.materials if mat.library is None) - w_mesg = "(All Materials in this .blend file)" - elif materials == 'UNUSED': - mats = (mat for mat in bpy.data.materials if mat.library is None and mat.users == 0) - w_mesg = "(Unused Materials - Active/Selected Objects)" - else: - mats = [] - if materials == 'ACTIVE': - objs = [bpy.context.active_object] - w_mesg = "(All Materials on Active Object)" - elif materials == 'SELECTED': - objs = bpy.context.selected_objects - w_mesg = "(All Materials on Selected Objects)" - elif materials == 'SCENE': - objs = bpy.context.scene.objects - w_mesg = "(All Scene Objects)" - else: - # used materials - objs = bpy.data.objects - w_mesg = "(All Used Materials)" - - mats = (mat for ob in objs if hasattr(ob.data, "materials") for - mat in ob.data.materials if mat.library is None) - - # collect mat names for warning_messages - matnames = [] - - warn_mesg = ('FAKE_SET_ON' if fake_user == 'ON' else 'FAKE_SET_OFF') - - for mat in mats: - mat.use_fake_user = (fake_user == 'ON') - matnames.append(getattr(mat, "name", "NO NAME")) - - if operator: - if matnames: - warning_messages(operator, warn_mesg, matnames, 'MAT', w_mesg) - else: - warning_messages(operator, 'FAKE_NO_MAT') - - for area in bpy.context.screen.areas: - if area.type in ('PROPERTIES', 'NODE_EDITOR', 'OUTLINER'): - area.tag_redraw() - - -def replace_material(m1, m2, all_objects=False, update_selection=False, operator=None): - # replace material named m1 with material named m2 - # m1 is the name of original material - # m2 is the name of the material to replace it with - # 'all' will replace throughout the blend file - - matorg = bpy.data.materials.get(m1) - matrep = bpy.data.materials.get(m2) - - if matorg != matrep and None not in (matorg, matrep): - # store active object - if all_objects: - objs = bpy.data.objects - else: - objs = bpy.context.selected_editable_objects - - for ob in objs: - if ob.type == 'MESH': - match = False - for m in ob.material_slots: - if m.material == matorg: - m.material = matrep - # don't break the loop as the material can be - # referenced more than once - - # Indicate which objects were affected - if update_selection: - ob.select_set(True) - match = True - - if update_selection and not match: - ob.select_set(False) - else: - if operator: - warning_messages(operator, "REP_MAT_NONE") - - -def select_material_by_name(find_mat_name): - # in object mode selects all objects with material find_mat_name - # in edit mode selects all polygons with material find_mat_name - - find_mat = bpy.data.materials.get(find_mat_name) - - if find_mat is None: - return - - # check for edit mode - editmode = False - - scn = bpy.context.scene - - # set selection mode to polygons - scn.tool_settings.mesh_select_mode = False, False, True - - actob = bpy.context.active_object - if actob.mode == 'EDIT': - editmode = True - bpy.ops.object.mode_set() - - if not editmode: - objs = bpy.data.objects - for ob in objs: - if included_object_types(ob.type): - ms = ob.material_slots - for m in ms: - if m.material == find_mat: - ob.select_set(True) - # the active object may not have the mat! - # set it to one that does! - scn.objects.active = ob - break - else: - ob.select_set(False) - # deselect non-meshes - else: - ob.select_set(False) - else: - # it's edit mode, so select the polygons - ob = actob - ms = ob.material_slots - - # same material can be on multiple slots - slot_indeces = [] - i = 0 - - for m in ms: - if m.material == find_mat: - slot_indeces.append(i) - i += 1 - me = ob.data - - for f in me.polygons: - if f.material_index in slot_indeces: - f.select = True - else: - f.select = False - me.update() - - if editmode: - bpy.ops.object.mode_set(mode='EDIT') - - -def mat_to_texface(operator=None): - # assigns the first image in each material to the polygons in the active - # uv layer for all selected objects - - # check for editmode - editmode = False - - actob = bpy.context.active_object - if actob.mode == 'EDIT': - editmode = True - bpy.ops.object.mode_set() - - # collect object names for warning messages - message_a = [] - # Flag if there are non MESH objects selected - mixed_obj = False - - for ob in bpy.context.selected_editable_objects: - if ob.type == 'MESH': - # get the materials from slots - ms = ob.material_slots - - # build a list of images, one per material - images = [] - # get the textures from the mats - for m in ms: - if m.material is None: - continue - gotimage = False - textures = zip(m.material.texture_slots, m.material.use_textures) - for t, enabled in textures: - if enabled and t is not None: - tex = t.texture - if tex.type == 'IMAGE': - img = tex.image - images.append(img) - gotimage = True - break - - if not gotimage: - images.append(None) - - # check materials for warning messages - mats = ob.material_slots.keys() - if operator and not mats and mixed_obj is False: - message_a.append(ob.name) - - # now we have the images, apply them to the uvlayer - me = ob.data - - # got uvs? - if not me.uv_textures: - scn = bpy.context.scene - scn.objects.active = ob - bpy.ops.mesh.uv_texture_add() - scn.objects.active = actob - - # get active uv layer - for t in me.uv_textures: - if t.active: - uvtex = t.data - for f in me.polygons: - # check that material had an image! - if images and images[f.material_index] is not None: - uvtex[f.index].image = images[f.material_index] - else: - uvtex[f.index].image = None - me.update() - else: - message_a.append(ob.name) - mixed_obj = True - - if editmode: - bpy.ops.object.mode_set(mode='EDIT') - - if operator and message_a: - warn_mess = ('MAT_TEX_NO_MESH' if mixed_obj is True else 'MAT_TEX_NO_MAT') - warning_messages(operator, warn_mess, message_a) - - -def assignmatslots(ob, matlist): - # given an object and a list of material names - # removes all material slots from the object - # adds new ones for each material in matlist - # adds the materials to the slots as well. - - scn = bpy.context.scene - ob_active = bpy.context.active_object - scn.objects.active = ob - - for s in ob.material_slots: - remove_material_slot() - - # re-add them and assign material - if matlist: - for m in matlist: - try: - mat = bpy.data.materials[m] - ob.data.materials.append(mat) - except: - # there is no material with that name in data - # or an empty mat is for some reason assigned - # to face indices, mat tries to get an '' as mat index - pass - - # restore active object - scn.objects.active = ob_active - - -def cleanmatslots(operator=None): - # check for edit mode - editmode = False - actob = bpy.context.active_object - - # active object? - if actob: - if actob.mode == 'EDIT': - editmode = True - bpy.ops.object.mode_set() - - # is active object selected ? - selected = bool(actob.select) - actob.select_set(True) - - objs = bpy.context.selected_editable_objects - # collect all object names for warning_messages - message_a = [] - # Flags if there are non MESH objects selected - mixed_obj, mixed_obj_slot = False, False - - for ob in objs: - if ob.type != 'MESH': - mat_empty = [] - message_a.append(getattr(ob, "name", "NO NAME")) - if mixed_obj is False: - mixed_obj = True - - # at least try to remove empty material slots - if ob.type in {'CURVE', 'SURFACE', 'FONT', 'META'}: - mats = ob.material_slots - mat_empty = [i for i, slot in enumerate(mats) if not slot.material] - - if not mat_empty: - continue - - if mixed_obj_slot is False: - mixed_obj_slot = True - - # Ctx - copy the context for operator override - Ctx = bpy.context.copy() - # for this operator it needs only the active object replaced - Ctx['object'] = ob - - for index in mat_empty: - try: - ob.active_material_index = index - bpy.ops.object.material_slot_remove(Ctx) - except: - continue - continue - - mats = ob.material_slots.keys() - maxindex = len(mats) - 1 # indices start from zero - - # if mats is empty then mats[faceindex] will be out of range - if not mats: - message_a.append(getattr(ob, "name", "NO NAME")) - continue - - # check the polygons on the mesh to build a list of used materials - usedMatIndex = [] # we'll store used materials indices here - faceMats = [] - badIndices = set() # collect face indices that are out material slot's range - me = ob.data - for f in me.polygons: - # get the material index for this face... - faceindex = f.material_index - # check if the mats[faceindex] is not out of range - if faceindex > maxindex: - badIndices.add(faceindex) - continue - - # indices will be lost: Store face mat use by name - currentfacemat = mats[faceindex] - faceMats.append(currentfacemat) - - # check if index is already listed as used or not - found = False - for m in usedMatIndex: - if m == faceindex: - found = True - - if found is False: - # add this index to the list - usedMatIndex.append(faceindex) - - # re-assign the used mats to the mesh and leave out the unused - ml = [] - mnames = [] - for u in usedMatIndex: - if u not in badIndices: - ml.append(mats[u]) - # we'll need a list of names to get the face indices... - mnames.append(mats[u]) - - assignmatslots(ob, ml) - - # restore face indices: - i = 0 - for f in me.polygons: - if i not in badIndices: - matindex = mnames.index(faceMats[i]) - f.material_index = matindex - i += 1 - - if message_a and operator: - warn_s = 'C_OB_MIX_NO_MAT' if mixed_obj is True else 'C_OB_NO_MAT' - warn_mess = 'C_OB_MIX_SLOT_MAT' if mixed_obj_slot else warn_s - warning_messages(operator, warn_mess, message_a) - - if actob: - # restore selection state - actob.select = selected - - if editmode: - bpy.ops.object.mode_set(mode='EDIT') - - -# separate edit mode mesh function (faster than iterating through all faces) - -def assign_mat_mesh_edit(matname="Default", operator=None): - actob = bpy.context.active_object - found = False - - # check if material exists, if it doesn't then create it - target = bpy.data.materials.get(matname) - - if not target: - target = bpy.data.materials.new(matname) - - if (actob.type in {'MESH'} and actob.mode in {'EDIT'}): - # check material slots for matname material - found = False - i = 0 - mats = actob.material_slots - for m in mats: - if m.name == matname: - found = True - # make slot active - actob.active_material_index = i - break - i += 1 - - # the material is not attached to the object - if not found: - actob.data.materials.append(target) - - # is selected ? - selected = bool(actob.select) - # select active object - actob.select_set(True) - - # activate the chosen material - actob.active_material_index = i - - # assign the material to the object - bpy.ops.object.material_slot_assign() - actob.data.update() - - # restore selection state - actob.select = selected - - if operator: - mat_names = ("A New Untitled" if matname in ("", None) else matname) - warning_messages(operator, 'A_MAT_NAME_EDIT', mat_names, 'MAT') - - -def assign_mat(matname="Default", operator=None): - # get the active object so we can restore it later - actob = bpy.context.active_object - - # is active object selected ? - selected = bool(actob.select_get) - actob.select_set(True) - - # check if material exists, if it doesn't then create it - target = bpy.data.materials.get(matname) - - if not target: - target = bpy.data.materials.new(matname) - - # if object mode then set all polygons - editmode = False - allpolygons = True - - if actob.mode == 'EDIT': - editmode = True - allpolygons = False - bpy.ops.object.mode_set() - - objs = bpy.context.selected_editable_objects - - # collect non mesh object names - message_a = [] - - for ob in objs: - # skip the objects that can't have mats - if not included_object_types(ob.type): - message_a.append(ob.name) - continue - else: - # set the active object to our object - scn = bpy.context.scene - scn.objects.select_get = ob - - if ob.type in {'CURVE', 'SURFACE', 'FONT', 'META'}: - found = False - i = 0 - for m in bpy.data.materials: - if m.name == matname: - found = True - index = i - break - i += 1 - if not found: - index = i - 1 - targetlist = [index] - assignmatslots(ob, targetlist) - elif ob.type == 'MESH': - # check material slots for matname material - found = False - i = 0 - mats = ob.material_slots - for m in mats: - if m.name == matname: - found = True - index = i - # make slot active - ob.active_material_index = i - break - i += 1 - - if not found: - index = i - # the material is not attached to the object - ob.data.materials.append(target) - - # now assign the material: - me = ob.data - if allpolygons: - for f in me.polygons: - f.material_index = index - elif allpolygons is False: - for f in me.polygons: - if f.select: - f.material_index = index - me.update() - - # restore the active object - bpy.context.view_layer.objects.active = actob - - # restore selection state - actob.select_set = selected - - if editmode: - bpy.ops.object.mode_set(mode='EDIT') - - if operator and message_a: - warning_messages(operator, 'A_OB_MIX_NO_MAT', message_a) - - -def check_texture(img, mat): - # finds a texture from an image - # makes a texture if needed - # adds it to the material if it isn't there already - - tex = bpy.data.textures.get(img.name) - - if tex is None: - tex = bpy.data.textures.new(name=img.name, type='IMAGE') - - tex.image = img - - # see if the material already uses this tex - # add it if needed - found = False - for m in mat.texture_slots: - if m and m.texture == tex: - found = True - break - if not found and mat: - mtex = mat.texture_slots.add() - mtex.texture = tex - mtex.texture_coords = 'UV' - mtex.use_map_color_diffuse = True - - -def texface_to_mat(operator=None): - # edit mode check here! - editmode = False - ob = bpy.context.object - if ob.mode == 'EDIT': - editmode = True - bpy.ops.object.mode_set() - - for ob in bpy.context.selected_editable_objects: - faceindex = [] - unique_images = [] - # collect object names for warning messages - message_a = [] - - # check if object has UV and texture data and active image in Editor - if check_texface_to_mat(ob): - # get the texface images and store indices - for f in ob.data.uv_textures.active.data: - if f.image: - img = f.image - # build list of unique images - if img not in unique_images: - unique_images.append(img) - faceindex.append(unique_images.index(img)) - else: - img = None - faceindex.append(None) - else: - message_a.append(ob.name) - continue - - # check materials for images exist; create if needed - matlist = [] - - for i in unique_images: - if i: - try: - m = bpy.data.materials[i.name] - except: - m = bpy.data.materials.new(name=i.name) - continue - - finally: - matlist.append(m.name) - # add textures if needed - check_texture(i, m) - - # set up the object material slots - assignmatslots(ob, matlist) - - # set texface indices to material slot indices.. - me = ob.data - - i = 0 - for f in faceindex: - if f is not None: - me.polygons[i].material_index = f - i += 1 - if editmode: - bpy.ops.object.mode_set(mode='EDIT') - - if operator and message_a: - warning_messages(operator, "TEX_MAT_NO_CRT", message_a) - - -def remove_materials(operator=None, setting="SLOT"): - # Remove material slots from active object - # SLOT - removes the object's active material - # ALL - removes the all the object's materials - actob = bpy.context.active_object - actob_name = getattr(actob, "name", "NO NAME") - - if not actob: - return - - if not included_object_types(actob.type): - if operator: - warning_messages(operator, 'OB_CANT_MAT', actob_name) - return - - if (hasattr(actob.data, "materials") and len(actob.data.materials) > 0): - if setting == "SLOT": - remove_material_slot() - elif setting == "ALL": - for mat in actob.data.materials: - try: - remove_material_slot() - except: - pass - - if operator: - warn_mess = 'R_ACT_MAT_ALL' if setting == "ALL" else 'R_ACT_MAT' - warning_messages(operator, warn_mess, actob_name) - - elif operator: - warning_messages(operator, 'R_OB_NO_MAT', actob_name) - - -def remove_materials_all(operator=None): - # Remove material slots from all selected objects - # counter for material slots warning messages, collect errors - mat_count, collect_mess = False, [] - - for ob in bpy.context.selected_editable_objects: - if not included_object_types(ob.type): - continue - else: - # code from blender stack exchange (by CoDEmanX) - ob.active_material_index = 0 - - if (hasattr(ob.data, "materials") and len(ob.material_slots) >= 1): - mat_count = True - - # Ctx - copy the context for operator override - Ctx = bpy.context.copy() - # for this operator it needs only the active object replaced - Ctx['object'] = ob - - for i in range(len(ob.material_slots)): - try: - bpy.ops.object.material_slot_remove(Ctx) - except: - ob_name = getattr(ob, "name", "NO NAME") - collect_mess.append(ob_name) - pass - - if operator: - warn_msg = ('R_ALL_NO_MAT' if mat_count is False else 'R_ALL_SL_MAT') - if not collect_mess: - warning_messages(operator, warn_msg) - else: - warning_messages(operator, 'R_OB_FAIL_MAT', collect_mess) - - -# Operator Classes # - -class VIEW3D_OT_show_mat_preview(Operator): - bl_label = "Preview Active Material" - bl_idname = "view3d.show_mat_preview" - bl_description = "Show the preview of Active Material and context related settings" - bl_options = {'REGISTER', 'UNDO'} - - is_not_undo = False # prevent drawing props on undo - - @classmethod - def poll(cls, context): - obj = context.active_object - return (obj is not None and - obj.active_material is not None and - included_object_types(obj.type)) - - def invoke(self, context, event): - self.is_not_undo = True - return context.window_manager.invoke_props_dialog(self, width=200) - - def draw(self, context): - layout = self.layout - ob = context.active_object - prw_size = size_preview() - - if self.is_not_undo is False: - layout.label(text=UNDO_MESSAGE, icon="INFO") - return - - if not (ob and hasattr(ob, "active_material")): - layout.label(text="No Active Object or Active Material", icon="INFO") - return - - mat = ob.active_material - is_opaque = ( - True if (ob and hasattr(ob, "show_transparent") and - ob.show_transparent is True) else False - ) - is_opaque_bi = ( - True if (mat and hasattr(mat, "use_transparency") and - mat.use_transparency is True) else False - ) - is_mesh = True if ob.type == 'MESH' else False - - if size_type_is_preview(): - layout.template_ID_preview(ob, "active_material", new="material.new", - rows=prw_size['Width'], cols=prw_size['Height']) - else: - layout.template_ID(ob, "active_material", new="material.new") - layout.separator() - - if c_render_engine("Both"): - layout.prop(mat, "use_nodes", icon='NODETREE') - - if not c_need_of_viewport_colors(): - other_render = ( - "*Unavailable with this Renderer*" if not c_render_engine("Both") else - "*Unavailable in this Context*" - ) - no_col_label = ( - "*Only available in Solid Shading*" if c_render_engine("Cycles") else - other_render - ) - layout.label(text=no_col_label, icon="INFO") - return - - color_txt = "Viewport Color:" if c_render_engine("Cycles") else "Diffuse" - spec_txt = "Viewport Specular:" if c_render_engine("Cycles") else "Specular" - col = layout.column(align=True) - col.label(color_txt) - col.prop(mat, "diffuse_color", text="") - - if c_render_engine("BI"): - # Blender Render - col.prop(mat, "diffuse_intensity", text="Intensity") - col.separator() - - col.label(spec_txt) - col.prop(mat, "specular_color", text="") - col.prop(mat, "specular_hardness") - - if (c_render_engine("BI") and not c_context_use_nodes()): - # Blender Render - col.separator() - col.prop(mat, "use_transparency") - col.separator() - if is_opaque_bi: - col.prop(mat, "transparency_method", text="") - col.separator() - col.prop(mat, "alpha") - elif (c_render_engine("Cycles") and is_mesh): - # Cycles - col.separator() - col.prop(ob, "show_transparent", text="Transparency") - if is_opaque: - col.separator() - col.prop(mat, "alpha") - col.separator() - col.label(text="Viewport Alpha:") - col.prop(mat.game_settings, "alpha_blend", text="") - layout.separator() - - def check(self, context): - return self.is_not_undo - - def execute(self, context): - self.is_not_undo = False - - return {'FINISHED'} - - -class VIEW3D_OT_copy_material_to_selected(Operator): - bl_idname = "view3d.copy_material_to_selected" - bl_label = "Copy Materials to others" - bl_description = ("Copy Material From Active to Selected objects\n" - "In case of multiple materials, only the first slot is assigned\n" - "Works on Object's Data linked Materials") - bl_options = {'REGISTER', 'UNDO'} - - @classmethod - def poll(cls, context): - obj = context.active_object - return (c_data_has_materials() and - obj is not None and - included_object_types(obj.type) and - obj.active_material is not None and - context.selected_editable_objects) - - def execute(self, context): - warn_mess = "DEFAULT" - if (len(context.selected_editable_objects) < 2): - warn_mess = 'CPY_MAT_ONE_OB' - else: - if check_is_excluded_obj_types(context): - warn_mess = 'CPY_MAT_MIX_OB' - try: - bpy.ops.object.material_slot_copy() - warn_mess = 'CPY_MAT_DONE' - except: - warning_messages(self, 'CPY_MAT_FAIL') - return {'CANCELLED'} - - warning_messages(self, warn_mess) - - return {'FINISHED'} - - -class VIEW3D_OT_texface_to_material(Operator): - bl_idname = "view3d.texface_to_material" - bl_label = "Texface Images to Material/Texture" - bl_description = ("Create texture materials for images assigned in UV editor \n" - "Needs an UV Unwrapped Mesh and an image active in the \n" - "UV/Image Editor for each Selected Object") - bl_options = {'REGISTER', 'UNDO'} - - @classmethod - def poll(cls, context): - return context.active_object is not None - - def invoke(self, context, event): - return context.window_manager.invoke_confirm(self, event) - - def execute(self, context): - if not context.selected_editable_objects: - warning_messages(self, 'TEX_MAT_NO_SL') - return {'CANCELLED'} - - texface_to_mat(self) - - return {'FINISHED'} - - -class VIEW3D_OT_set_new_material_name(Operator): - bl_idname = "view3d.set_new_material_name" - bl_label = "New Material Settings" - bl_description = ("Set the Base name of the new Material\n" - "and tweaking after the new Material creation") - bl_options = {'REGISTER'} - - def invoke(self, context, event): - return context.window_manager.invoke_props_dialog(self) - - def draw(self, context): - layout = self.layout - scene = context.scene.mat_context_menu - - box = layout.box() - box.label(text="Base name:") - box.prop(scene, "set_material_name", text="", icon="SYNTAX_ON") - layout.separator() - layout.prop(scene, "use_tweak") - - def execute(self, context): - - return {'FINISHED'} - - -class VIEW3D_OT_assign_material(Operator): - bl_idname = "view3d.assign_material" - bl_label = "Assign Material" - bl_description = "Assign a material to the selection" - bl_property = "matname" - bl_options = {'REGISTER', 'UNDO'} - - is_existing: BoolProperty( - name="Is it a new Material", - options={'HIDDEN'}, - default=True, - ) - matname: StringProperty( - name="Material Name", - description="Name of the Material to Assign", - options={'HIDDEN'}, - default="Material_New", - maxlen=128, - ) - is_not_undo = False # prevent drawing props on undo - - @classmethod - def poll(cls, context): - return context.active_object is not None - - def invoke(self, context, event): - self.is_not_undo = True - - if not self.is_existing or use_mat_menu_type() not in {'POPUP'}: - return self.execute(context) - - materials_lists_fill_names(context, refresh=True, is_object=False) - return context.window_manager.invoke_props_dialog(self, width=400, height=200) - - def check(self, context): - return self.is_not_undo - - def draw(self, context): - draw_ui_list_popups(self, context, obj_data=False) - - def execute(self, context): - actob = context.active_object - scene = context.scene.mat_context_menu - mn = self.matname - tweak = scene.use_tweak - - if use_mat_menu_type() == 'POPUP' and self.is_existing: - mats_col = context.scene.mat_specials_mats - len_mats = len(mats_col) - mat_index = scene.index_mat - - if not (len_mats > 0 and mat_index is not None and mat_index <= len_mats): - self.report({'WARNING'}, - "No Materials in the Scene. Please use the Add New option") - return {"CANCELLED"} - - mats_up = mats_col[mat_index].name - mn = mats_up - - if not self.is_existing: - new_name = check_mat_name_unique(scene.set_material_name) - mn = new_name - - if (actob.type in {'MESH'} and actob.mode in {'EDIT'}): - assign_mat_mesh_edit(mn, self) - else: - assign_mat(mn, self) - - if use_cleanmat_slots(): - cleanmatslots() - - mat_to_texface() - self.is_not_undo = False - - activate_mat_slot(actob, mn) - - if tweak and not self.is_existing: - try: - bpy.ops.view3d.show_mat_preview('INVOKE_DEFAULT') - except: - self.report({'INFO'}, "Preview Active Material could not be opened") - - return {'FINISHED'} - - -class VIEW3D_OT_clean_material_slots(Operator): - bl_idname = "view3d.clean_material_slots" - bl_label = "Clean Material Slots" - bl_description = ("Removes any unused material slots \n" - "from selected objects in Object mode") - bl_options = {'REGISTER', 'UNDO'} - - @classmethod - # materials can't be removed in Edit mode - def poll(cls, context): - return (c_data_has_materials() and - context.active_object is not None and - not context.object.mode == 'EDIT') - - def execute(self, context): - cleanmatslots(self) - - return {'FINISHED'} - - -class VIEW3D_OT_material_to_texface(Operator): - bl_idname = "view3d.material_to_texface" - bl_label = "Material Images to Texface" - bl_description = ("Transfer material assignments to UV editor \n" - "Works on a Mesh Object with a Material and Texture\n" - "assigned. Used primarily with MultiTexture Shading") - bl_options = {'REGISTER', 'UNDO'} - - @classmethod - def poll(cls, context): - return (c_data_has_materials() and - context.active_object is not None) - - def execute(self, context): - if not context.selected_editable_objects: - warning_messages(self, "MAT_TEX_NO_SL") - return {'CANCELLED'} - - mat_to_texface(self) - - return {'FINISHED'} - - -class VIEW3D_OT_material_remove_slot(Operator): - bl_idname = "view3d.material_remove_slot" - bl_label = "Remove Active Slot (Active Object)" - bl_description = ("Remove active material slot from active object \n" - "Can't be used in Edit Mode") - bl_options = {'REGISTER', 'UNDO'} - - @classmethod - def poll(cls, context): - # materials can't be removed in Edit mode - return (c_data_has_materials() and - context.active_object is not None and - not context.active_object.mode == 'EDIT') - - def execute(self, context): - if not context.selected_editable_objects: - warning_messages(self, 'R_NO_SL_MAT') - return {'CANCELLED'} - - remove_materials(self, "SLOT") - - return {'FINISHED'} - - -class VIEW3D_OT_material_remove_object(Operator): - bl_idname = "view3d.material_remove_object" - bl_label = "Remove all Slots (Active Object)" - bl_description = ("Remove all material slots from active object \n" - "Can't be used in Edit Mode") - bl_options = {'REGISTER', 'UNDO'} - - @classmethod - def poll(cls, context): - # materials can't be removed in Edit mode - return (c_data_has_materials() and - context.active_object is not None and - not context.active_object.mode == 'EDIT') - - def execute(self, context): - if not context.selected_editable_objects: - warning_messages(self, 'R_NO_SL_MAT') - return {'CANCELLED'} - - remove_materials(self, "ALL") - - return {'FINISHED'} - - -class VIEW3D_OT_material_remove_all(Operator): - bl_idname = "view3d.material_remove_all" - bl_label = "Remove All Material Slots" - bl_description = ("Remove all material slots from all selected objects \n" - "Can't be used in Edit Mode") - bl_options = {'REGISTER', 'UNDO'} - - @classmethod - def poll(cls, context): - # materials can't be removed in Edit mode - return (c_data_has_materials() and - context.active_object is not None and - not context.active_object.mode == 'EDIT') - - def invoke(self, context, event): - return context.window_manager.invoke_confirm(self, event) - - def execute(self, context): - if not context.selected_editable_objects: - warning_messages(self, 'R_NO_SL_MAT') - return {'CANCELLED'} - - remove_materials_all(self) - - return {'FINISHED'} - - -class VIEW3D_OT_select_material_by_name(Operator): - bl_idname = "view3d.select_material_by_name" - bl_label = "Select Material By Name" - bl_description = "Select geometry with this material assigned to it" - bl_options = {'REGISTER', 'UNDO'} - - matname: StringProperty( - name="Material Name", - description="Name of Material to Select", - maxlen=63, - ) - is_not_undo = False - is_edit = False - - @classmethod - def poll(cls, context): - obj = context.active_object - return (c_data_has_materials() and - obj is not None and obj.mode in {"OBJECT", "EDIT"}) - - def invoke(self, context, event): - self.is_not_undo = True - - if use_mat_menu_type() not in {'POPUP'}: - return self.execute(context) - - obj = context.active_object - self.is_edit = bool(obj.mode == 'EDIT') - materials_lists_fill_names(context, refresh=True, is_object=self.is_edit) - - return context.window_manager.invoke_props_dialog(self, width=400, height=200) - - def check(self, context): - return self.is_not_undo - - def draw(self, context): - draw_ui_list_popups(self, context, obj_data=self.is_edit) - - def execute(self, context): - if use_mat_menu_type() == 'POPUP': - mats_col = context.scene.mat_specials_mats - scene = context.scene.mat_context_menu - len_mats = len(mats_col) - mat_index = scene.index_mat - - if not (len_mats > 0 and mat_index is not None and mat_index <= len_mats): - self.report({'WARNING'}, - "No materials found. Operation Cancelled") - return {"CANCELLED"} - - mats_up = mats_col[mat_index].name - mn = mats_up - else: - mn = self.matname - - select_material_by_name(mn) - message = 'SL_MAT_EDIT_BY_NAME' if self.is_edit else 'SL_MAT_BY_NAME' - warning_messages(self, message, mn) - self.is_not_undo = False - - return {'FINISHED'} - - -class VIEW3D_OT_replace_material(Operator): - bl_idname = "view3d.replace_material" - bl_label = "Replace Material" - bl_description = "Replace a material by name" - bl_options = {'REGISTER', 'UNDO'} - - matorg: StringProperty( - name="Original", - description="Material to replace", - maxlen=63, - ) - matrep: StringProperty( - name="Replacement", - description="Replacement material", - maxlen=63, - ) - all_objects: BoolProperty( - name="All objects", - description="If enabled, replace for all objects in this blend file\n" - "If disabled, only selected objects will be affected", - default=False, - ) - update_selection: BoolProperty( - name="Update Selection", - description="Select affected objects and deselect unaffected", - default=True, - ) - - @classmethod - def poll(cls, context): - return c_data_has_materials() - - def draw(self, context): - layout = self.layout - layout.prop_search(self, "matorg", bpy.data, "materials") - layout.prop_search(self, "matrep", bpy.data, "materials") - layout.prop(self, "all_objects") - layout.prop(self, "update_selection") - - def invoke(self, context, event): - return context.window_manager.invoke_props_dialog(self) - - def execute(self, context): - replace_material( - self.matorg, self.matrep, self.all_objects, - self.update_selection, self - ) - self.matorg, self.matrep = "", "" - - return {'FINISHED'} - - -class VIEW3D_OT_fake_user_set(Operator): - bl_idname = "view3d.fake_user_set" - bl_label = "Set Fake User" - bl_description = "Enable/disable fake user for materials" - bl_options = {'REGISTER', 'UNDO'} - - fake_user: EnumProperty( - name="Fake User", - description="Turn fake user on or off", - items=(('ON', "On", "Enable fake user"), ('OFF', "Off", "Disable fake user")), - default='ON', - ) - materials: EnumProperty( - name="Materials", - description="Chose what objects and materials to affect", - items=(('ACTIVE', "Active object", "Materials of active object only"), - ('SELECTED', "Selected objects", "Materials of selected objects"), - ('SCENE', "Scene objects", "Materials of objects in current scene"), - ('USED', "Used", "All materials used by objects"), - ('UNUSED', "Unused", "Currently unused materials"), - ('ALL', "All", "All materials in this blend file")), - default='UNUSED', - ) - - @classmethod - def poll(cls, context): - return c_data_has_materials() - - def draw(self, context): - layout = self.layout - layout.prop(self, "fake_user", expand=True) - layout.prop(self, "materials") - - def invoke(self, context, event): - return context.window_manager.invoke_props_dialog(self) - - def execute(self, context): - fake_user_set(self.fake_user, self.materials, self) - - return {'FINISHED'} - - -class MATERIAL_OT_set_transparent_back_side(Operator): - bl_idname = "material.set_transparent_back_side" - bl_label = "Transparent back (BI)" - bl_description = ("Creates BI nodes with Alpha output connected to Front/Back\n" - "Geometry node on Object's Active Material Slot") - bl_options = {'REGISTER', 'UNDO'} - - @classmethod - def poll(cls, context): - obj = context.active_object - if not obj: - return False - mat = obj.active_material - if not mat: - return False - if mat.node_tree: - if (len(mat.node_tree.nodes) == 0): - return True - if not mat.use_nodes: - return True - return False - - def execute(self, context): - obj = context.active_object - mat = obj.active_material - try: - mat.use_nodes = True - if (mat.node_tree): - for node in mat.node_tree.nodes: - if (node): - mat.node_tree.nodes.remove(node) - - mat.use_transparency = True - node_mat = mat.node_tree.nodes.new('ShaderNodeMaterial') - node_out = mat.node_tree.nodes.new('ShaderNodeOutput') - node_geo = mat.node_tree.nodes.new('ShaderNodeGeometry') - node_mat.material = mat - node_out.location = [node_out.location[0] + 500, node_out.location[1]] - node_geo.location = [node_geo.location[0] + 150, node_geo.location[1] - 150] - mat.node_tree.links.new(node_mat.outputs[0], node_out.inputs[0]) - mat.node_tree.links.new(node_geo.outputs[8], node_out.inputs[1]) - except: - warning_messages(self, 'E_MAT_TRNSP_BACK') - return {'CANCELLED'} - - if hasattr(mat, "name"): - warning_messages(self, 'MAT_TRNSP_BACK', mat.name, 'MAT') - - return {'FINISHED'} - - -class MATERIAL_OT_move_slot_top(Operator): - bl_idname = "material.move_material_slot_top" - bl_label = "Slot to the top" - bl_description = "Move the active material slot on top" - bl_options = {'REGISTER', 'UNDO'} - - @classmethod - def poll(cls, context): - obj = context.active_object - if not obj: - return False - if (len(obj.material_slots) <= 2): - return False - if (obj.active_material_index <= 0): - return False - return True - - def execute(self, context): - activeObj = context.active_object - - for i in range(activeObj.active_material_index): - bpy.ops.object.material_slot_move(direction='UP') - - active_mat = context.object.active_material - if active_mat and hasattr(active_mat, "name"): - warning_messages(self, 'MOVE_SLOT_UP', active_mat.name, 'MAT') - - return {'FINISHED'} - - -class MATERIAL_OT_move_slot_bottom(Operator): - bl_idname = "material.move_material_slot_bottom" - bl_label = "Slots to the bottom" - bl_description = "Move the active material slot to the bottom" - bl_options = {'REGISTER', 'UNDO'} - - @classmethod - def poll(cls, context): - obj = context.active_object - if not obj: - return False - if (len(obj.material_slots) <= 2): - return False - if (len(obj.material_slots) - 1 <= obj.active_material_index): - return False - return True - - def execute(self, context): - activeObj = context.active_object - lastSlotIndex = len(activeObj.material_slots) - 1 - - for i in range(lastSlotIndex - activeObj.active_material_index): - bpy.ops.object.material_slot_move(direction='DOWN') - - active_mat = context.object.active_material - if active_mat and hasattr(active_mat, "name"): - warning_messages(self, 'MOVE_SLOT_DOWN', active_mat.name, 'MAT') - - return {'FINISHED'} - - -class MATERIAL_OT_link_to_base_names(Operator): - bl_idname = "material.link_to_base_names" - bl_label = "Merge Base Names" - bl_description = ("Replace .001, .002 slots with Original \n" - "Material/Name on All Materials/Objects") - bl_options = {'REGISTER', 'UNDO'} - - mat_keep: StringProperty( - name="Material to keep", - default="", - ) - is_auto: BoolProperty( - name="Auto Rename/Replace", - description="Automatically Replace names by stripping numerical suffix", - default=False, - ) - mat_error = [] # collect mat for warning messages - is_not_undo = False # prevent drawing props on undo - check_no_name = True # check if no name is passed - - @classmethod - def poll(cls, context): - return (c_data_has_materials() and context.active_object is not None) - - def draw(self, context): - layout = self.layout - - if self.is_not_undo is False: - layout.label(text=UNDO_MESSAGE, icon="INFO") - return - - boxee = layout.box() - boxee.prop_search(self, "mat_keep", bpy.data, "materials") - boxee.enabled = not self.is_auto - layout.separator() - - boxs = layout.box() - boxs.prop(self, "is_auto", text="Auto Rename/Replace", icon="SYNTAX_ON") - - def invoke(self, context, event): - self.is_not_undo = True - return context.window_manager.invoke_props_dialog(self) - - def replace_name(self): - # use the chosen material as a base one, check if there is a name - self.check_no_name = (False if self.mat_keep in {""} else True) - - if self.check_no_name is True: - for mat in bpy.data.materials: - name = mat.name - if name == self.mat_keep: - try: - base, suffix = name.rsplit('.', 1) - # trigger the exception - num = int(suffix, 10) - self.mat_keep = base - mat.name = self.mat_keep - return - except ValueError: - if name not in self.mat_error: - self.mat_error.append(name) - return - return - - def split_name(self, material): - name = material.name - - if '.' not in name: - return name, None - - base, suffix = name.rsplit('.', 1) - - try: - # trigger the exception - num = int(suffix, 10) - except ValueError: - # Not a numeric suffix - if name not in self.mat_error: - self.mat_error.append(name) - return name, None - - if self.is_auto is False: - if base == self.mat_keep: - return base, suffix - else: - return name, None - - return base, suffix - - def fixup_slot(self, slot): - if not slot.material: - return - - base, suffix = self.split_name(slot.material) - if suffix is None: - return - - try: - base_mat = bpy.data.materials[base] - except KeyError: - print("\n[Materials Utils Specials]\nLink to base names\nError:" - "Base material %r not found\n" % base) - return - - slot.material = base_mat - - def check(self, context): - return self.is_not_undo - - def main_loop(self, context): - for ob in context.scene.objects: - for slot in ob.material_slots: - self.fixup_slot(slot) - - def execute(self, context): - if self.is_auto is False: - self.replace_name() - if self.check_no_name is True: - self.main_loop(context) - else: - warning_messages(self, 'MAT_LINK_NO_NAME') - self.is_not_undo = False - return {'CANCELLED'} - - self.main_loop(context) - - if use_cleanmat_slots(): - cleanmatslots() - - if self.mat_error: - warning_messages(self, 'MAT_LINK_ERROR', self.mat_error, 'MAT') - - self.is_not_undo = False - - return {'FINISHED'} - - -class MATERIAL_OT_check_converter_path(Operator): - bl_idname = "material.check_converter_path" - bl_label = "Check Converters images/data save path" - bl_description = "Check if the given path is writable (has OS writing privileges)" - bl_options = {'REGISTER', 'INTERNAL'} - - def check_valid_path(self, context): - sc = context.scene - paths = bpy.path.abspath(sc.mat_context_menu.conv_path) - - if bpy.data.filepath == "": - warning_messages(self, "DIR_PATH_EMPTY", override=True) - return False - - if not os_path.exists(paths): - warning_messages(self, 'DIR_PATH_N_ERROR', override=True) - return False - - if not os_access(paths, os.W_OK | os.X_OK): - warning_messages(self, 'DIR_PATH_A_ERROR', override=True) - return False - - try: - path_test = os_path.join(paths, "XYfoobartestXY.txt") - with open(path_test, 'w') as f: - f.closed - os_remove(path_test) - return True - except (OSError, IOError): - warning_messages(self, 'DIR_PATH_W_ERROR', override=True) - return False - - return True - - def execute(self, context): - if not self.check_valid_path(context): - return {'CANCELLED'} - - warning_messages(self, 'DIR_PATH_W_OK', override=True) - - return {'FINISHED'} - - -# Material selections pop-up - -class VIEW3D_UL_assign_material_popup_ui(UIList): - - def draw_item(self, context, layout, data, item, icon, active_data, active_propname): - self.use_filter_show = True - - col = layout.column(align=True) - col.alignment = "LEFT" - mat = bpy.data.materials.get(item.name, None) - if not mat: - col.label(text="{} - is not available".format(item.name), icon="ERROR") - else: - split = col.split(factor=0.75, align=True) - row = split.row(align=True) - row.label(text=item.name, translate=False, icon="MATERIAL_DATA") - subrow = split.row(align=True) - subrow.alignment = "RIGHT" - subrow.label(text="", icon="PINNED" if item.mat_fake_user else "BLANK1") - subrow = split.row(align=True) - subrow.alignment = "RIGHT" - subrow.label(text="", icon="LINK_BLEND" if item.mat_lib else "BLANK1") - - -def draw_ui_list_popups(self, context, obj_data=False): - layout = self.layout - - if not self.is_not_undo: - layout.label(text=UNDO_MESSAGE, icon="INFO") - return - - matlen = len(context.scene.mat_specials_mats) - matdata = "in Object's Data" if obj_data else "in Data" - matgramma = "Material" if matlen == 1 else "Materials" - matcount = "No" if matlen < 1 else matlen - - box = layout.box() - col = box.column(align=True) - col.label(text="{} {} {}".format(matcount, matgramma, matdata), - icon="INFO") - sub_split = col.split(factor=0.7, align=True) - sub_box_1 = sub_split.box() - sub_box_1.label(text="Name") - sub_split_2 = sub_split.split(factor=0.5, align=True) - sub_box_2 = sub_split_2.box() - sub_box_2.label(text="Fake") - sub_box_3 = sub_split_2.box() - sub_box_3.label(text="Lib") - - col.template_list( - "VIEW3D_UL_assign_material_popup_ui", - 'mat_context_menu', - context.scene, - 'mat_specials_mats', - context.scene.mat_context_menu, - 'index_mat', - rows=10 - ) - return - - -# Menu classes - -class VIEW3D_MT_assign_material(Menu): - bl_label = "Assign Material" - - def draw(self, context): - layout = self.layout - layout.operator_context = 'INVOKE_REGION_WIN' - - if (not context.active_object): - # info why the add material is innactive - layout.label(text="*No active Object in the Scene*", icon="INFO") - use_separator(self, context) - - mat_prop_name = context.scene.mat_context_menu.set_material_name - add_new = layout.operator( - "view3d.assign_material", - text="Add New", icon='ZOOM_IN' - ) - add_new.matname = mat_prop_name - add_new.is_existing = False - - if use_mat_menu_type() != 'POPUP': - use_separator(self, context) - - if c_data_has_materials(): - mat_entry = layout.column() - - if use_mat_menu_type() == 'POPUP': - matp = mat_entry.operator( - "view3d.assign_material", - icon='MATERIAL_DATA' - ) - matp.is_existing = True - elif use_mat_menu_type() == 'COLUMNS': - get_col_size = switch_to_column() - mat_entry = layout.column_flow(columns=get_col_size) - - if use_mat_menu_type() in {'COLUMNS', 'STANDARD'}: - for material_name in bpy.data.materials.keys(): - mats = mat_entry.operator( - "view3d.assign_material", - text=material_name, - icon='MATERIAL_DATA' - ) - mats.matname = material_name - mats.is_existing = True - - -class VIEW3D_MT_select_material(Menu): - bl_label = "Select by Material" - - def draw(self, context): - layout = self.layout - layout.operator_context = 'INVOKE_REGION_WIN' - obj = context.active_object - has_users = False - col = layout.column() - - if not c_data_has_materials(): - layout.label(text="*No Materials in the Data*", icon="INFO") - return - elif not obj: - layout.label(text="*No Active Object*", icon="INFO") - return - elif obj.mode == "EDIT" and not c_obj_data_has_materials(obj): - layout.label(text="*No Materials in the Object's Data*", icon="INFO") - return - elif use_mat_menu_type() == 'POPUP': - col.operator( - "view3d.select_material_by_name", - text="Select by Material", - icon='HAND', - ) - return - - if obj.mode == 'OBJECT': - if use_mat_menu_type() == 'COLUMNS': - get_col_size = switch_to_column(is_edit=False) - col = layout.column_flow(columns=get_col_size) - # show all used materials in entire blend file - for material_name, material in bpy.data.materials.items(): - if material.users > 0: - has_users = True - col.operator( - "view3d.select_material_by_name", - text=material_name, - icon='MATERIAL_DATA', - ).matname = material_name - if not has_users: - layout.label(text="*No users for Materials in the data*", icon="INFO") - return - elif obj.mode == 'EDIT': - if use_mat_menu_type() == 'COLUMNS': - get_col_size = switch_to_column(is_edit=True) - col = layout.column_flow(columns=get_col_size) - # show only the materials on this object - mats = obj.material_slots.keys() - for m in mats: - if m not in "": # skip empty slots - col.operator( - "view3d.select_material_by_name", - text=m, - icon='MATERIAL_DATA' - ).matname = m - else: - layout.label(text="*Works only in Object and Edit mode*", icon="INFO") - - -class VIEW3D_MT_remove_material(Menu): - bl_label = "Clean Slots" - - def draw(self, context): - layout = self.layout - layout.operator_context = 'INVOKE_REGION_WIN' - - if context.mode in {'PAINT_TEXTURE'}: - layout.label( - text="Removing materials can lead to loss of painting data", - icon="INFO" - ) - use_separator(self, context) - - layout.operator( - "view3d.clean_material_slots", - text="Clean Material Slots", - icon='COLOR_BLUE' - ) - use_separator(self, context) - - if c_render_engine("Lux"): - layout.label(text="Sorry, other Menu functions are", icon="INFO") - layout.label(text="unavailable with Lux Renderer") - return - - layout.operator("view3d.material_remove_slot", icon='COLOR_GREEN') - layout.operator("view3d.material_remove_object", icon='COLOR_RED') - - if use_remove_mat_all(): - use_separator(self, context) - layout.operator( - "view3d.material_remove_all", - text="Remove Material Slots (All Selected Objects)", - icon='CANCEL' - ) - - -class VIEW3D_MT_master_material(Menu): - bl_label = "Material Specials Menu" - - def draw(self, context): - layout = self.layout - layout.operator_context = 'INVOKE_REGION_WIN' - - if use_mat_preview() is True: - layout.operator("view3d.show_mat_preview", icon="VISIBLE_IPO_ON") - use_separator(self, context) - - if use_mat_menu_type() == 'POPUP': - VIEW3D_MT_assign_material.draw(self, context) - use_separator(self, context) - VIEW3D_MT_select_material.draw(self, context) - else: - layout.menu("VIEW3D_MT_assign_material", icon='ZOOMIN') - layout.menu("VIEW3D_MT_select_material", icon='HAND') - use_separator(self, context) - - layout.operator("view3d.copy_material_to_selected", icon="COPY_ID") - use_separator(self, context) - - layout.menu("VIEW3D_MT_remove_material", icon="COLORSET_10_VEC") - use_separator(self, context) - - layout.operator("view3d.replace_material", - text='Replace Material', - icon='ARROW_LEFTRIGHT') - layout.operator("view3d.fake_user_set", - text='Set Fake User', - icon='UNPINNED') - use_separator(self, context) - - layout.menu("VIEW3D_MT_mat_special", icon="SOLO_ON") - - -class VIEW3D_MT_mat_special(Menu): - bl_label = "Specials" - - def draw(self, context): - layout = self.layout - - layout.operator("view3d.set_new_material_name", icon="SETTINGS") - - if c_render_engine("Cycles"): - if (enable_converters() is True and converter_type('BI_CONV')): - ml_restore_1 = layout.operator("ml.restore", - text='To BI Nodes Off', - icon="BLENDER") - ml_restore_1.switcher = False - ml_restore_1.renderer = "BI" - - ml_restore_2 = layout.operator("ml.restore", - text='To BI Nodes On', - icon="APPEND_BLEND") - ml_restore_2.switcher = True - ml_restore_2.renderer = "BI" - use_separator(self, context) - - elif c_render_engine("BI"): - if (enable_converters() is True and converter_type('CYC_CONV')): - layout.operator("ml.refresh_active", - text='Convert Active to Cycles', - icon='NODE_INSERT_OFF') - layout.operator("ml.refresh", - text='Convert All to Cycles', - icon='NODE_INSERT_ON') - use_separator(self, context) - ml_restore_1 = layout.operator("ml.restore", - text='To Cycles Nodes Off', - icon="SOLID") - ml_restore_1.switcher = False - ml_restore_1.renderer = "CYCLES" - - ml_restore_2 = layout.operator("ml.restore", - text='To Cycles Nodes On', - icon="IMGDISPLAY") - ml_restore_2.switcher = True - ml_restore_2.renderer = "CYCLES" - use_separator(self, context) - - layout.operator("material.set_transparent_back_side", - icon='IMAGE_RGB_ALPHA', - text="Transparent back (BI)") - layout.operator("view3d.material_to_texface", - text="Material to Texface", - icon='MATERIAL_DATA') - layout.operator("view3d.texface_to_material", - text="Texface to Material", - icon='TEXTURE_SHADED') - use_separator(self, context) - - layout.operator("material.link_to_base_names", icon="KEYTYPE_BREAKDOWN_VEC") - use_separator(self, context) - layout.operator("texture.patern_rename", - text='Rename Image As Texture', - icon='TEXTURE') - - -# Specials Menu's # - -def menu_func(self, context): - layout = self.layout - layout.operator_context = 'INVOKE_REGION_WIN' - - use_separator(self, context) - if use_mat_menu_type() == 'POPUP': - VIEW3D_MT_assign_material.draw(self, context) - use_separator(self, context) - VIEW3D_MT_select_material.draw(self, context) - else: - layout.menu("VIEW3D_MT_assign_material", icon='ZOOMIN') - layout.menu("VIEW3D_MT_select_material", icon='HAND') - use_separator(self, context) - - layout.operator("view3d.replace_material", - text='Replace Material', - icon='ARROW_LEFTRIGHT') - use_separator(self, context) - - layout.menu("VIEW3D_MT_remove_material", icon="COLORSET_10_VEC") - use_separator(self, context) - - layout.operator("view3d.fake_user_set", - text='Set Fake User', - icon='UNPINNED') - use_separator(self, context) - - layout.menu("VIEW3D_MT_mat_special", icon="SOLO_ON") - - -def menu_move(self, context): - layout = self.layout - layout.operator_context = 'INVOKE_REGION_WIN' - - layout.operator("material.move_material_slot_top", - icon='TRIA_UP', text="Slot to top") - layout.operator("material.move_material_slot_bottom", - icon='TRIA_DOWN', text="Slot to bottom") - use_separator(self, context) - - -# Converters Menu's # - -class MATERIAL_MT_scenemassive_opt(Menu): - bl_idname = "scenemassive.opt" - bl_description = "Additional Options for Convert BI to Cycles" - bl_label = "Options" - bl_options = {'REGISTER'} - - def draw(self, context): - layout = self.layout - scene = context.scene.mat_context_menu - - layout.prop(scene, "EXTRACT_ALPHA", - text="Extract Alpha Textures (slow)") - use_separator(self, context) - layout.prop(scene, "EXTRACT_PTEX", - text="Extract Procedural Textures (slow)") - use_separator(self, context) - layout.prop(scene, "EXTRACT_OW", text="Re-extract Textures") - use_separator(self, context) - layout.prop(scene, "SET_FAKE_USER", text="Set Fake User on unused images") - use_separator(self, context) - layout.prop(scene, "SCULPT_PAINT", text="Sculpt/Texture paint mode") - use_separator(self, context) - layout.prop(scene, "UV_UNWRAP", text="Set Auto UV Unwrap (Active Object)") - use_separator(self, context) - layout.prop(scene, "enable_report", text="Enable Report in the UI") - use_separator(self, context) - - layout.label(text="Set the Bake Resolution") - res = str(scene.img_bake_size) - layout.label(text="Current Setting is : %s" % (res + "x" + res), icon='INFO') - use_separator(self, context) - layout.prop(scene, "img_bake_size", icon='NODE_SEL', expand=True) - - -class MATERIAL_PT_scenemassive(Panel): - bl_label = "Convert BI Materials to Cycles" - bl_space_type = "PROPERTIES" - bl_region_type = "WINDOW" - bl_context = "material" - bl_options = {'DEFAULT_CLOSED'} - - @classmethod - def poll(cls, context): - return (enable_converters() is True and converter_type('BI_CONV')) - - def draw_header(self, context): - layout = self.layout - help_panel_header(layout, menu_type="HELP_MT_biconvert") - - def draw(self, context): - layout = self.layout - sc = context.scene - col = layout.column(align=True) - box = col.box() - - split = box.box().split(0.5) - split.operator("ml.refresh", - text="Convert All to Cycles", icon='MATERIAL') - split.operator("ml.refresh_active", - text="Convert Active to Cycles", icon='MATERIAL') - row = box.row() - ml_restore = row.operator("ml.restore", text="To BI Nodes Off", - icon='MATERIAL') - ml_restore.switcher = False - ml_restore.renderer = "BI" - row.menu("scenemassive.opt", text="Advanced Options", icon='SCRIPTWIN') - - box = col.box() - box.label(text="Save Directory") - split = box.split(0.85) - split.prop(sc.mat_context_menu, "conv_path", text="", icon="RENDER_RESULT") - split.operator("material.check_converter_path", - text="", icon="EXTERNAL_DATA") - - -class MATERIAL_PT_xps_convert(Panel): - bl_label = "Convert to BI and Cycles Nodes" - bl_space_type = "PROPERTIES" - bl_region_type = "WINDOW" - bl_context = "material" - bl_options = {'DEFAULT_CLOSED'} - - @classmethod - def poll(cls, context): - return (enable_converters() is True and converter_type('CYC_CONV')) - - def draw_header(self, context): - layout = self.layout - help_panel_header(layout, menu_type="help.nodeconvert") - - def draw(self, context): - layout = self.layout - col = layout.column(align=True) - box = col.box() - - box.label(text="Multi Image Support (Imports)", icon="INFO") - split = box.box().split(0.5) - split.operator( - "xps_tools.convert_to_cycles_all", - text="Convert All to Nodes", icon="TEXTURE" - ) - split.operator( - "xps_tools.convert_to_cycles_selected", - text="Convert Selected to Nodes", icon="TEXTURE" - ) - - box = col.box() - ml_restore = box.operator("ml.restore", text="To BI Nodes ON", - icon='MATERIAL') - ml_restore.switcher = True - ml_restore.renderer = "BI" - - -# Converters Help # - -class MATERIAL_MT_biconv_help(Menu): - bl_idname = "HELP_MT_biconvert" - bl_description = "Read Instructions & Current Limitations" - bl_label = "Usage Information Guide" - bl_options = {'REGISTER'} - - def draw(self, context): - layout = self.layout - layout.label(text="If possible, avoid multiple conversions in a row") - layout.label(text="Save Your Work Often", icon="ERROR") - use_separator(self, context) - layout.label(text="Try to link them manually using Mix Color nodes") - layout.label(text="Only the last Image in the stack gets linked to Shader") - layout.label(text="Current limitation:", icon="MOD_EXPLODE") - use_separator(self, context) - layout.label(text="Select the texture loaded in the image node") - layout.label(text="Press Ctrl/T to create the image nodes") - layout.label(text="In the Node Editor, Select the Diffuse Node") - layout.label(text="Enable Node Wrangler add-on", icon="NODETREE") - layout.label(text="If Unconnected or No Image Node Error:", icon="MOD_EXPLODE") - use_separator(self, context) - layout.label(text="Extract Alpha: the images have to have alpha channel") - layout.label(text="The default path is the folder where the current .blend is") - layout.label(text="During Baking, the script will check writing privileges") - layout.label(text="Set the save path for extracting images with full access") - layout.label(text="May Require Run As Administrator on Windows OS", icon="ERROR") - layout.label(text="Converts Bi Textures to Image Files:", icon="MOD_EXPLODE") - use_separator(self, context) - layout.label(text="The Converter report can point out to some failures") - layout.label(text="Some material combinations are unsupported") - layout.label(text="Single BI Texture/Image per convert is only supported") - layout.label(text="Converts Basic BI non node materials to Cycles") - use_separator(self, context) - layout.label(text="Convert Bi Materials to Cycles Nodes:", icon="INFO") - - -class MATERIAL_MT_nodeconv_help(Menu): - bl_idname = "help.nodeconvert" - bl_description = "Read Instructions & Current Limitations" - bl_label = "Usage Information Guide" - bl_options = {'REGISTER'} - - def draw(self, context): - layout = self.layout - layout.label(text="If possible, avoid multiple conversions in a row") - layout.label(text="Save Your Work Often", icon="ERROR") - use_separator(self, context) - layout.label(text="Relinking and removing some not needed nodes") - layout.label(text="The result Node tree will need some cleaning up") - use_separator(self, context) - layout.label(text="Select the texture loaded in the image node") - layout.label(text="Press Ctrl/T to create the image nodes") - layout.label(text="In the Node Editor, Select the Diffuse Node") - layout.label(text="Enable Node Wrangler add-on", icon="NODETREE") - layout.label(text="If Unconnected or No Image Node Error:", icon="MOD_EXPLODE") - use_separator(self, context) - layout.label(text="For Specular Nodes, Image color influence has to be enabled") - layout.label(text="Generated images (i.e. Noise and others) are not converted") - layout.label(text="The Converter report can point out to some failures") - layout.label(text="Not all Files will produce good results", icon="ERROR") - layout.label(text="fbx, .dae, .obj, .3ds, .xna and more") - layout.label(text="*Supports Imported Files*:", icon="IMPORT") - use_separator(self, context) - layout.label(text="For some file types") - layout.label(text="Supports Alpha, Normals, Specular and Diffuse") - layout.label(text="Then Converts BI Nodes to Cycles Nodes") - layout.label(text="Converts BI non node materials to BI Nodes") - use_separator(self, context) - layout.label(text="Convert Materials/Image Textures from Imports:", icon="INFO") - - -# Make Report -class MATERIAL_OT_converter_report(Operator): - bl_idname = "mat_converter.reports" - bl_label = "Material Converter Report" - bl_description = "Report about done Material Conversions" - bl_options = {'REGISTER', 'INTERNAL'} - - message: StringProperty(maxlen=8192) - - def draw(self, context): - layout = self.layout - layout.label(text="Information:", icon='INFO') - - if self.message and type(self.message) is str: - list_string = self.message.split("*") - for line in range(len(list_string)): - layout.label(text=str(list_string[line])) - - def invoke(self, context, event): - return context.window_manager.invoke_props_dialog(self, width=500) - - def execute(self, context): - - return {'FINISHED'} - - -# Scene Properties -class material_specials_scene_mats(PropertyGroup): - name: StringProperty() - mat_lib: BoolProperty( - default=False - ) - mat_fake_user: BoolProperty( - default=False - ) - - -class material_specials_scene_props(PropertyGroup): - conv_path: StringProperty( - name="Save Directory", - description="Path to save images during conversion\n" - "Default is the location of the blend file", - default="//", - subtype='DIR_PATH' - ) - EXTRACT_ALPHA: BoolProperty( - attr="EXTRACT_ALPHA", - default=False, - description="Extract Alpha channel from non-procedural images\n" - "Don't use this option if the image doesn't have Alpha" - ) - SET_FAKE_USER: BoolProperty( - attr="SET_FAKE_USER", - default=False, - description="Set fake user on unused images, so they can be kept in the .blend" - ) - EXTRACT_PTEX: BoolProperty( - attr="EXTRACT_PTEX", - default=False, - description="Extract procedural images and bake them to jpeg" - ) - EXTRACT_OW: BoolProperty( - attr="Overwrite", - default=False, - description="Extract textures again instead of re-using previously extracted textures" - ) - SCULPT_PAINT: BoolProperty( - attr="SCULPT_PAINT", - default=False, - description="Conversion geared towards sculpting and painting.\n" - "Creates a diffuse, glossy mixed with layer weight.\n" - "Image nodes are not connected" - ) - UV_UNWRAP: BoolProperty( - attr="UV_UNWRAP", - default=False, - description="Use automatic Angle based UV Unwrap for the Active Object" - ) - enable_report: BoolProperty( - attr="enable_report", - default=False, - description="Enable Converter Report in the UI" - ) - img_bake_size: EnumProperty( - name="Bake Image Size", - description="Set the resolution size of baked images \n", - items=( - ('512', "Set : 512 x 512", "Bake Resolution 512 x 512"), - ('1024', "Set : 1024 x 1024", "Bake Resolution 1024 x 1024"), - ('2048', "Set : 2048 x 2048", "Bake Resolution 2048 x 2048") - ), - default='1024' - ) - set_material_name: StringProperty( - name="New Material name", - description="What Base name pattern to use for a new created Material\n" - "It is appended by an automatic numeric pattern depending\n" - "on the number of Scene's materials containing the Base", - default="Material_New", - maxlen=128 - ) - use_tweak: BoolProperty( - name="Tweak Settings", - description="Open Preview Active Material after new Material creation", - default=False - ) - index_mat: IntProperty( - name="index", - options={"HIDDEN"} - ) - - -# Add-on Preferences -class VIEW3D_MT_material_utils_pref(AddonPreferences): - bl_idname = __name__ - - show_warnings: BoolProperty( - name="Enable Warning messages", - default=False, - description="Show warning messages when an action is executed or failed" - ) - show_remove_mat: BoolProperty( - name="Enable Remove all Materials", - default=False, - description="Enable Remove all Materials for all Selected Objects\n\n" - "Use with care - if you want to keep materials after\n" - "closing or reloading Blender, Set Fake User for them" - ) - show_mat_preview: BoolProperty( - name="Enable Material Preview", - default=True, - description="Material Preview of the Active Object\n" - "Contains the preview of the active Material,\n" - "Use nodes, Color, Specular and Transparency\n" - "settings depending on the Context and Preferences" - ) - set_cleanmatslots: BoolProperty( - name="Enable Auto Clean", - default=True, - description="Enable Automatic Removal of unused Material Slots\n" - "called together with the Assign Material menu option.\n" - "Apart from preference and the cases when it affects\n" - "adding materials, enabling it can have some\n" - "performance impact on very dense meshes" - ) - show_separators: BoolProperty( - name="Use Separators in the menus", - default=True, - description="Use separators in the menus, a trade-off between\n" - "readability vs. using more space for displaying items" - ) - show_converters: BoolProperty( - name="Enable Converters", - default=False, - description="Enable Material Converters" - ) - set_preview_size: EnumProperty( - name="Preview Menu Size", - description="Set the preview menu size\n" - "depending on the number of materials\n" - "in the scene (width and height)", - items=( - ('2x2', "Size 2x2", "Width 2 Height 2"), - ('2x3', "Size 2x3", "Width 3 Height 2"), - ('3x3', "Size 3x3", "Width 3 Height 3"), - ('3x4', "Size 3x4", "Width 4 Height 3"), - ('4x4', "Size 4x4", "Width 4 Height 4"), - ('5x5', "Size 5x5", "Width 5 Height 5"), - ('6x6', "Size 6x6", "Width 6 Height 6"), - ('0x0', "List", "Display as a List") - ), - default='3x3' - ) - set_preview_type: EnumProperty( - name="Preview Menu Type", - description="Set the the Preview menu type", - items=( - ('LIST', "Classic", - "Display as a Classic List like in Blender Properties.\n" - "Preview of Active Material is not available\n\n" - "Note: Choosing a different material from the list will replace the active one"), - ('PREVIEW', "Preview Display", - "Display as a preview of Thumbnails\n" - "It can have some performance issues with scenes containing a lot of materials\n" - "Preview of Active Material is available\n\n" - "Note: Choosing a different material from the list will replace the active one") - ), - default='PREVIEW' - ) - set_experimental_type: EnumProperty( - name="Experimental Features", - description="Set the type of converters enabled", - items=( - ('ALL', "All Converters", - "Enable all Converters"), - ('CYC_CONV', "BI and Cycles Nodes", - "Enable Cycles related Convert"), - ('BI_CONV', "BI To Cycles", - "Enable Blender Internal related Converters") - ), - default='ALL', - ) - set_add_material_menu: EnumProperty( - name="Add Material Menu", - description="Set the type of Add Material menu", - items=( - ('STANDARD', "Standard Menu", - "Material entries in the menu are below each other"), - ('COLUMNS', "Column Menu", - "Material entries are placed in column blocks"), - ('POPUP', "Pop up Menu", - "Material entries are placed in a scrollable list inside a pop-up menu") - ), - default='POPUP' - ) - - def draw(self, context): - layout = self.layout - sc = context.scene - - col_m = layout.column(align=True) - - box = col_m.box() - box.label(text="Save Directory") - split = box.split(factor=0.85) - split.prop(sc.mat_context_menu, "conv_path", text="", icon="RENDER_RESULT") - split.operator( - "material.check_converter_path", - text="", icon="EXTERNAL_DATA" - ) - box = col_m.box() - split = box.split(align=True) - - col = split.column(align=True) - col.prop(self, "show_warnings") - col.prop(self, "show_remove_mat") - col.prop(self, "set_cleanmatslots") - col.prop(self, "show_separators") - - col = split.column(align=True) - col.label(text="Apply / Select Material mode:") - col.prop(self, "set_add_material_menu", expand=True) - - box = col_m.box() - size_split = 0.3 if self.show_mat_preview else 1.0 - split = box.split(factor=size_split, align=True) - split.prop(self, "show_mat_preview") - - if self.show_mat_preview: - subsplit = split.split(factor=0.7, align=True) - row = subsplit.row(align=True) - row.prop(self, "set_preview_type", expand=True) - - subrow = subsplit.row(align=True) - subrow.enabled = True if self.set_preview_type in {'PREVIEW'} else False - subrow.prop(self, "set_preview_size", text="") - - box = col_m.box() - size_split = 0.3 if self.show_converters else 1.0 - split = box.split(factor=size_split, align=True) - split.prop(self, "show_converters") - - if self.show_converters: - row = split.row(align=True) - row.prop(self, "set_experimental_type", expand=True) - - -# utility functions: - -def help_panel_header(layout, menu_type="VIEW3D_MT_master_material"): - layout.separator() - box = layout.box() - box.scale_y = 0.5 - box.menu(menu_type, text="", icon="INFO") - layout.separator() - - -def activate_mat_slot(actob, matname): - mats = actob.material_slots - for i, m in enumerate(mats): - if m.name == matname: - # make slot active - actob.active_material_index = i - break - - -def materials_lists_fill_names(context, refresh=False, is_object=False): - mats_list = context.scene.mat_specials_mats - if refresh: - for key in mats_list.keys(): - index = mats_list.find(key) - if index != -1: - mats_list.remove(index) - - obj = context.active_object - mat_collection = [] - if (is_object and obj): - mat_collection = [ - slot.material for slot in obj.material_slots if - slot.material - ] - else: - mat_collection = bpy.data.materials - - for mat in mat_collection: - if mat.name not in mats_list.keys() or refresh: - prop = mats_list.add() - prop.name = mat.name - prop.mat_lib = bool(mat.library) - prop.mat_fake_user = mat.use_fake_user - - -def switch_to_column(is_edit=False): - obj = bpy.context.active_object - collect = obj.material_slots if is_edit else bpy.data.materials - col_size = int(round(len(collect) / COLUMN_SPLIT)) - - return col_size if col_size > 0 else 1 - - -def remove_material_slot(): - if bpy.ops.object.material_slot_remove.poll(): - bpy.ops.object.material_slot_remove() - - -def check_mat_name_unique(name_id="Material_new"): - # check if the new name pattern is in materials' data - name_list = [] - suffix = 1 - try: - if c_data_has_materials(): - name_list = [mat.name for mat in bpy.data.materials if name_id in mat.name] - new_name = "{}_{}".format(name_id, len(name_list) + suffix) - if new_name in name_list: - # KISS failed - numbering is not sequential - # try harvesting numbers in material names, find the rightmost ones - test_num = [] - from re import findall - for words in name_list: - test_num.append(findall("\d+", words)) - - suffix += max([int(l[-1]) for l in test_num]) - new_name = "{}_{}".format(name_id, suffix) - return new_name - except Exception as e: - print("\n[Materials Utils Specials]\nfunction: check_mat_name_unique\nError: %s \n" % e) - pass - return name_id - - -def included_object_types(objects): - # Pass the bpy.data.objects.type to avoid needless assigning/removing - # included - type that can have materials - included = ['MESH', 'CURVE', 'SURFACE', 'FONT', 'META', 'GPENCIL'] - obj = objects - return bool(obj and obj in included) - - -def check_is_excluded_obj_types(contxt): - # pass the context to check if selected objects have excluded types - if contxt and contxt.selected_editable_objects: - for obj in contxt.selected_editable_objects: - if not included_object_types(obj.type): - return True - return False - - -def check_texface_to_mat(obj): - # check for UV data presence - if obj: - if hasattr(obj.data, "uv_textures"): - if hasattr(obj.data.uv_textures, "active"): - if hasattr(obj.data.uv_textures.active, "data"): - return True - return False - - -def c_context_mat_preview(): - # returns the type of viewport shading - # needed for using the optional UI elements (the context gets lost) - - # code from BA user SynaGl0w - # if there are multiple 3d views return the biggest screen area one - views_3d = [area for area in bpy.context.screen.areas if - area.type == 'VIEW_3D' and area.spaces.active] - - if views_3d: - main_view_3d = max(views_3d, key=lambda area: area.width * area.height) - return main_view_3d.spaces.active.viewport_shade - return "NONE" - - -def c_context_use_nodes(): - # checks if Use Nodes is ticked on - actob = bpy.context.active_object - u_node = (actob.active_material.use_nodes if - hasattr(actob, "active_material") else False) - - return bool(u_node) - - -def c_render_engine(cyc=None): - # valid cyc inputs "Cycles", "BI", "Both", "Lux" - scene = bpy.context.scene - render_engine = scene.render.engine - - r_engines = {"Cycles": 'CYCLES', - "BI": 'BLENDER_RENDER', - "Both": ('CYCLES', 'BLENDER_RENDER'), - "Lux": 'LUXRENDER_RENDER'} - if cyc: - return (True if cyc in r_engines and render_engine in r_engines[cyc] else False) - return render_engine - - -def c_need_of_viewport_colors(): - # check the context where using Viewport color and friends are needed - # Cycles and BI are supported - if c_render_engine("Cycles"): - if c_context_use_nodes() and c_context_mat_preview() == 'SOLID': - return True - elif c_context_mat_preview() in ('SOLID', 'TEXTURED', 'MATERIAL'): - return True - elif (c_render_engine("BI") and not c_context_use_nodes()): - return True - - return False - - -# Draw Separator -def use_separator(operator, context): - # pass the preferences show_separators bool to enable/disable them - pref = return_preferences() - useSep = pref.show_separators - if useSep: - operator.layout.separator() - - -# preferences utilities - -def return_preferences(): - return bpy.context.preferences.addons[__name__].preferences - - -def use_remove_mat_all(): - pref = return_preferences() - show_rmv_mat = pref.show_remove_mat - - return bool(show_rmv_mat) - - -def use_mat_menu_type(): - pref = return_preferences() - use_menu_mat = pref.set_add_material_menu - - return use_menu_mat - - -def use_mat_preview(): - pref = return_preferences() - show_mat_prw = pref.show_mat_preview - - return bool(show_mat_prw) - - -def use_cleanmat_slots(): - pref = return_preferences() - use_mat_clean = pref.set_cleanmatslots - - return bool(use_mat_clean) - - -def size_preview(): - pref = return_preferences() - set_size_prw = pref.set_preview_size - - cell_w = int(set_size_prw[0]) - cell_h = int(set_size_prw[-1]) - cell_tbl = {'Width': cell_w, 'Height': cell_h} - - return cell_tbl - - -def size_type_is_preview(): - pref = return_preferences() - set_prw_type = pref.set_preview_type - - return bool(set_prw_type in {'PREVIEW'}) - - -def enable_converters(): - pref = return_preferences() - shw_conv = pref.show_converters - - return shw_conv - - -def converter_type(types='ALL'): - # checks the type of the preferences 'ALL', 'CYC_CONV', 'BI_CONV' - pref = return_preferences() - set_exp_type = pref.set_experimental_type - - return bool(set_exp_type in {'ALL'} or types == set_exp_type) - -# ----------------------------------------------------- -# Registration -# ------------------------------------------------------ -classes = ( - VIEW3D_OT_show_mat_preview, - VIEW3D_OT_copy_material_to_selected, - VIEW3D_OT_texface_to_material, - VIEW3D_OT_set_new_material_name, - VIEW3D_OT_assign_material, - VIEW3D_OT_clean_material_slots, - VIEW3D_OT_material_to_texface, - VIEW3D_OT_material_remove_slot, - VIEW3D_OT_material_remove_object, - VIEW3D_OT_material_remove_all, - VIEW3D_OT_select_material_by_name, - VIEW3D_OT_replace_material, - VIEW3D_OT_fake_user_set, - MATERIAL_OT_set_transparent_back_side, - MATERIAL_OT_move_slot_top, - MATERIAL_OT_move_slot_bottom, - MATERIAL_OT_link_to_base_names, - MATERIAL_OT_check_converter_path, - VIEW3D_UL_assign_material_popup_ui, - VIEW3D_MT_assign_material, - VIEW3D_MT_select_material, - VIEW3D_MT_remove_material, - VIEW3D_MT_master_material, - VIEW3D_MT_mat_special, - MATERIAL_MT_scenemassive_opt, - MATERIAL_PT_scenemassive, - MATERIAL_PT_xps_convert, - MATERIAL_MT_biconv_help, - MATERIAL_MT_nodeconv_help, - MATERIAL_OT_converter_report, - material_specials_scene_mats, - material_specials_scene_props, - VIEW3D_MT_material_utils_pref -) - -def register(): - for cls in classes: - bpy.utils.register_class(cls) - - warning_messages_utils.MAT_SPEC_NAME = __name__ - - # Register Scene Properties - bpy.types.Scene.mat_context_menu= PointerProperty( - type=material_specials_scene_props - ) - bpy.types.Scene.mat_specials_mats= CollectionProperty( - name="Material name", - type=material_specials_scene_mats - ) - - kc = bpy.context.window_manager.keyconfigs.addon - if kc: - km = kc.keymaps.new(name="3D View", space_type="VIEW_3D") - kmi = km.keymap_items.new('wm.call_menu', 'Q', 'PRESS', shift=True) - kmi.properties.name = "VIEW3D_MT_master_material" - - bpy.types.MATERIAL_MT_context_menu.prepend(menu_move) - bpy.types.MATERIAL_MT_context_menu.append(menu_func) - - -def unregister(): - kc = bpy.context.window_manager.keyconfigs.addon - if kc: - km = kc.keymaps["3D View"] - for kmi in km.keymap_items: - if kmi.idname == 'wm.call_menu': - if kmi.properties.name == "VIEW3D_MT_master_material": - km.keymap_items.remove(kmi) - break - - bpy.types.MATERIAL_MT_context_menu.remove(menu_move) - bpy.types.MATERIAL_MT_context_menu.remove(menu_func) - - del bpy.types.Scene.mat_context_menu - del bpy.types.Scene.mat_specials_mats - - for cls in classes: - bpy.utils.unregister_class(cls) - - -if __name__ == "__main__": - register() |