Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender-addons.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormeta-androcto <meta.androcto1@gmail.com>2019-05-24 08:58:45 +0300
committermeta-androcto <meta.androcto1@gmail.com>2019-05-24 08:58:45 +0300
commit01d80b8f602f392d975255d5fef97dcab2e0589b (patch)
tree292a321f33192967dc117d21b5c44907af69f2c8 /materials_utils
parent4fbdba4b2d86cd290b90c83124154dfd5c97504d (diff)
materials_utils: move to contrib: T63750
Diffstat (limited to 'materials_utils')
-rw-r--r--materials_utils/__init__.py2741
-rw-r--r--materials_utils/material_converter.py793
-rw-r--r--materials_utils/materials_cycles_converter.py977
-rw-r--r--materials_utils/texture_rename.py127
-rw-r--r--materials_utils/warning_messages_utils.py181
5 files changed, 0 insertions, 4819 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()
diff --git a/materials_utils/material_converter.py b/materials_utils/material_converter.py
deleted file mode 100644
index 3a208e80..00000000
--- a/materials_utils/material_converter.py
+++ /dev/null
@@ -1,793 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import bpy
-import math
-from mathutils import Vector
-from bpy.types import Operator
-from .warning_messages_utils import (
- warning_messages,
- c_is_cycles_addon_enabled,
- c_data_has_materials,
- collect_report,
-)
-
-# -----------------------------------------------------------------------------
-# Globals
-
-nodesDictionary = None
-
-NODE_FRAME = 'NodeFrame'
-BI_MATERIAL_NODE = 'ShaderNodeMaterial'
-BI_OUTPUT_NODE = 'ShaderNodeOutput'
-TEXTURE_IMAGE_NODE = 'ShaderNodeTexImage'
-OUTPUT_NODE = 'ShaderNodeOutputMaterial'
-RGB_MIX_NODE = 'ShaderNodeMixRGB'
-MAPPING_NODE = 'ShaderNodeMapping'
-NORMAL_MAP_NODE = 'ShaderNodeNormalMap'
-SHADER_MIX_NODE = 'ShaderNodeMixShader'
-SHADER_ADD_NODE = 'ShaderNodeAddShader'
-COORD_NODE = 'ShaderNodeTexCoord'
-RGB_TO_BW_NODE = 'ShaderNodeRGBToBW'
-BSDF_DIFFUSE_NODE = 'ShaderNodeBsdfDiffuse'
-BSDF_EMISSION_NODE = 'ShaderNodeEmission'
-BSDF_TRANSPARENT_NODE = 'ShaderNodeBsdfTransparent'
-BSDF_GLOSSY_NODE = 'ShaderNodeBsdfGlossy'
-BSDF_GLASS_NODE = 'ShaderNodeBsdfGlass'
-
-textureNodeSizeX = 150
-textureNodeSizeY = 350
-
-
-# -----------------------------------------------------------------------------
-# Functions
-
-def makeTextureNodeDict(cmat):
- global nodesDictionary
- nodesDictionary = {}
- textures = {textureSlot.texture for textureSlot in cmat.texture_slots if textureSlot}
-
- for tex in textures:
- texNode = None
- if tex.type == 'IMAGE':
- texNode = makeNodeUsingImage1(cmat, tex)
- if texNode:
- nodesDictionary[tex] = texNode
- return nodesDictionary
-
-
-def getTexNodeDic(texture):
- return nodesDictionary.get(texture)
-
-
-def clearNodes(TreeNodes):
- TreeNodes.nodes.clear()
-
-
-def clearCycleMaterial(cmat):
- TreeNodes = cmat.node_tree
- clearNodes(TreeNodes)
-
-
-def copyMapping(textureSlot, textureMapping):
- textureMapping.scale.x = textureSlot.scale.x
- textureMapping.scale.y = textureSlot.scale.y
- textureMapping.scale.z = textureSlot.scale.z
-
-
-def addRGBMixNode(TreeNodes, textureSlot, mixRgbNode, prevTexNode, newTexNode, nodeType, textureIdx):
- try:
- links = TreeNodes.links
- mixRgbNode.name = '{} Mix {:d}'.format(nodeType, textureIdx)
- mixRgbNode.blend_type = textureSlot.blend_type
- mixRgbNode.inputs['Fac'].default_value = textureSlot.diffuse_color_factor
- links.new(prevTexNode.outputs['Color'], mixRgbNode.inputs['Color2'])
- links.new(newTexNode.outputs['Color'], mixRgbNode.inputs['Color1'])
- except:
- collect_report("ERROR: Failure to find link with a Mix node")
-
-
-def makeBiNodes(cmat):
- # Create Blender Internal Material Nodes
- TreeNodes = cmat.node_tree
- links = TreeNodes.links
-
- BIFrame = TreeNodes.nodes.new(NODE_FRAME)
- BIFrame.name = 'BI Frame'
- BIFrame.label = 'BI Material'
-
- biShaderNodeMaterial = TreeNodes.nodes.new(BI_MATERIAL_NODE)
- biShaderNodeMaterial.parent = BIFrame
- biShaderNodeMaterial.name = 'BI Material'
- biShaderNodeMaterial.material = cmat
- biShaderNodeMaterial.location = 0, 600
-
- biShaderNodeOutput = TreeNodes.nodes.new(BI_OUTPUT_NODE)
- biShaderNodeOutput.parent = BIFrame
- biShaderNodeOutput.name = 'BI Output'
- biShaderNodeOutput.location = 200, 600
- try:
- links.new(biShaderNodeMaterial.outputs['Color'], biShaderNodeOutput.inputs['Color'])
- links.new(biShaderNodeMaterial.outputs['Alpha'], biShaderNodeOutput.inputs['Alpha'])
- except:
- collect_report("ERROR: Failure to find links with the BI Shader Material")
-
-
-def placeNode(node, posX, posY, deltaX, deltaY, countX, countY):
- nodeX = posX - (deltaX * countX)
- nodeY = posY - (deltaY * countY)
- node.location = nodeX, nodeY
-
-
-def makeImageTextureNode(TreeNodes, img):
- texNode = TreeNodes.nodes.new(TEXTURE_IMAGE_NODE)
- texNode.image = img
- return texNode
-
-
-def makeNodeUsingImage1(cmat, texture):
- TreeNodes = cmat.node_tree
- img = texture.image
- texNode = makeImageTextureNode(TreeNodes, img)
- return texNode
-
-
-def makeMainShader(TreeNodes):
- mainShader = TreeNodes.nodes.new(BSDF_DIFFUSE_NODE)
- mainShader.name = 'Diffuse BSDF'
- mainShader.location = 0, 0
- return mainShader
-
-
-def makeEmissionShader(TreeNodes):
- mainShader = TreeNodes.nodes.new(BSDF_EMISSION_NODE)
- mainShader.name = 'Emmission'
- mainShader.location = 0, 0
- return mainShader
-
-
-def makeMaterialOutput(TreeNodes):
- shout = TreeNodes.nodes.new(OUTPUT_NODE)
- shout.location = 200, 0
- return shout
-
-
-def replaceNode(oldNode, newNode):
- newNode.location = oldNode.location
- try:
- for link in oldNode.outputs['BSDF'].links:
- link.new(newNode.outputs['BSDF'], link.to_socket)
- for link in oldNode.inputs['Color'].links:
- link.new(newNode.inputs['Color'], link.from_socket)
- for link in oldNode.inputs['Normal'].links:
- link.new(newNode.inputs['Normal'], link.from_socket)
- except:
- collect_report("ERROR: Failure to replace node")
-
-
-def BIToCycleTexCoord(links, textureSlot, texCoordNode, textureMappingNode):
- # Texture Coordinates
- linkOutput = None
- if textureSlot.texture_coords in {'TANGENT', 'STRESS', 'STRAND'}:
- linkOutput = None
- elif textureSlot.texture_coords == 'REFLECTION':
- linkOutput = 'Reflection'
- elif textureSlot.texture_coords == 'NORMAL':
- linkOutput = 'Normal'
- elif textureSlot.texture_coords == 'WINDOW':
- linkOutput = 'Window'
- elif textureSlot.texture_coords == 'UV':
- linkOutput = 'UV'
- elif textureSlot.texture_coords == 'ORCO':
- linkOutput = 'Generated'
- elif textureSlot.texture_coords == 'OBJECT':
- linkOutput = 'Object'
- elif textureSlot.texture_coords == 'GLOBAL':
- linkOutput = 'Camera'
-
- if linkOutput:
- links.new(texCoordNode.outputs[linkOutput], textureMappingNode.inputs['Vector'])
-
-
-def createDiffuseNodes(cmat, texCoordNode, mainShader, materialOutput):
- TreeNodes = cmat.node_tree
- links = TreeNodes.links
- texCount = len([node for node in TreeNodes.nodes if node.type == 'MAPPING'])
- currPosY = -textureNodeSizeY * texCount
-
- textureSlots = [textureSlot for textureSlot in cmat.texture_slots if
- (textureSlot and textureSlot.use_map_color_diffuse)]
-
- texCount = len(textureSlots)
- texNode = None
- latestNode = None
- groupName = 'Diffuse'
-
- if any(textureSlots):
- diffuseFrame = TreeNodes.nodes.new(NODE_FRAME)
- diffuseFrame.name = '{} Frame'.format(groupName)
- diffuseFrame.label = '{}'.format(groupName)
-
- for textureIdx, textureSlot in enumerate(textureSlots):
- texNode = getTexNodeDic(textureSlot.texture)
- if texNode:
- tex_node_name = getattr(texNode.image, "name", "")
- collect_report("INFO: Generating {} Nodes for: ".format(groupName) + tex_node_name)
- texNode.parent = diffuseFrame
- placeNode(texNode, -500 - ((texCount - 1) * 200),
- currPosY, textureNodeSizeX, textureNodeSizeY, 0, textureIdx)
-
- # Add mapping node
- textureMapping = TreeNodes.nodes.new(MAPPING_NODE)
- textureMapping.parent = diffuseFrame
- renameNode(textureMapping, '{} Mapping'.format(groupName), texCount, textureIdx)
- textureMapping.location = texNode.location + Vector((-400, 0))
- copyMapping(textureSlot, textureMapping)
-
- # Texture Coordinates
- BIToCycleTexCoord(links, textureSlot, texCoordNode, textureMapping)
-
- # Place the texture node
- renameNode(texNode, '{} Texture'.format(groupName), texCount, textureIdx)
- links.new(textureMapping.outputs['Vector'], texNode.inputs['Vector'])
-
- # Add multiply node
- colorMult = TreeNodes.nodes.new(RGB_MIX_NODE)
- colorMult.parent = diffuseFrame
- renameNode(colorMult, 'Color Mult', texCount, textureIdx)
- colorMult.blend_type = 'MIX'
- colorMult.inputs['Fac'].default_value = 1
- colorMult.inputs['Color1'].default_value = (1, 1, 1, 1)
-
- colorMult.location = texNode.location + Vector((200, 0))
- links.new(texNode.outputs['Color'], colorMult.inputs['Color2'])
-
- texNode = colorMult
- if textureSlot.use and textureIdx == 0:
- latestNode = texNode
-
- if textureSlot.use and textureIdx > 0:
- try:
- # Create a node to mix multiple texture nodes
- mixRgbNode = TreeNodes.nodes.new(RGB_MIX_NODE)
- mixRgbNode.parent = diffuseFrame
- addRGBMixNode(TreeNodes, textureSlot, mixRgbNode, texNode, latestNode,
- '{}'.format(groupName), textureIdx)
- mixRgbNode.location = Vector(
- (max(texNode.location.x, latestNode.location.x),
- (texNode.location.y + latestNode.location.y) / 2)) + Vector((200, 0)
- )
- latestNode = mixRgbNode
- except:
- continue
-
- if latestNode:
- links.new(latestNode.outputs['Color'], mainShader.inputs['Color'])
-
- # Y Position next texture node
- currPosY = currPosY - (textureNodeSizeY * (texCount))
-
- # BI Material to Cycles - Alpha Transparency
- textureSlots = [textureSlot for textureSlot in cmat.texture_slots if
- (textureSlot and textureSlot.use_map_alpha)]
- texCount = len(textureSlots)
- texNode = None
- latestNode = None
- for textureIdx, textureSlot in enumerate(textureSlots):
- texNode = getTexNodeDic(textureSlot.texture)
- if texNode:
- tex_node_name = getattr(texNode.image, "name", "")
- collect_report("INFO: Generating Transparency Nodes for: " + tex_node_name)
- if textureSlot.use and textureIdx == 0:
- latestNode = texNode
-
- if textureSlot.use and textureIdx > 0:
- try:
- # Create a node to mix multiple texture nodes
- mixAlphaNode = TreeNodes.nodes.new(RGB_MIX_NODE)
- mixAlphaNode.name = 'Alpha Mix {:d}'.format(textureIdx)
- mixAlphaNode.blend_type = textureSlot.blend_type
- mixAlphaNode.inputs['Fac'].default_value = textureSlot.diffuse_color_factor
- placeNode(mixAlphaNode, -200 - ((texCount - textureIdx - 1) * 200), 400 - 240,
- textureNodeSizeX, textureNodeSizeY, 0, 0)
- links.new(texNode.outputs['Alpha'], mixAlphaNode.inputs['Color2'])
- links.new(latestNode.outputs['Alpha'], mixAlphaNode.inputs['Color1'])
- latestNode = mixAlphaNode
- except:
- continue
- if latestNode:
- alphaMixShader = TreeNodes.nodes.get('Alpha Mix Shader')
- if alphaMixShader:
- if latestNode.type == 'TEX_IMAGE':
- outputLink = 'Alpha'
- else:
- outputLink = 'Color'
- links.new(latestNode.outputs[outputLink], alphaMixShader.inputs['Fac'])
-
-
-def createNormalNodes(cmat, texCoordNode, mainShader, materialOutput):
- TreeNodes = cmat.node_tree
- links = TreeNodes.links
- texCount = len([node for node in TreeNodes.nodes if node.type == 'MAPPING'])
- currPosY = -textureNodeSizeY * texCount
-
- textureSlots = [textureSlot for textureSlot in cmat.texture_slots if
- (textureSlot and textureSlot.use_map_normal)]
- texCount = len(textureSlots)
- texNode = None
- latestNode = None
- groupName = 'Normal'
- if any(textureSlots):
- normalFrame = TreeNodes.nodes.new(NODE_FRAME)
- normalFrame.name = '{} Frame'.format(groupName)
- normalFrame.label = '{}'.format(groupName)
-
- for textureIdx, textureSlot in enumerate(textureSlots):
- texNode = getTexNodeDic(textureSlot.texture)
- if texNode:
- tex_node_name = getattr(texNode.image, "name", "")
- collect_report("INFO: Generating Normal Nodes for: " + tex_node_name)
- texNode.parent = normalFrame
- placeNode(texNode, -500 - ((texCount) * 200), currPosY,
- textureNodeSizeX, textureNodeSizeY, 0, textureIdx)
-
- # Add mapping node
- normalMapping = TreeNodes.nodes.new(MAPPING_NODE)
- normalMapping.parent = normalFrame
- renameNode(normalMapping, '{} Mapping'.format(groupName), texCount, textureIdx)
- normalMapping.location = texNode.location + Vector((-400, 0))
- copyMapping(textureSlot, normalMapping)
-
- # Texture Coordinates
- BIToCycleTexCoord(links, textureSlot, texCoordNode, normalMapping)
-
- # Place the texture node
- renameNode(texNode, '{} Texture'.format(groupName), texCount, textureIdx)
- if texNode.image.image:
- texNode.image.colorspace_settings.is_data = True
- links.new(normalMapping.outputs['Vector'], texNode.inputs['Vector'])
-
- # Add multiply node
- normalMult = TreeNodes.nodes.new(RGB_MIX_NODE)
- normalMult.parent = normalFrame
- renameNode(normalMult, 'Normal Mult', texCount, textureIdx)
- normalMult.blend_type = 'MIX'
- normalMult.inputs['Fac'].default_value = 1
- normalMult.inputs['Color1'].default_value = (.5, .5, 1, 1)
-
- normalMult.location = texNode.location + Vector((200, 0))
- links.new(texNode.outputs['Color'], normalMult.inputs['Color2'])
-
- texNode = normalMult
- if textureSlot.use and textureIdx == 0:
- latestNode = texNode
-
- if textureSlot.use and textureIdx > 0:
- try:
- # Create a node to mix multiple texture nodes
- mixRgbNode = TreeNodes.nodes.new(RGB_MIX_NODE)
- mixRgbNode.parent = normalFrame
- addRGBMixNode(TreeNodes, textureSlot, mixRgbNode, texNode, latestNode,
- '{}'.format(groupName), textureIdx)
- mixRgbNode.location = Vector(
- (max(texNode.location.x, latestNode.location.x),
- (texNode.location.y + latestNode.location.y) / 2)) + Vector((200, 0)
- )
- latestNode = mixRgbNode
- except:
- continue
-
- if latestNode:
- normalMapNode = TreeNodes.nodes.new(NORMAL_MAP_NODE)
- normalMapNode.parent = normalFrame
- normalMapNode.location = latestNode.location + Vector((200, 0))
- links.new(latestNode.outputs['Color'], normalMapNode.inputs['Color'])
- links.new(normalMapNode.outputs['Normal'], mainShader.inputs['Normal'])
-
-
-def createSpecularNodes(cmat, texCoordNode, mainShader, mainDiffuse, materialOutput):
- TreeNodes = cmat.node_tree
- links = TreeNodes.links
- texCount = len([node for node in TreeNodes.nodes if node.type == 'MAPPING'])
- currPosY = -textureNodeSizeY * texCount
-
- textureSlots = [textureSlot for textureSlot in cmat.texture_slots if
- (textureSlot and textureSlot.use_map_color_spec)]
- texCount = len(textureSlots)
- texNode = None
- latestNode = None
- groupName = 'Specular'
- if any(textureSlots):
- specularFrame = TreeNodes.nodes.new(NODE_FRAME)
- specularFrame.name = '{} Frame'.format(groupName)
- specularFrame.label = '{}'.format(groupName)
-
- for textureIdx, textureSlot in enumerate(textureSlots):
- texNode = getTexNodeDic(textureSlot.texture)
- if texNode:
- tex_node_name = getattr(texNode.image, "name", "")
- collect_report("INFO: Generating {} Nodes for: ".format(groupName) + tex_node_name)
- texNode.parent = specularFrame
- placeNode(texNode, -500 - ((texCount) * 200),
- currPosY, textureNodeSizeX, textureNodeSizeY, 0, textureIdx)
-
- # Add mapping node
- specularMapping = TreeNodes.nodes.new(MAPPING_NODE)
- specularMapping.parent = specularFrame
- renameNode(specularMapping, '{} Mapping'.format(groupName), texCount, textureIdx)
- specularMapping.location = texNode.location + Vector((-400, 0))
- copyMapping(textureSlot, specularMapping)
-
- # Texture Coordinates
- BIToCycleTexCoord(links, textureSlot, texCoordNode, specularMapping)
-
- # Place the texture node
- renameNode(texNode, '{} Texture'.format(groupName), texCount, textureIdx)
- links.new(specularMapping.outputs['Vector'], texNode.inputs['Vector'])
-
- # Add multiply node
- specularMult = TreeNodes.nodes.new(RGB_MIX_NODE)
- specularMult.parent = specularFrame
- renameNode(specularMult, 'Specular Mult', texCount, textureIdx)
- specularMult.blend_type = 'MULTIPLY'
- specularMult.inputs['Fac'].default_value = 1
- specularMult.inputs['Color1'].default_value = (1, 1, 1, 1)
-
- specularMult.location = texNode.location + Vector((200, 0))
- links.new(texNode.outputs['Color'], specularMult.inputs['Color2'])
-
- texNode = specularMult
- if textureSlot.use and textureIdx == 0:
- latestNode = texNode
-
- if textureSlot.use and textureIdx > 0:
- try:
- # Create a node to mix multiple texture nodes
- mixRgbNode = TreeNodes.nodes.new(RGB_MIX_NODE)
- mixRgbNode.parent = specularFrame
- addRGBMixNode(TreeNodes, textureSlot, mixRgbNode, texNode, latestNode,
- '{}'.format(groupName), textureIdx)
- mixRgbNode.location = Vector(
- (max(texNode.location.x, latestNode.location.x),
- (texNode.location.y + latestNode.location.y) / 2)) + Vector((200, 0)
- )
- latestNode = mixRgbNode
- except:
- continue
-
- if latestNode:
- try:
- glossShader = TreeNodes.nodes.new(BSDF_GLOSSY_NODE)
- RGBToBW = TreeNodes.nodes.new(RGB_TO_BW_NODE)
- RGBToBW.location = Vector((0, latestNode.location.y)) + Vector((0, 0))
- glossShader.location = Vector((0, latestNode.location.y)) + Vector((0, -80))
-
- links.new(latestNode.outputs['Color'], glossShader.inputs['Color'])
- links.new(latestNode.outputs['Color'], RGBToBW.inputs['Color'])
-
- outputNode = TreeNodes.nodes.get('Material Output')
- spec_mixer_1 = TreeNodes.nodes.new(SHADER_MIX_NODE)
- spec_mixer_1.location = outputNode.location
- spec_mixer_2 = TreeNodes.nodes.new(SHADER_MIX_NODE)
- spec_mixer_2.inputs['Fac'].default_value = .4
- spec_mixer_2.location = outputNode.location + Vector((180, 0))
- links.new(spec_mixer_1.outputs['Shader'], spec_mixer_2.inputs[2])
- links.new(spec_mixer_2.outputs['Shader'], outputNode.inputs['Surface'])
- links.new(RGBToBW.outputs['Val'], spec_mixer_1.inputs['Fac'])
-
- links.new(glossShader.outputs['BSDF'], spec_mixer_1.inputs[2])
-
- outputNode.location += Vector((360, 0))
- normalMapNode = TreeNodes.nodes.get('Normal Map')
- links.new(normalMapNode.outputs['Normal'], glossShader.inputs['Normal'])
-
- if mainDiffuse.type == 'BSDF_DIFFUSE':
- outputLink = 'BSDF'
- else:
- outputLink = 'Shader'
-
- links.new(mainDiffuse.outputs[outputLink], spec_mixer_1.inputs[1])
- links.new(mainDiffuse.outputs[outputLink], spec_mixer_2.inputs[1])
- except:
- return
-
-
-def createEmissionNodes(cmat, texCoordNode, mainShader, materialOutput):
- TreeNodes = cmat.node_tree
- links = TreeNodes.links
- texCount = len([node for node in TreeNodes.nodes if node.type == 'MAPPING'])
- currPosY = -textureNodeSizeY * texCount
-
- textureSlots = [textureSlot for textureSlot in cmat.texture_slots if
- (textureSlot and textureSlot.use_map_emit)]
- texCount = len(textureSlots)
- texNode = None
- latestNode = None
- groupName = 'Emission'
- if any(textureSlots):
- emissionFrame = TreeNodes.nodes.new(NODE_FRAME)
- emissionFrame.name = '{} Frame'.format(groupName)
- emissionFrame.label = '{}'.format(groupName)
-
- for textureIdx, textureSlot in enumerate(textureSlots):
- texNode = getTexNodeDic(textureSlot.texture)
- if texNode:
- tex_node_name = getattr(texNode.image, "name", "")
- collect_report("INFO: Generating {} Nodes for: ".format(groupName) + tex_node_name)
- texNode.parent = emissionFrame
- placeNode(texNode, -500 - ((texCount) * 200), currPosY,
- textureNodeSizeX, textureNodeSizeY, 0, textureIdx)
-
- # Add mapping node
- emissionMapping = TreeNodes.nodes.new(MAPPING_NODE)
- emissionMapping.parent = emissionFrame
- renameNode(emissionMapping, '{} Mapping'.format(groupName), texCount, textureIdx)
- emissionMapping.location = texNode.location + Vector((-400, 0))
- copyMapping(textureSlot, emissionMapping)
-
- # Texture Coordinates
- BIToCycleTexCoord(links, textureSlot, texCoordNode, emissionMapping)
-
- # Place the texture node
- renameNode(texNode, '{} Texture'.format(groupName), texCount, textureIdx)
- if texNode.image.image:
- texNode.image.colorspace_settings.is_data = True
- links.new(emissionMapping.outputs['Vector'], texNode.inputs['Vector'])
-
- # Add multiply node
- emissionMult = TreeNodes.nodes.new(RGB_MIX_NODE)
- emissionMult.parent = emissionFrame
- renameNode(emissionMult, 'Emission Mult', texCount, textureIdx)
- emissionMult.blend_type = 'MIX'
- emissionMult.inputs['Fac'].default_value = 1
- emissionMult.inputs['Color1'].default_value = (0, 0, 0, 1)
-
- emissionMult.location = texNode.location + Vector((200, 0))
- links.new(texNode.outputs['Color'], emissionMult.inputs['Color2'])
-
- texNode = emissionMult
- if textureSlot.use and textureIdx == 0:
- latestNode = texNode
-
- if textureSlot.use and textureIdx > 0:
- try:
- # Create a node to mix multiple texture nodes
- mixRgbNode = TreeNodes.nodes.new(RGB_MIX_NODE)
- mixRgbNode.parent = emissionFrame
- addRGBMixNode(TreeNodes, textureSlot, mixRgbNode, texNode, latestNode,
- '{}'.format(groupName), textureIdx)
- mixRgbNode.location = Vector(
- (max(texNode.location.x, latestNode.location.x),
- (texNode.location.y + latestNode.location.y) / 2)) + Vector((200, 0)
- )
- latestNode = mixRgbNode
- except:
- continue
-
- if latestNode:
- try:
- emissionNode = TreeNodes.nodes.new(BSDF_EMISSION_NODE)
- emissionNode.inputs['Strength'].default_value = 1
- addShaderNode = TreeNodes.nodes.new(SHADER_ADD_NODE)
- addShaderNode.location = materialOutput.location + Vector((0, -100))
- xPos = mainShader.location.x
- yPos = latestNode.location.y
-
- emissionNode.location = Vector((xPos, yPos))
- materialOutput.location += Vector((400, 0))
-
- node = materialOutput.inputs[0].links[0].from_node
- node.location += Vector((400, 0))
-
- links.new(latestNode.outputs['Color'], emissionNode.inputs['Color'])
- links.new(emissionNode.outputs['Emission'], addShaderNode.inputs[1])
- links.new(mainShader.outputs['BSDF'], addShaderNode.inputs[0])
- links.new(addShaderNode.outputs['Shader'], node.inputs[2])
- except:
- return
-
-
-def renameNode(node, baseName, nodesCount, nodeIndex):
- if nodesCount == 1:
- node.name = baseName
- else:
- node.name = '{} {:d}'.format(baseName, nodeIndex + 1)
-
-
-def hasAlphaTex(cmat):
- tex_is_transp = False
- for textureSlot in cmat.texture_slots:
- if textureSlot:
- if textureSlot.use:
- if textureSlot.use_map_alpha:
- tex_is_transp = tex_is_transp or True
- return tex_is_transp
-
-
-def AutoNode(active=False, operator=None):
- collect_report("________________________________________", True, False)
- collect_report("START CYCLES CONVERSION")
-
- if active:
- materials = [mat for obj in bpy.context.selected_objects if
- obj.type == 'MESH' for mat in obj.data.materials]
- else:
- materials = bpy.data.materials
-
- # No Materials for the chosen action - abort
- if not materials:
- if operator:
- if active:
- warning_messages(operator, 'CONV_NO_SEL_MAT', override=True)
- else:
- warning_messages(operator, 'CONV_NO_SC_MAT', override=True)
- return
-
- for cmat in materials:
- # check for empty material (it will fall through the first check)
- test_empty = getattr(cmat, "name", None)
- if test_empty is None:
- collect_report("INFO: An empty material was hit, skipping")
- continue
- else:
- cmat.use_nodes = True
- clearCycleMaterial(cmat)
- makeBiNodes(cmat)
- makeCyclesFromBI(cmat)
-
- collect_report("Conversion finished !", False, True)
-
- bpy.context.scene.render.engine = 'CYCLES'
-
-
-def makeCyclesFromBI(cmat):
- mat_name = getattr(cmat, "name", "NO NAME")
- collect_report("Converting Material: " + mat_name)
-
- global nodesDictionary
- TreeNodes = cmat.node_tree
- links = TreeNodes.links
-
- # Convert this material from non-nodes to Cycles nodes
- mainShader = None
- mainDiffuse = None
- Mix_Alpha = None
-
- tex_is_transp = hasAlphaTex(cmat)
-
- cmat_use_transp = cmat.use_transparency and cmat.alpha < 1
- cmat_trans_method = cmat.transparency_method
- cmat_ior = cmat.raytrace_transparency.ior
- cmat_transp_z = cmat_use_transp and cmat_trans_method == 'Z_TRANSPARENCY'
- cmat_transp_ray = cmat_use_transp and cmat_trans_method == 'RAYTRACE' and cmat_ior == 1
- cmat_mirror = cmat.raytrace_mirror.use
- cmat_mirror_fac = cmat.raytrace_mirror.reflect_factor
-
- # Material Shaders
- # Diffuse nodes
- # --------------------------------------
-
- # Make Diffuse and Output nodes
- mainShader = makeMainShader(TreeNodes)
- mainShader.inputs['Roughness'].default_value = math.sqrt(max(cmat.specular_intensity, 0.0))
- mainDiffuse = mainShader
- materialOutput = makeMaterialOutput(TreeNodes)
- links.new(mainShader.outputs['BSDF'], materialOutput.inputs['Surface'])
-
- texCoordNode = TreeNodes.nodes.new(COORD_NODE)
- texCoordNode.name = 'Texture Coordinate'
-
- # Material Transparent
- if not cmat_mirror and cmat_use_transp and tex_is_transp and (cmat_transp_z or cmat_transp_ray):
- collect_report("INFO: Make TRANSPARENT material nodes: " + cmat.name)
- Mix_Alpha = TreeNodes.nodes.new(SHADER_MIX_NODE)
- Mix_Alpha.name = 'Alpha Mix Shader'
- Mix_Alpha.location = materialOutput.location
- materialOutput.location += Vector((180, 0))
- Mix_Alpha.inputs['Fac'].default_value = cmat.alpha
- transparentShader = TreeNodes.nodes.new(BSDF_TRANSPARENT_NODE)
- transparentShader.location = mainShader.location
- mainShader.location += Vector((0, -100))
- links.new(transparentShader.outputs['BSDF'], Mix_Alpha.inputs[1])
- links.new(mainShader.outputs['BSDF'], Mix_Alpha.inputs[2])
- links.new(Mix_Alpha.outputs['Shader'], materialOutput.inputs['Surface'])
- mainDiffuse = Mix_Alpha
-
- if cmat_mirror and cmat_mirror_fac > 0.001:
- if cmat_use_transp:
- # Material Glass
- collect_report("INFO: Make GLASS shader node: " + cmat.name)
- newShader = TreeNodes.nodes.new(BSDF_GLASS_NODE)
- shader = newShader
- replaceNode(shader, newShader)
- TreeNodes.nodes.remove(shader)
- else:
- # Material Mirror
- collect_report("INFO: Make MIRROR shader node: " + cmat.name)
- newShader = TreeNodes.nodes.new(BSDF_GLOSSY_NODE)
- shader = newShader
- replaceNode(shader, newShader)
- TreeNodes.nodes.remove(shader)
-
- nodesDictionary = makeTextureNodeDict(cmat)
-
- # --------------------------------------
- # Texture nodes
-
- # BI Material to Cycles - Diffuse Textures
- createDiffuseNodes(cmat, texCoordNode, mainShader, materialOutput)
-
- # BI Material to Cycles - Normal map
- createNormalNodes(cmat, texCoordNode, mainShader, materialOutput)
-
- # BI Material to Cycles - Specular map
- createSpecularNodes(cmat, texCoordNode, mainShader, mainDiffuse, materialOutput)
-
- # BI Material to Cycles - Emission map
- createEmissionNodes(cmat, texCoordNode, mainShader, materialOutput)
-
- # Texture coordinates
- # list all nodes connected to outputs
- mappingNodes = [link.to_node for output in texCoordNode.outputs for link in output.links]
- mappingNodesCount = len(mappingNodes)
-
- if mappingNodes:
- xList = [node.location.x for node in mappingNodes]
- yList = [node.location.y for node in mappingNodes]
- minPosX = min(xList) - 400
- avgPosY = sum(yList) / mappingNodesCount
- texCoordNode.location = Vector((minPosX, avgPosY))
-
-
-# -----------------------------------------------------------------------------
-# Operator Classes
-
-class material_convert_all(Operator):
- bl_idname = "xps_tools.convert_to_cycles_all"
- bl_label = "Convert All Materials"
- bl_description = ("Convert All Materials to BI and Cycles Nodes\n"
- "Needs saving the .blend file first")
- bl_options = {'REGISTER', 'UNDO'}
-
- @classmethod
- def poll(cls, context):
- return (bpy.data.filepath != "" and c_is_cycles_addon_enabled() and
- c_data_has_materials())
-
- def execute(self, context):
- AutoNode(False, self)
-
- return {'FINISHED'}
-
-
-class material_convert_selected(Operator):
- bl_idname = "xps_tools.convert_to_cycles_selected"
- bl_label = "Convert All Materials From Selected Objects"
- bl_description = ("Convert All Materials on Selected Objects to BI and Cycles Nodes\n"
- "Needs saving the .blend file first")
- bl_options = {'REGISTER', 'UNDO'}
-
- @classmethod
- def poll(cls, context):
- return (bpy.data.filepath != "" and c_data_has_materials() and
- c_is_cycles_addon_enabled() and
- bool(next((obj for obj in context.selected_objects if obj.type == 'MESH'), None))
- )
-
- def execute(self, context):
- AutoNode(True, self)
-
- return {'FINISHED'}
-
-
-def register():
- bpy.utils.register_module(__name__)
- pass
-
-
-def unregister():
- bpy.utils.unregister_module(__name__)
- pass
-
-
-if __name__ == "__main__":
- register()
diff --git a/materials_utils/materials_cycles_converter.py b/materials_utils/materials_cycles_converter.py
deleted file mode 100644
index 573e4309..00000000
--- a/materials_utils/materials_cycles_converter.py
+++ /dev/null
@@ -1,977 +0,0 @@
-# gpl: author Silvio Falcinelli. Fixes by angavrilov and others.
-# special thanks to user blenderartists.org cmomoney
-# -*- coding: utf-8 -*-
-
-import bpy
-from os import path as os_path
-from bpy.types import Operator
-from math import (
- log2, ceil, sqrt,
-)
-from bpy.props import (
- BoolProperty,
- EnumProperty,
-)
-from .warning_messages_utils import (
- warning_messages,
- c_is_cycles_addon_enabled,
- c_data_has_materials,
- collect_report,
-)
-
-# -----------------------------------------------------------------------------
-# Globals
-
-# switch for operator's function called after AutoNodeInitiate
-CHECK_AUTONODE = False
-
-# set the node color for baked images (default greenish)
-NODE_COLOR = (0.32, 0.75, 0.32)
-# set the node color for the paint base images (default reddish)
-NODE_COLOR_PAINT = (0.6, 0.0, 0.0)
-# set the mix node color (default blueish)
-NODE_COLOR_MIX = (0.1, 0.7, 0.8)
-
-# color for sculpt/texture painting setting (default clay the last entry is Roughness)
-PAINT_SC_COLOR = (0.80, 0.75, 0.54, 0.9)
-CLAY_GLOSSY = (0.38, 0.032, 0.023, 1)
-
-# -----------------------------------------------------------------------------
-# Functions
-
-
-def AutoNodeSwitch(renderer="CYCLES", switch="OFF", operator=None):
- mats = bpy.data.materials
- use_nodes = (True if switch in ("ON") else False)
- warn_message = ('BI_SW_NODES_ON' if switch in ("ON") else
- 'BI_SW_NODES_OFF')
- warn_message_2 = ('CYC_SW_NODES_ON' if switch in ("ON") else
- 'CYC_SW_NODES_OFF')
- for cmat in mats:
- cmat.use_nodes = use_nodes
- renders = ('CYCLES' if renderer and renderer == "CYCLES" else
- 'BLENDER_RENDER')
- bpy.context.scene.render.engine = renders
- if operator:
- warning_messages(operator, (warn_message_2 if renders in ('CYCLES') else
- warn_message))
-
-
-def SetFakeUserTex():
- images = bpy.data.images
- for image in images:
- has_user = getattr(image, "users", -1)
- image_name = getattr(image, "name", "NONAME")
-
- if has_user == 0:
- image.use_fake_user = True
- collect_report("INFO: Set fake user for unused image: " + image_name)
-
-
-def BakingText(tex, mode, tex_type=None):
- collect_report("INFO: start bake texture named: " + tex.name)
- saved_img_path = None
-
- bpy.ops.object.mode_set(mode='OBJECT')
- sc = bpy.context.scene
- tmat = ''
- img = ''
- Robj = bpy.context.active_object
- for n in bpy.data.materials:
- if n.name == 'TMP_BAKING':
- tmat = n
- if not tmat:
- tmat = bpy.data.materials.new('TMP_BAKING')
- tmat.name = "TMP_BAKING"
-
- bpy.ops.mesh.primitive_plane_add()
- tm = bpy.context.active_object
- tm.name = "TMP_BAKING"
- tm.data.name = "TMP_BAKING"
- bpy.ops.object.select_pattern(extend=False, pattern="TMP_BAKING",
- case_sensitive=False)
- sc.objects.active = tm
- bpy.context.scene.render.engine = 'BLENDER_RENDER'
- tm.data.materials.append(tmat)
- if len(tmat.texture_slots.items()) == 0:
- tmat.texture_slots.add()
- tmat.texture_slots[0].texture_coords = 'UV'
- tmat.texture_slots[0].use_map_alpha = True
- tmat.texture_slots[0].texture = tex.texture
- tmat.texture_slots[0].use_map_alpha = True
- tmat.texture_slots[0].use_map_color_diffuse = False
- tmat.use_transparency = True
- tmat.alpha = 0
- tmat.use_nodes = False
- tmat.diffuse_color = 1, 1, 1
- bpy.ops.object.mode_set(mode='EDIT')
- bpy.ops.uv.unwrap()
-
- # clean up temporary baking images if any
- for n in bpy.data.images:
- if n.name == 'TMP_BAKING':
- n.user_clear()
- bpy.data.images.remove(n)
-
- if mode == "ALPHA" and tex.texture.type == 'IMAGE':
- sizeX = tex.texture.image.size[0]
- sizeY = tex.texture.image.size[1]
- else:
- bake_size = (int(sc.mat_context_menu.img_bake_size) if
- sc.mat_context_menu.img_bake_size else 1024)
- sizeX = bake_size
- sizeY = bake_size
-
- bpy.ops.image.new(name="TMP_BAKING", width=sizeX, height=sizeY,
- color=(0.0, 0.0, 0.0, 1.0), alpha=True, float=False)
-
- sc.render.engine = 'BLENDER_RENDER'
- img = bpy.data.images["TMP_BAKING"]
- img = bpy.data.images.get("TMP_BAKING")
- img.file_format = ("JPEG" if not mode == "ALPHA" else "PNG")
-
- # switch temporarily to 'IMAGE EDITOR', other approaches are not reliable
- check_area = False
- store_area = bpy.context.area.type
- collect_report("INFO: Temporarily switching context to Image Editor")
- try:
- bpy.context.area.type = 'IMAGE_EDITOR'
- bpy.context.area.spaces[0].image = bpy.data.images["TMP_BAKING"]
- check_area = True
- except:
- collect_report("ERROR: Setting to Image Editor failed, Baking aborted")
- check_area = False
-
- if check_area:
- paths = bpy.path.abspath(sc.mat_context_menu.conv_path)
- tex_name = getattr(getattr(tex.texture, "image", None), "name", None)
- texture_name = (tex_name.rpartition(".")[0] if tex_name else tex.texture.name)
- new_tex_name = "baked"
- name_append = ("_BAKING" if mode == "ALPHA" and
- tex.texture.type == 'IMAGE' else "_PTEXT")
- new_appendix = (".jpg" if not mode == "ALPHA" else ".png")
-
- if name_append in texture_name:
- new_tex_name = texture_name
- elif tex_type:
- new_tex_name = tex_type + name_append
- else:
- new_tex_name = texture_name + name_append
-
- img.filepath_raw = paths + new_tex_name + new_appendix
- saved_img_path = img.filepath_raw
-
- sc.render.bake_type = 'ALPHA'
- sc.render.use_bake_selected_to_active = True
- sc.render.use_bake_clear = True
-
- # try to bake if it fails give report
- try:
- bpy.ops.object.bake_image()
- img.save()
- except:
- # no return value, so the image loading is skipped
- saved_img_path = None
- collect_report("ERROR: Baking could not be completed. "
- "Check System Console for info")
- if store_area:
- bpy.context.area.type = store_area
-
- bpy.ops.object.mode_set(mode='OBJECT')
- bpy.ops.object.delete()
- bpy.ops.object.select_pattern(extend=False, pattern=Robj.name, case_sensitive=False)
- sc.objects.active = Robj
- img.user_clear()
- bpy.data.images.remove(img)
-
- if tmat.users == 0:
- bpy.data.materials.remove(tmat)
-
- if saved_img_path:
- collect_report("------- Baking finished -------")
- return saved_img_path
-
-
-def AutoNodeInitiate(active=False, operator=None):
- # Checks with bpy.ops.material.check_converter_path
- # if it's possible to write in the output path
- # if it passes proceeds with calling AutoNode
-
- # if CheckImagePath(operator):
- check_path = bpy.ops.material.check_converter_path()
-
- global CHECK_AUTONODE
-
- if 'FINISHED' in check_path:
- sc = bpy.context.scene
- CHECK_AUTONODE = True
- collect_report("_______________________", True, False)
- AutoNode(active, operator)
- if sc.mat_context_menu.SET_FAKE_USER:
- SetFakeUserTex()
- else:
- warning_messages(operator, 'DIR_PATH_CONVERT', override=True)
-
-
-def AutoNode(active=False, operator=None):
- global CHECK_AUTONODE
- sc = bpy.context.scene
- if active:
- # fix for empty slots by angavrilov
- mats = [slot.material for slot in bpy.context.active_object.material_slots if
- slot.material]
- else:
- mats = bpy.data.materials
-
- # No Materials for the chosen action - abort
- if not mats:
- CHECK_AUTONODE = False
- if operator:
- if active:
- act_obj = bpy.context.active_object
- warning_messages(operator, 'CONV_NO_OBJ_MAT', act_obj.name)
- else:
- warning_messages(operator, 'CONV_NO_SC_MAT')
- return
-
- for cmat in mats:
- # check for empty material (it will fall through the first check)
- test_empty = getattr(cmat, "name", None)
- if test_empty is None:
- collect_report("An empty material was hit, skipping")
- continue
-
- cmat.use_nodes = True
- TreeNodes = cmat.node_tree
- links = TreeNodes.links
-
- # Don't alter nodes of locked materials
- locked = False
- for n in TreeNodes.nodes:
- if n.type == 'ShaderNodeOutputMaterial':
- if n.label == 'Locked':
- locked = True
- break
-
- if not locked:
- # Convert this material from non-nodes to Cycles nodes
- shader = ''
- shtsl = ''
- Add_Emission = ''
- Add_Translucent = ''
- Mix_Alpha = ''
- sT = False
- # check if some link creation failed
- link_fail = False
-
- for n in TreeNodes.nodes:
- TreeNodes.nodes.remove(n)
-
- # Starting point is diffuse BSDF and output material and a Color Ramp node
- shader = TreeNodes.nodes.new('ShaderNodeBsdfDiffuse')
- shader.location = 10, 10
- shader_val = TreeNodes.nodes.new('ShaderNodeValToRGB')
- shader_val.location = 0, -200
- shout = TreeNodes.nodes.new('ShaderNodeOutputMaterial')
- shout.location = 200, 10
- try:
- links.new(shader.outputs[0], shout.inputs[0])
- links.new(shader.inputs[0], shader_val.outputs[0])
- except:
- link_fail = True
-
- # Create other shader types only sculpt/texture paint mode is False
- sculpt_paint = sc.mat_context_menu.SCULPT_PAINT
- if sculpt_paint is False:
-
- cmat_is_transp = cmat.use_transparency and cmat.alpha < 1
-
- if not cmat.raytrace_mirror.use and not cmat_is_transp:
- if not shader.type == 'ShaderNodeBsdfDiffuse':
- collect_report("INFO: Make DIFFUSE shader node for: " + cmat.name)
- TreeNodes.nodes.remove(shader)
- shader = TreeNodes.nodes.new('ShaderNodeBsdfDiffuse')
- shader.location = 10, 10
- try:
- links.new(shader.outputs[0], shout.inputs[0])
- except:
- link_fail = True
-
- if cmat.raytrace_mirror.use and cmat.raytrace_mirror.reflect_factor > 0.001 and cmat_is_transp:
- if not shader.type == 'ShaderNodeBsdfGlass':
- collect_report("INFO: Make GLASS shader node for: " + cmat.name)
- TreeNodes.nodes.remove(shader)
- shader = TreeNodes.nodes.new('ShaderNodeBsdfGlass')
- shader.location = 0, 100
- try:
- links.new(shader.outputs[0], shout.inputs[0])
- except:
- link_fail = True
-
- if cmat.raytrace_mirror.use and not cmat_is_transp and cmat.raytrace_mirror.reflect_factor > 0.001:
- if not shader.type == 'ShaderNodeBsdfGlossy':
- collect_report("INFO: Make MIRROR shader node for: " + cmat.name)
- TreeNodes.nodes.remove(shader)
- shader = TreeNodes.nodes.new('ShaderNodeBsdfGlossy')
- shader.location = 0, 10
- try:
- links.new(shader.outputs[0], shout.inputs[0])
- except:
- link_fail = True
-
- if cmat.emit > 0.001:
- if (not shader.type == 'ShaderNodeEmission' and not
- cmat.raytrace_mirror.reflect_factor > 0.001 and not cmat_is_transp):
-
- collect_report("INFO: Mix EMISSION shader node for: " + cmat.name)
- TreeNodes.nodes.remove(shader)
- shader = TreeNodes.nodes.new('ShaderNodeEmission')
- shader.location = 0, 200
- try:
- links.new(shader.outputs[0], shout.inputs[0])
- except:
- link_fail = True
- else:
- if not Add_Emission:
- collect_report("INFO: Add EMISSION shader node for: " + cmat.name)
- shout.location = 600, 100
- Add_Emission = TreeNodes.nodes.new('ShaderNodeAddShader')
- Add_Emission.location = 370, 100
-
- shem = TreeNodes.nodes.new('ShaderNodeEmission')
- shem.location = 0, 200
- try:
- links.new(Add_Emission.outputs[0], shout.inputs[0])
- links.new(shem.outputs[0], Add_Emission.inputs[1])
- links.new(shader.outputs[0], Add_Emission.inputs[0])
- except:
- link_fail = True
-
- shem.inputs['Color'].default_value = (cmat.diffuse_color.r,
- cmat.diffuse_color.g,
- cmat.diffuse_color.b, 1)
- shem.inputs['Strength'].default_value = cmat.emit
-
- if cmat.translucency > 0.001:
- collect_report("INFO: Add BSDF_TRANSLUCENT shader node for: " + cmat.name)
- shout.location = 770, 330
- Add_Translucent = TreeNodes.nodes.new('ShaderNodeAddShader')
- Add_Translucent.location = 580, 490
-
- shtsl = TreeNodes.nodes.new('ShaderNodeBsdfTranslucent')
- shtsl.location = 400, 350
- try:
- links.new(Add_Translucent.outputs[0], shout.inputs[0])
- links.new(shtsl.outputs[0], Add_Translucent.inputs[1])
-
- if Add_Emission:
- links.new(Add_Emission.outputs[0], Add_Translucent.inputs[0])
- else:
- links.new(shader.outputs[0], Add_Translucent.inputs[0])
- except:
- link_fail = True
-
- shtsl.inputs['Color'].default_value = (cmat.translucency,
- cmat.translucency,
- cmat.translucency, 1)
- if sculpt_paint is False:
- shader.inputs['Color'].default_value = (cmat.diffuse_color.r,
- cmat.diffuse_color.g,
- cmat.diffuse_color.b, 1)
- else:
- # Create Clay Material (Diffuse, Glossy, Layer Weight)
- shader.inputs['Color'].default_value = PAINT_SC_COLOR
- shader.inputs['Roughness'].default_value = 0.9486
-
- # remove Color Ramp and links from the default shader and reroute
- try:
- shout.location = 400, 0
- for link in links:
- links.remove(link)
-
- clay_frame = TreeNodes.nodes.new('NodeFrame')
- clay_frame.name = 'Clay Material'
- clay_frame.label = 'Clay Material'
-
- sh_glossy = TreeNodes.nodes.new('ShaderNodeBsdfGlossy')
- sh_glossy.location = 0, 200
- sh_glossy.inputs['Color'].default_value = CLAY_GLOSSY
- sh_mix = TreeNodes.nodes.new('ShaderNodeMixShader')
- sh_mix.location = 200, 0
- sh_weight = TreeNodes.nodes.new('ShaderNodeLayerWeight')
- sh_weight.location = 0, 350
- links.new(sh_mix.outputs[0], shout.inputs[0])
- links.new(sh_weight.outputs[1], sh_mix.inputs[0])
- links.new(shader.outputs[0], sh_mix.inputs[1])
- links.new(sh_glossy.outputs[0], sh_mix.inputs[2])
- # set frame as parent to everything
- for clay_node in (shader, sh_glossy, sh_mix, sh_weight):
- clay_node.parent = clay_frame
- except:
- collect_report("ERROR: Failure to create Clay Material")
-
- if not sculpt_paint:
- if shader.type == 'ShaderNodeBsdfDiffuse':
- shader.inputs['Roughness'].default_value = cmat.specular_intensity
-
- if shader.type == 'ShaderNodeBsdfGlossy':
- shader.inputs['Roughness'].default_value = sqrt(max(1 - cmat.raytrace_mirror.gloss_factor, 0.0))
-
- if shader.type == 'ShaderNodeBsdfGlass':
- shader.inputs['Roughness'].default_value = sqrt(max(1 - cmat.raytrace_mirror.gloss_factor, 0.0))
- shader.inputs['IOR'].default_value = cmat.raytrace_transparency.ior
-
- if shader.type == 'ShaderNodeEmission':
- shader.inputs['Strength'].default_value = cmat.emit
-
- # texture presence check
- is_textures = False
-
- for tex in cmat.texture_slots:
- if tex:
- if not is_textures:
- is_textures = True
- break
-
- if is_textures:
- # collect the texture nodes created
- # for spreading a bit the texture nodes
- tex_node_collect = []
-
- sM = True
-
- for tex in cmat.texture_slots:
- sT = False
- tex_use = getattr(tex, "use", None)
- baked_path = None
- if tex_use:
- tex_node_loc = -200, 450
- ma_alpha = getattr(tex, "use_map_alpha", None)
- sM = (False if ma_alpha else True)
- img = None
-
- if tex.texture.type == 'IMAGE':
- if sc.mat_context_menu.EXTRACT_ALPHA and tex.texture.use_alpha:
- if (not
- os_path.exists(bpy.path.abspath(tex.texture.image.filepath + "_BAKING.png")) or
- sc.mat_context_menu.EXTRACT_OW):
- baked_path = BakingText(tex, 'ALPHA')
-
- if baked_path:
- try:
- img = bpy.data.images.load(baked_path)
- collect_report("INFO: Loading Baked texture path:")
- collect_report(baked_path)
- except:
- collect_report("ERROR: Baked image could not be loaded")
- else:
- has_image = getattr(tex.texture, "image", None)
- if has_image:
- img = has_image
-
- if img:
- img_name = (img.name if hasattr(img, "name") else "NO NAME")
- shtext = TreeNodes.nodes.new('ShaderNodeTexImage')
- shtext.location = tex_node_loc
- shtext.hide = True
- shtext.width_hidden = 150
- shtext.image = img
- shtext.name = img_name
- shtext.label = "Image " + img_name
- if baked_path:
- shtext.use_custom_color = True
- shtext.color = NODE_COLOR
- collect_report("INFO: Creating Image Node for image: " + img_name)
- tex_node_collect.append(shtext)
- sT = True
- else:
- collect_report("ERROR: A problem occurred with loading an image for {} "
- "(possibly missing)".format(tex.texture.name))
- else:
- if sc.mat_context_menu.EXTRACT_PTEX or (sc.mat_context_menu.EXTRACT_ALPHA and ma_alpha):
- if (not os_path.exists(bpy.path.abspath(tex.texture.name + "_PTEXT.jpg")) or
- sc.mat_context_menu.EXTRACT_OW):
- tex_type = tex.texture.type.lower()
- collect_report("Attempting to Extract Procedural Texture type: " + tex_type)
- baked_path = BakingText(tex, 'PTEX', tex_type)
-
- if baked_path:
- try:
- img = bpy.data.images.load(baked_path)
- collect_report("Loading Baked texture path:")
- collect_report(baked_path)
- img_name = (img.name if hasattr(img, "name") else "NO NAME")
- shtext = TreeNodes.nodes.new('ShaderNodeTexImage')
- shtext.location = tex_node_loc
- shtext.hide = True
- shtext.width_hidden = 150
- shtext.image = img
- shtext.name = img_name
- shtext.label = "Baked Image " + img_name
- shtext.use_custom_color = True
- shtext.color = NODE_COLOR
- collect_report("Creating Image Node for baked image: " + img_name)
- tex_node_collect.append(shtext)
- sT = True
- except:
- collect_report("ERROR: Failure to load baked image: " + img_name)
- else:
- collect_report("ERROR: Failure during baking, no images loaded")
-
- if sculpt_paint is False:
- if (cmat_is_transp and cmat.raytrace_transparency.ior == 1 and
- not cmat.raytrace_mirror.use and sM):
-
- if not shader.type == 'ShaderNodeBsdfTransparent':
- collect_report("INFO: Make TRANSPARENT shader node for: " + cmat.name)
- TreeNodes.nodes.remove(shader)
- shader = TreeNodes.nodes.new('ShaderNodeBsdfTransparent')
- shader.location = 0, 470
- try:
- links.new(shader.outputs[0], shout.inputs[0])
- except:
- link_fail = True
-
- shader.inputs['Color'].default_value = (cmat.diffuse_color.r,
- cmat.diffuse_color.g,
- cmat.diffuse_color.b, 1)
-
- if sT and sculpt_paint is False:
- if tex.use_map_color_diffuse:
- try:
- links.new(shtext.outputs[0], shader.inputs[0])
- except:
- pass
- if tex.use_map_emit:
- if not Add_Emission:
- collect_report("INFO: Mix EMISSION + Texture shader node for: " + cmat.name)
- intensity = 0.5 + (tex.emit_factor / 2)
-
- shout.location = 550, 330
- Add_Emission = TreeNodes.nodes.new('ShaderNodeAddShader')
- Add_Emission.name = "Add_Emission"
- Add_Emission.location = 370, 490
-
- shem = TreeNodes.nodes.new('ShaderNodeEmission')
- shem.location = 180, 380
-
- try:
- links.new(Add_Emission.outputs[0], shout.inputs[0])
- links.new(shem.outputs[0], Add_Emission.inputs[1])
- links.new(shader.outputs[0], Add_Emission.inputs[0])
- links.new(shtext.outputs[0], shem.inputs[0])
- except:
- link_fail = True
-
- shem.inputs['Color'].default_value = (cmat.diffuse_color.r,
- cmat.diffuse_color.g,
- cmat.diffuse_color.b, 1)
- shem.inputs['Strength'].default_value = intensity * 2
-
- if tex.use_map_mirror:
- try:
- links.new(shtext.outputs[0], shader.inputs[0])
- except:
- link_fail = True
-
- if tex.use_map_translucency:
- if not Add_Translucent:
- collect_report("INFO: Add Translucency + Texture shader node for: " + cmat.name)
-
- intensity = 0.5 + (tex.emit_factor / 2)
- shout.location = 550, 330
- Add_Translucent = TreeNodes.nodes.new('ShaderNodeAddShader')
- Add_Translucent.name = "Add_Translucent"
- Add_Translucent.location = 370, 290
-
- shtsl = TreeNodes.nodes.new('ShaderNodeBsdfTranslucent')
- shtsl.location = 180, 240
- try:
- links.new(shtsl.outputs[0], Add_Translucent.inputs[1])
-
- if Add_Emission:
- links.new(Add_Translucent.outputs[0], shout.inputs[0])
- links.new(Add_Emission.outputs[0], Add_Translucent.inputs[0])
- pass
- else:
- links.new(Add_Translucent.outputs[0], shout.inputs[0])
- links.new(shader.outputs[0], Add_Translucent.inputs[0])
-
- links.new(shtext.outputs[0], shtsl.inputs[0])
- except:
- link_fail = True
-
- if tex.use_map_alpha:
- if not Mix_Alpha:
- collect_report("INFO: Mix Alpha + Texture shader node for: " + cmat.name)
-
- shout.location = 750, 330
- Mix_Alpha = TreeNodes.nodes.new('ShaderNodeMixShader')
- Mix_Alpha.name = "Add_Alpha"
- Mix_Alpha.location = 570, 290
- sMask = TreeNodes.nodes.new('ShaderNodeBsdfTransparent')
- sMask.location = 250, 180
- tMask, imask = None, None
-
- # search if the texture node already exists, if not create
- nodes = getattr(cmat.node_tree, "nodes", None)
- img_name = getattr(img, "name", "NO NAME")
- for node in nodes:
- if type(node) == bpy.types.ShaderNodeTexImage:
- node_name = getattr(node, "name")
- if img_name in node_name:
- tMask = node
- collect_report("INFO: Using existing Texture Node for Mask: " + node_name)
- break
-
- if tMask is None:
- tMask = TreeNodes.nodes.new('ShaderNodeTexImage')
- tMask.location = tex_node_loc
- tex_node_collect.append(tMask)
-
- try:
- file_path = getattr(img, "filepath", None)
- if file_path:
- imask = bpy.data.images.load(file_path)
- else:
- imask = bpy.data.images.get(img_name)
- collect_report("INFO: Attempting to load image for Mask: " + img_name)
- except:
- collect_report("ERROR: Failure to load image for Mask: " + img_name)
-
- if imask:
- tMask.image = imask
-
- if tMask:
- try:
- links.new(Mix_Alpha.inputs[0], tMask.outputs[1])
- links.new(shout.inputs[0], Mix_Alpha.outputs[0])
- links.new(sMask.outputs[0], Mix_Alpha.inputs[1])
-
- if not Add_Translucent:
- if Add_Emission:
- links.new(Mix_Alpha.inputs[2], Add_Emission.outputs[0])
- else:
- links.new(Mix_Alpha.inputs[2], shader.outputs[0])
- else:
- links.new(Mix_Alpha.inputs[2], Add_Translucent.outputs[0])
- except:
- link_fail = True
- else:
- collect_report("ERROR: Mix Alpha could not be created "
- "(mask image could not be loaded)")
-
- if tex.use_map_normal:
- t = TreeNodes.nodes.new('ShaderNodeRGBToBW')
- t.location = -0, 300
- try:
- links.new(t.outputs[0], shout.inputs[2])
- links.new(shtext.outputs[1], t.inputs[0])
- except:
- link_fail = True
-
- if sculpt_paint:
- try:
- # create a new image for texture painting and make it active
- img_size = (int(sc.mat_context_menu.img_bake_size) if
- sc.mat_context_menu.img_bake_size else 1024)
- paint_mat_name = getattr(cmat, "name", "NO NAME")
- paint_img_name = "Paint Base Image {}".format(paint_mat_name)
- bpy.ops.image.new(name=paint_img_name, width=img_size, height=img_size,
- color=(1.0, 1.0, 1.0, 1.0), alpha=True, float=False)
-
- img = bpy.data.images.get(paint_img_name)
- img_name = (img.name if hasattr(img, "name") else "NO NAME")
- shtext = TreeNodes.nodes.new('ShaderNodeTexImage')
- shtext.hide = True
- shtext.width_hidden = 150
- shtext.location = tex_node_loc
- shtext.image = img
- shtext.name = img_name
- shtext.label = "Paint: " + img_name
- shtext.use_custom_color = True
- shtext.color = NODE_COLOR_PAINT
- shtext.select = True
- collect_report("INFO: Creating Image Node for Painting: " + img_name)
- collect_report("WARNING: Don't forget to save it on Disk")
- tex_node_collect.append(shtext)
- except:
- collect_report("ERROR: Failed to create image and node for Texture Painting")
-
- # spread the texture nodes, create node frames if necessary
- # create texture coordinate and mapping too
- row_node = -1
- tex_node_collect_size = len(tex_node_collect)
- median_point = ((tex_node_collect_size / 2) * 100)
- check_frame = bool(tex_node_collect_size > 1)
-
- node_frame, tex_map = None, None
- node_f_coord, node_f_mix = None, None
- tex_map_collection, tex_map_coord = [], None
- tree_size, tree_tex_start = 0, 0
-
- if check_frame:
- node_frame = TreeNodes.nodes.new('NodeFrame')
- node_frame.name = 'Converter Textures'
- node_frame.label = 'Converter Textures'
-
- node_f_coord = TreeNodes.nodes.new('NodeFrame')
- node_f_coord.name = "Coordinates"
- node_f_coord.label = "Coordinates"
-
- node_f_mix = TreeNodes.nodes.new('NodeFrame')
- node_f_mix.name = "Mix"
- node_f_mix.label = "Mix"
-
- if tex_node_collect:
- tex_map_coord = TreeNodes.nodes.new('ShaderNodeTexCoord')
- tex_map_coord.location = -900, 575
-
- # precalculate the depth of the inverted tree
- tree_size = int(ceil(log2(tex_node_collect_size)))
- # offset the start of the mix nodes by the depth of the tree
- tree_tex_start = ((tree_size + 1) * 150)
-
- for node_tex in tex_node_collect:
- row_node += 1
- col_node_start = (median_point - (-(row_node * 50) + median_point))
- tex_node_row = tree_tex_start + 300
- mix_node_row = tree_tex_start + 620
- tex_node_loc = (-(tex_node_row), col_node_start)
-
- try:
- node_tex.location = tex_node_loc
- if check_frame:
- node_tex.parent = node_frame
- else:
- node_tex.hide = False
-
- tex_node_name = getattr(node_tex, "name", "NO NAME")
- tex_map_name = "Mapping: {}".format(tex_node_name)
- tex_map = TreeNodes.nodes.new('ShaderNodeMapping')
- tex_map.location = (-(mix_node_row), col_node_start)
- tex_map.width = 240
- tex_map.hide = True
- tex_map.width_hidden = 150
- tex_map.name = tex_map_name
- tex_map.label = tex_map_name
- tex_map_collection.append(tex_map)
- links.new(tex_map.outputs[0], node_tex.inputs[0])
- except:
- link_fail = True
- continue
-
- if tex_map_collection:
- tex_mix_start = len(tex_map_collection) / 2
- row_map_start = -(tree_tex_start + 850)
-
- if tex_map_coord:
- tex_map_coord.location = (row_map_start,
- (median_point - (tex_mix_start * 50)))
-
- for maps in tex_map_collection:
- try:
- if node_f_coord:
- maps.parent = node_f_coord
- else:
- maps.hide = False
-
- links.new(maps.inputs[0], tex_map_coord.outputs['UV'])
- except:
- link_fail = True
- continue
-
- # create mix nodes to connect texture nodes to the shader input
- # sculpt mode doesn't need them
- if check_frame and not sculpt_paint:
- mix_node_pairs = loop_node_from_list(TreeNodes, links, tex_node_collect,
- 0, tree_tex_start, median_point, node_f_mix)
-
- for n in range(1, tree_size):
- mix_node_pairs = loop_node_from_list(TreeNodes, links, mix_node_pairs,
- n, tree_tex_start, median_point, node_f_mix)
- try:
- for node in mix_node_pairs:
- links.new(node.outputs[0], shader.inputs[0])
- except:
- link_fail = True
-
- mix_node_pairs = []
-
- tex_node_collect, tex_map_collection = [], []
-
- if link_fail:
- collect_report("ERROR: Some of the node links failed to connect")
-
- else:
- collect_report("No textures in the Scene, no Image Nodes to add")
-
- bpy.context.scene.render.engine = 'CYCLES'
-
-
-def loop_node_from_list(TreeNodes, links, node_list, loc, start, median_point, frame):
- row = 1
- mix_nodes = []
- node_list_size = len(node_list)
- tuplify = [tuple(node_list[s:s + 2]) for s in range(0, node_list_size, 2)]
- for nodes in tuplify:
- row += 1
- create_mix = create_mix_node(TreeNodes, links, nodes, loc, start,
- median_point, row, frame)
- if create_mix:
- mix_nodes.append(create_mix)
- return mix_nodes
-
-
-def create_mix_node(TreeNodes, links, nodes, loc, start, median_point, row, frame):
- mix_node = TreeNodes.nodes.new('ShaderNodeMixRGB')
- mix_node.name = "MIX level: " + str(loc)
- mix_node.label = "MIX level: " + str(loc)
- mix_node.use_custom_color = True
- mix_node.color = NODE_COLOR_MIX
- mix_node.hide = True
- mix_node.width_hidden = 75
-
- if frame:
- mix_node.parent = frame
- mix_node.location = -(start - loc * 175), ((median_point / 4) + (row * 50))
-
- try:
- if len(nodes) > 1:
- links.new(nodes[0].outputs[0], mix_node.inputs["Color2"])
- links.new(nodes[1].outputs[0], mix_node.inputs["Color1"])
- elif len(nodes) == 1:
- links.new(nodes[0].outputs[0], mix_node.inputs["Color1"])
- except:
- collect_report("ERROR: Link failed for mix node {}".format(mix_node.label))
- return mix_node
-
-
-def unwrap_active_object(context):
- enable_unwrap = context.scene.mat_context_menu.UV_UNWRAP
- if enable_unwrap:
- obj_name = getattr(context.active_object, "name", "UNNAMED OBJECT")
- try:
- # it's possible that the active object would fail UV Unwrap
- bpy.ops.object.editmode_toggle()
- bpy.ops.uv.unwrap(method='ANGLE_BASED', margin=0.001)
- bpy.ops.object.editmode_toggle()
- collect_report("INFO: UV Unwrapping active object {}".format(obj_name))
- except:
- collect_report("ERROR: UV Unwrapping failed for "
- "active object {}".format(obj_name))
-
-
-# -----------------------------------------------------------------------------
-# Operator Classes
-
-class mllock(Operator):
- bl_idname = "ml.lock"
- bl_label = "Lock"
- bl_description = "Lock/unlock this material against modification by conversions"
- bl_options = {'REGISTER', 'UNDO'}
-
- @classmethod
- def poll(cls, context):
- return (c_is_cycles_addon_enabled() and c_data_has_materials())
-
- def execute(self, context):
- cmat = bpy.context.selected_objects[0].active_material
- TreeNodes = cmat.node_tree
- for n in TreeNodes.nodes:
- if n.type == 'ShaderNodeOutputMaterial':
- n.label = "" if n.label == "Locked" else "Locked"
-
- return {'FINISHED'}
-
-
-class mlrefresh(Operator):
- bl_idname = "ml.refresh"
- bl_label = "Convert All Materials"
- bl_description = ("Convert All Materials in the scene from non-nodes to Cycles\n"
- "Needs saving the .blend file first")
- bl_options = {'REGISTER', 'UNDO'}
-
- @classmethod
- def poll(cls, context):
- return (bpy.data.filepath != "" and c_is_cycles_addon_enabled() and
- c_data_has_materials())
-
- def execute(self, context):
- AutoNodeInitiate(False, self)
-
- if CHECK_AUTONODE is True:
- unwrap_active_object(context)
-
- collect_report("Conversion finished !", False, True)
-
- return {'FINISHED'}
-
-
-class mlrefresh_active(Operator):
- bl_idname = "ml.refresh_active"
- bl_label = "Convert All Materials From Active Object"
- bl_description = ("Convert all Active Object's Materials from non-nodes to Cycles\n"
- "Needs saving the .blend file first")
- bl_options = {'REGISTER', 'UNDO'}
-
- @classmethod
- def poll(cls, context):
- return (bpy.data.filepath != "" and c_is_cycles_addon_enabled() and
- c_data_has_materials() and context.active_object is not None)
-
- def execute(self, context):
- AutoNodeInitiate(True, self)
- if CHECK_AUTONODE is True:
- unwrap_active_object(context)
-
- collect_report("Conversion finished !", False, True)
-
- return {'FINISHED'}
-
-
-class mlrestore(Operator):
- bl_idname = "ml.restore"
- bl_label = "Switch Between Renderers"
- bl_description = ("Switch between Renderers \n"
- "(Doesn't create new nor converts existing materials)")
- bl_options = {'REGISTER', 'UNDO'}
-
- switcher: BoolProperty(
- name="Use Nodes",
- description="When restoring, switch Use Nodes On/Off",
- default=True
- )
- renderer: EnumProperty(
- name="Renderer",
- description="Choose Cycles or Blender Internal",
- items=(
- ('CYCLES', "Cycles", "Switch to Cycles"),
- ('BI', "Blender Internal", "Switch to Blender Internal")
- ),
- default='CYCLES',
- )
-
- @classmethod
- def poll(cls, context):
- return c_is_cycles_addon_enabled()
-
- def execute(self, context):
- switch = "ON" if self.switcher else "OFF"
- AutoNodeSwitch(self.renderer, switch, self)
-
- return {'FINISHED'}
-
-
-def register():
- bpy.utils.register_module(__name__)
- pass
-
-
-def unregister():
- bpy.utils.unregister_module(__name__)
- pass
-
-
-if __name__ == "__main__":
- register()
diff --git a/materials_utils/texture_rename.py b/materials_utils/texture_rename.py
deleted file mode 100644
index 7ca26c4a..00000000
--- a/materials_utils/texture_rename.py
+++ /dev/null
@@ -1,127 +0,0 @@
-# gpl: author Yadoob
-# -*- coding: utf-8 -*-
-
-import bpy
-from bpy.types import (
- Operator,
- Panel,
-)
-from bpy.props import (
- BoolProperty,
- StringProperty,
-)
-from .warning_messages_utils import (
- warning_messages,
- c_data_has_images,
-)
-
-
-class TEXTURE_OT_patern_rename(Operator):
- bl_idname = "texture.patern_rename"
- bl_label = "Texture Renamer"
- bl_description = ("Replace the Texture names pattern with the attached Image ones\n"
- "Works on all Textures (Including Brushes)\n"
- "The First field - the name pattern to replace\n"
- "The Second - search for existing names")
- bl_options = {'REGISTER', 'UNDO'}
-
- def_name = "Texture" # default name
- is_not_undo = False # prevent drawing props on undo
-
- named: StringProperty(
- name="Search for name",
- description="Enter the name pattern or choose the one from the dropdown list below",
- default=def_name
- )
- replace_all: BoolProperty(
- name="Replace all",
- description="Replace all the Textures in the data with the names of the images attached",
- default=False
- )
-
- @classmethod
- def poll(cls, context):
- return c_data_has_images()
-
- def draw(self, context):
- layout = self.layout
- if not self.is_not_undo:
- layout.label(text="*Only Undo is available*", icon="INFO")
- return
-
- layout.prop(self, "replace_all")
-
- box = layout.box()
- box.enabled = not self.replace_all
- box.prop(self, "named", text="Name pattern", icon="SYNTAX_ON")
-
- box = layout.box()
- box.enabled = not self.replace_all
- box.prop_search(self, "named", bpy.data, "textures")
-
- def invoke(self, context, event):
- self.is_not_undo = True
- return context.window_manager.invoke_props_dialog(self)
-
- def check(self, context):
- return self.is_not_undo
-
- def execute(self, context):
- errors = [] # collect texture names without images attached
- tex_count = len(bpy.data.textures)
-
- for texture in bpy.data.textures:
- try:
- is_allowed = self.named in texture.name if not self.replace_all else True
- if texture and is_allowed and texture.type in {"IMAGE"}:
- textname = ""
- img = (bpy.data.textures[texture.name].image if bpy.data.textures[texture.name] else None)
- if not img:
- errors.append(str(texture.name))
- for word in img.name:
- if word != ".":
- textname = textname + word
- else:
- break
- texture.name = textname
- if texture.type != "IMAGE": # rename specific textures as clouds, environment map...
- texture.name = texture.type.lower()
- except:
- continue
-
- if tex_count == 0:
- warning_messages(self, 'NO_TEX_RENAME')
- elif errors:
- warning_messages(self, 'TEX_RENAME_F', errors, 'TEX')
-
- # reset name to default
- self.named = self.def_name
-
- self.is_not_undo = False
-
- return {'FINISHED'}
-
-
-class TEXTURE_PT_rename_panel(Panel):
- bl_label = "Texture Rename"
- bl_space_type = 'PROPERTIES'
- bl_region_type = 'WINDOW'
- bl_context = "texture"
-
- def draw(self, context):
- layout = self.layout
- layout.operator("texture.patern_rename")
-
-
-def register():
- bpy.utils.register_class(TEXTURE_OT_patern_rename)
- bpy.utils.register_class(TEXTURE_PT_rename_panel)
-
-
-def unregister():
- bpy.utils.unregister_class(TEXTURE_PT_rename_panel)
- bpy.utils.unregister_class(TEXTURE_OT_patern_rename)
-
-
-if __name__ == "__main__":
- register()
diff --git a/materials_utils/warning_messages_utils.py b/materials_utils/warning_messages_utils.py
deleted file mode 100644
index 1ce12d9d..00000000
--- a/materials_utils/warning_messages_utils.py
+++ /dev/null
@@ -1,181 +0,0 @@
-# gpl: author lijenstina
-# -*- coding: utf-8 -*-
-
-import bpy
-
-# Globals #
-
-# change the name for the properties settings
-MAT_SPEC_NAME = "materials_context_menu"
-
-# collect messages for the report operator
-COLLECT_REPORT = []
-
-
-# Functions
-
-def warning_messages(operator=None, warn='DEFAULT', object_name="", is_mat=None,
- fake="", override=False):
- # Enter warning messages to the message dictionary
- # warn - if nothing passed falls back to DEFAULT
- # a list of strings can be passed and concatenated in obj_name too
- # is_mat a switch to change to materials or textures for obj_name('MAT','TEX', 'FILE', None)
- # fake - optional string that can be passed
- # MAX_COUNT - max members of an list to be displayed in UI report
- # override - important messages that should be enabled, no matter the setting
-
- # pass the show_warnings bool to enable/disable them
- addon = bpy.context.preferences.addons[MAT_SPEC_NAME]
- get_warn = addon.preferences.show_warnings if addon else False
- show_warn = get_warn if override is False else override
-
- if show_warn and operator:
- obj_name = ""
- MAX_COUNT = 6
- gramma_s, gramma_p = " - has ", " - have "
-
- if is_mat:
- if is_mat in ('MAT'):
- gramma_s, gramma_p = " - Material has ", " - Materials have "
- elif is_mat in ('TEX'):
- gramma_s, gramma_p = " - Texture has ", " - Textures have "
- elif is_mat in ('FILE'):
- gramma_s, gramma_p = " - File ", " - Files "
-
- # print the whole list in the console if abbreviated
- obj_size_big = False
-
- if object_name:
- if type(object_name) is list:
- obj_name = ", ".join(object_name)
- obj_size = len(object_name)
-
- # compare string list size
- if (1 < obj_size <= MAX_COUNT):
- obj_name = "{}{}".format(obj_name, gramma_p)
- elif (obj_size > MAX_COUNT):
- abbrevation = ("(Multiple)" if is_mat else "(Multiple Objects)")
- obj_size_big = True
- obj_name = "{}{}".format(abbrevation, gramma_p)
- elif (obj_size == 1):
- obj_name = "{}{}".format(obj_name, gramma_s)
- else:
- obj_name = "{}{}".format(object_name, gramma_s)
-
- message = {
- 'DEFAULT': "No editable selected objects, could not finish",
- 'PLACEHOLDER': "{}{}".format(warn, " - Message key is not present in the warning_message_utils"),
- 'RMV_EDIT': "{}{}".format(obj_name, "Unable to remove material slot in edit mode)"),
- 'A_OB_MIX_NO_MAT': "{}{}".format(obj_name, "No Material applied. Object type can't have materials"),
- 'A_MAT_NAME_EDIT': "{}{}".format(obj_name, " been applied to selection"),
- 'C_OB_NO_MAT': "{}{}".format(obj_name, "No Materials. Unused material slots are "
- "not cleaned"),
- 'C_OB_MIX_NO_MAT': "{}{}".format(obj_name, "No Materials or an Object type that "
- "can't have Materials (Clean Material Slots)"),
- 'C_OB_MIX_SLOT_MAT': "{}{}".format(obj_name, "No Materials or only empty Slots are removed "
- "(Clean Material Slots)"),
- 'R_OB_NO_MAT': "{}{}".format(obj_name, "No Materials. Nothing to remove"),
- 'R_OB_FAIL_MAT': "{}{}".format(obj_name, "Failed to remove materials - (Operator Error)"),
- 'R_NO_SL_MAT': "No Selection. Material slots are not removed",
- 'R_ALL_SL_MAT': "All materials removed from selected objects",
- 'R_ALL_NO_MAT': "Object(s) have no materials to remove",
- 'R_ACT_MAT': "{}{}".format(obj_name, "Removed active Material"),
- 'R_ACT_MAT_ALL': "{}{}".format(obj_name, "Removed all Material from the Object"),
- 'SL_MAT_EDIT_BY_NAME': "{}{}{}".format("Geometry with the Material ", obj_name, "been selected"),
- 'SL_MAT_BY_NAME': "{}{}{}".format("Objects with the Material ", obj_name, "been selected"),
- 'OB_CANT_MAT': "{}{}".format(obj_name, "Object type that can't have Materials"),
- 'REP_MAT_NONE': "Replace Material: No materials replaced",
- 'FAKE_SET_ON': "{}{}{}".format(obj_name, "set Fake user ", fake),
- 'FAKE_SET_OFF': "{}{}{}".format(obj_name, "disabled Fake user ", fake),
- 'FAKE_NO_MAT': "Fake User Settings: Object(s) with no Materials or no changes needed",
- 'CPY_MAT_MIX_OB': "Copy Materials to others: Some of the Object types can't have Materials",
- 'CPY_MAT_ONE_OB': "Copy Materials to others: Only one object selected",
- 'CPY_MAT_FAIL': "Copy Materials to others: (Operator Error)",
- 'CPY_MAT_DONE': "Materials are copied from active to selected objects",
- 'TEX_MAT_NO_SL': "Texface to Material: No Selected Objects",
- 'TEX_MAT_NO_CRT': "{}{}".format(obj_name, "not met the conditions for the tool (UVs, Active Images)"),
- 'MAT_TEX_NO_SL': "Material to Texface: No Selected Objects",
- 'MAT_TEX_NO_MESH': "{}{}".format(obj_name, "not met the conditions for the tool (Mesh)"),
- 'MAT_TEX_NO_MAT': "{}{}".format(obj_name, "not met the conditions for the tool (Material)"),
- 'BI_SW_NODES_OFF': "Switching to Blender Render, Use Nodes disabled",
- 'BI_SW_NODES_ON': "Switching to Blender Render, Use Nodes enabled",
- 'CYC_SW_NODES_ON': "Switching back to Cycles, Use Nodes enabled",
- 'CYC_SW_NODES_OFF': "Switching back to Cycles, Use Nodes disabled",
- 'TEX_RENAME_F': "{}{}".format(obj_name, "no Images assigned, skipping"),
- 'NO_TEX_RENAME': "No Textures in Data, nothing to rename",
- 'DIR_PATH_W_ERROR': "ERROR: Directory without writing privileges",
- 'DIR_PATH_N_ERROR': "ERROR: Directory not existing",
- 'DIR_PATH_A_ERROR': "ERROR: Directory not accessible",
- 'DIR_PATH_W_OK': "Directory has writing privileges",
- 'DIR_PATH_CONVERT': "Conversion Cancelled. Problem with chosen Directory, check System Console",
- 'DIR_PATH_EMPTY': "File Path is empty. Please save the .blend file first",
- 'MAT_LINK_ERROR': "{}{}".format(obj_name, "not be renamed or set as Base(s)"),
- 'MAT_LINK_NO_NAME': "No Base name given, No changes applied",
- 'MOVE_SLOT_UP': "{}{}".format(obj_name, "been moved on top of the stack"),
- 'MOVE_SLOT_DOWN': "{}{}".format(obj_name, "been moved to the bottom of the stack"),
- 'MAT_TRNSP_BACK': "{}{}".format(obj_name, "been set with Alpha connected to Front/Back Geometry node"),
- 'E_MAT_TRNSP_BACK': "Transparent back (BI): Failure to set the action",
- 'CONV_NO_OBJ_MAT': "{}{}".format(obj_name, "has no Materials. Nothing to convert"),
- 'CONV_NO_SC_MAT': "No Materials in the Scene. Nothing to convert",
- 'CONV_NO_SEL_MAT': "No Materials on Selected Objects. Nothing to convert",
- }
-
- # doh! did we passed an non existing dict key
- warn = (warn if warn in message else 'PLACEHOLDER')
-
- operator.report({'INFO'}, message[warn])
-
- if obj_size_big is True:
- print("\n[Materials Utils Specials]:\nFull list for the Info message is:\n\n",
- " ".join(names + "," + "\n" * ((i + 1) % 10 == 0) for i, names in enumerate(object_name)),
- "\n")
-
- # restore settings if overridden
- if override:
- addon.preferences.show_warnings = get_warn
-
-
-def collect_report(collection="", is_start=False, is_final=False):
- # collection passes a string for appending to COLLECT_REPORT global
- # is_final switches to the final report with the operator in __init__
- global COLLECT_REPORT
- scene = bpy.context.scene.mat_context_menu
- use_report = scene.enable_report
-
- if is_start:
- # there was a crash somewhere before the is_final call
- COLLECT_REPORT = []
-
- if collection and type(collection) is str:
- if use_report:
- COLLECT_REPORT.append(collection)
- print(collection)
-
- if is_final and use_report:
- # final operator pass uses * as delimiter for splitting into new lines
- messages = "*".join(COLLECT_REPORT)
- bpy.ops.mat_converter.reports('INVOKE_DEFAULT', message=messages)
- COLLECT_REPORT = []
-
-
-def c_is_cycles_addon_enabled():
- # checks if Cycles is enabled thanks to ideasman42
- return ('cycles' in bpy.context.preferences.addons.keys())
-
-
-def c_data_has_materials():
- # check for material presence in data
- return (len(bpy.data.materials) > 0)
-
-
-def c_obj_data_has_materials(obj):
- # check for material presence in object's data
- matlen = 0
- if obj:
- matlen = len(obj.data.materials)
- return (matlen > 0)
-
-
-def c_data_has_images():
- # check for image presence in data
- return (len(bpy.data.images) > 0)