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:
Diffstat (limited to 'space_view3d_materials_utils.py')
-rw-r--r--space_view3d_materials_utils.py689
1 files changed, 689 insertions, 0 deletions
diff --git a/space_view3d_materials_utils.py b/space_view3d_materials_utils.py
new file mode 100644
index 00000000..16dd9489
--- /dev/null
+++ b/space_view3d_materials_utils.py
@@ -0,0 +1,689 @@
+#(c) 2010 Michael Williamson (michaelw)
+#ported from original by Michael Williamsn
+#
+#tested r28370
+#
+#
+# ##### 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 #####
+
+bl_addon_info = {
+ "name": "Material Utils",
+ "author": "michaelw",
+ "version": (1,3),
+ "blender": (2, 5, 3),
+ "api": 32411,
+ "location": "View3D > Q key",
+ "description": "Menu of material tools (assign, select by etc) in the 3D View",
+ "warning": "",
+ "wiki_url": "http://wiki.blender.org/index.php/Extensions:2.5/Py/"\
+ "Scripts/3D interaction/Materials Utils",
+ "tracker_url": "https://projects.blender.org/tracker/index.php?"\
+ "func=detail&aid=22140&group_id=153&atid=469",
+ "category": "3D View"}
+
+"""
+This script has several functions and operators... grouped for convenience
+* assign material:
+ offers the user a list of ALL the materials in the blend file and an additional "new" entry
+ the chosen material will be assigned to all the selected objects in object mode.
+
+ in edit mode the selected faces get the selected material applied.
+
+ if the user chose "new" the new material can be renamed using the "last operator" section of the toolbox
+ After assigning the material "clean material slots" and "material to texface" are auto run to keep things tidy (see description bellow)
+
+
+* select by material
+ in object mode this offers the user a menu of all materials in the blend file
+ any objects using the selected material will become selected, any objects without the material will be removed from selection.
+
+ in edit mode: the menu offers only the materials attached to the current object. It will select the faces that use the material and deselect those that do not.
+
+* clean material slots
+ for all selected objects any empty material slots or material slots with materials that are not used by the mesh faces will be removed.
+
+* Any un-used materials and slots will be removed
+"""
+
+
+import bpy
+from bpy.props import*
+
+
+def replace_material(m1 , m2, all_objects = False):
+ #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
+ try:
+ matorg = bpy.data.materials[m1]
+ matrep = bpy.data.materials[m2]
+
+
+ #store active object
+ scn = bpy.context.scene
+ ob_active = bpy.context.active_object
+
+ if all_objects:
+ objs = bpy.data.objects
+
+ else:
+ objs = bpy.context.selected_editable_objects
+
+ for ob in objs:
+ if ob.type == 'MESH':
+ scn.objects.active = ob
+ print(ob.name)
+ ms = ob.material_slots.values()
+
+ for m in ms:
+ if m.material == matorg:
+ m.material = matrep
+ #don't break the loop as the material can be
+ # ref'd more than once
+
+ #restore active object
+ scn.objects.active = ob_active
+ except:
+ print('no match to replace')
+
+def select_material_by_name(find_mat):
+ #in object mode selects all objects with material find_mat
+ #in edit mode selects all faces with material find_mat
+
+ #check for editmode
+ editmode = False
+
+ scn = bpy.context.scene
+ 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 ob.type == 'MESH':
+ ms = ob.material_slots.values()
+ for m in ms:
+ if m.material.name == find_mat:
+ ob.select = True
+ #the active object may not have the mat!
+ #set it to one that does!
+ scn.objects.active = ob
+ break
+ else:
+ ob.select = False
+
+ #deselect non-meshes
+ else:
+ ob.select = False
+
+ else:
+ #it's editmode, so select the faces
+ ob = actob
+ ms = ob.material_slots.values()
+
+ #same material can be on multiple slots
+ slot_indeces =[]
+ i = 0
+ found = False
+ for m in ms:
+ if m.material.name == find_mat:
+ slot_indeces.append(i)
+ found = True
+ i += 1
+ me = ob.data
+ for f in me.faces:
+ 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():
+ #assigns the first image in each material to the faces in the active uvlayer
+ #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()
+
+ for ob in bpy.context.selected_editable_objects:
+ #get the materials from slots
+ ms = ob.material_slots.values()
+
+ #build a list of images, one per material
+ images=[]
+ #get the textures from the mats
+ for m in ms:
+ gotimage = False
+ textures = m.material.texture_slots.values()
+ if len(textures) >= 1:
+ for t in textures:
+ if t != None:
+ tex = t.texture
+ if tex.type == 'IMAGE':
+ img = tex.image
+ images.append(img)
+ gotimage =True
+ break
+
+ if not gotimage:
+ print('noimage on', m.name)
+ images.append(None)
+
+ #now we have the images
+ #applythem 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 uvlayer
+ for t in me.uv_textures:
+ if t.active:
+ uvtex = t.data.values()
+ for f in me.faces:
+ #check that material had an image!
+ if images[f.material_index] != None:
+ uvtex[f.index].image = images[f.material_index]
+ uvtex[f.index].use_image = True
+ else:
+ uvtex[f.index].use_image = False
+
+ me.update()
+
+
+ if editmode:
+ bpy.ops.object.mode_set(mode = 'EDIT')
+
+
+
+def assignmatslots(ob, matlist):
+ #given an object and a list of material names
+ #removes all material slots form 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:
+ bpy.ops.object.material_slot_remove()
+
+
+ #re-add them and assign material
+ i = 0
+ for m in matlist:
+ mat = bpy.data.materials[m]
+ bpy.ops.object.material_slot_add()
+ ob.material_slots.values()[i].material = mat
+ i += 1
+
+ #restore active object:
+ scn.objects.active = ob_active
+
+
+def cleanmatslots():
+ #check for edit mode
+ editmode = False
+ actob = bpy.context.active_object
+ if actob.mode == 'EDIT':
+ editmode =True
+ bpy.ops.object.mode_set()
+
+
+ objs = bpy.context.selected_editable_objects
+
+ for ob in objs:
+ print(ob.name)
+ mats = ob.material_slots.keys()
+
+ #check the faces on the mesh to build a list of used materials
+ usedMatIndex =[] #we'll store used materials indices here
+ faceMats =[]
+ me = ob.data
+ for f in me.faces:
+ #get the material index for this face...
+ faceindex = f.material_index
+
+ #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 = 0
+ for m in usedMatIndex:
+ if m == faceindex:
+ found = 1
+ #break
+
+ if found == 0:
+ #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:
+ 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.faces:
+ matindex = mnames.index(faceMats[i])
+ f.material_index = matindex
+ i += 1
+ print('Done')
+ if editmode:
+ bpy.ops.object.mode_set(mode = 'EDIT')
+
+
+
+
+
+def assign_mat(matname="Default"):
+ #get active object so we can restore it later
+ actob = bpy.context.active_object
+
+ #check if material exists, if it doesn't then create it
+ mats =bpy.data.materials
+ found = False
+ for m in mats:
+ if m.name == matname:
+ target = m
+ found = True
+ break
+ if not found:
+ target = bpy.data.materials.new(matname)
+
+
+ #if objectmodeset all faces
+ editmode = False
+ allfaces = True
+ if actob.mode == 'EDIT':
+ editmode =True
+ allfaces = False
+ bpy.ops.object.mode_set()
+
+ objs = bpy.context.selected_editable_objects
+
+ for ob in objs:
+ #set the active object to our object
+ scn = bpy.context.scene
+ scn.objects.active = ob
+
+
+ #check if the material is on the object already
+ if 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
+ #so attach it!
+
+ #add a material slot
+ bpy.ops.object.material_slot_add()
+
+ #make slot active
+ ob.active_material_index = i
+
+ #and assign material to slot
+ ob.material_slots.values()[i].material = target
+ #now assign the material:
+ me =ob.data
+ if allfaces:
+ for f in me.faces:
+ f.material_index = index
+ elif allfaces == False:
+ for f in me.faces:
+ if f.select:
+ f.material_index = index
+ me.update
+
+ #restore the active object
+ bpy.context.scene.objects.active = actob
+ if editmode:
+ bpy.ops.object.mode_set(mode = 'EDIT')
+
+
+
+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():
+ # editmode 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 = []
+
+ # get the texface images and store indices
+ if (ob.data.uv_textures):
+ 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)
+
+
+
+ #check materials for images exist; create if needed
+ matlist = []
+ for i in unique_images:
+ if i:
+ print(i.name)
+ 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 != None:
+ me.faces[i].material_index = f
+ i += 1
+ if editmode:
+ bpy.ops.object.mode_set(mode = 'EDIT')
+
+
+#operator classes:
+#---------------------------------------------------------------------
+
+class VIEW3D_OT_texface_to_material(bpy.types.Operator):
+ ''''''
+ bl_idname = "texface_to_material"
+ bl_label = "MW Texface Images to Material/Texture"
+ bl_options = {'REGISTER', 'UNDO'}
+
+ @classmethod
+ def poll(cls, context):
+ return context.active_object != None
+
+ def execute(self, context):
+ if context.selected_editable_objects:
+ texface_to_mat()
+ return {'FINISHED'}
+ else:
+ self.report({'WARNING'}, "No editable selected objects, could not finish")
+ return {'CANCELLED'}
+
+class VIEW3D_OT_assign_material(bpy.types.Operator):
+ '''assign a material to the selection'''
+ bl_idname = "assign_material"
+ bl_label = "MW Assign Material"
+ bl_options = {'REGISTER', 'UNDO'}
+
+ matname = StringProperty(name = 'Material Name',
+ description = 'Name of Material to Assign',
+ default = "", maxlen = 21)
+
+ @classmethod
+ def poll(cls, context):
+ return context.active_object != None
+
+ def execute(self, context):
+ mn = self.matname
+ print(mn)
+ assign_mat(mn)
+ cleanmatslots()
+ mat_to_texface()
+ return {'FINISHED'}
+
+class VIEW3D_OT_clean_material_slots(bpy.types.Operator):
+ '''removes any material slots from the
+ selected objects that are not used by the mesh'''
+ bl_idname = "clean_material_slots"
+ bl_label = "MW Clean Material Slots"
+ bl_options = {'REGISTER', 'UNDO'}
+
+ @classmethod
+ def poll(cls, context):
+ return context.active_object != None
+
+ def execute(self, context):
+ cleanmatslots()
+ return {'FINISHED'}
+
+class VIEW3D_OT_material_to_texface(bpy.types.Operator):
+ ''''''
+ bl_idname = "material_to_texface"
+ bl_label = "MW Material Images to Texface"
+ bl_options = {'REGISTER', 'UNDO'}
+
+ @classmethod
+ def poll(cls, context):
+ return context.active_object != None
+
+ def execute(self, context):
+ mat_to_texface()
+ return {'FINISHED'}
+
+class VIEW3D_OT_select_material_by_name(bpy.types.Operator):
+ ''''''
+ bl_idname = "select_material_by_name"
+ bl_label = "MW Select Material By Name"
+ bl_options = {'REGISTER', 'UNDO'}
+ matname = StringProperty(name = 'Material Name',
+ description = 'Name of Material to Select',
+ default = "", maxlen = 21)
+
+ @classmethod
+ def poll(cls, context):
+ return context.active_object != None
+
+ def execute(self, context):
+ mn = self.matname
+ select_material_by_name(mn)
+ return {'FINISHED'}
+
+
+class VIEW3D_OT_replace_material(bpy.types.Operator):
+ '''assign a material to the selection'''
+ bl_idname = "replace_material"
+ bl_label = "MW Replace Material"
+ bl_options = {'REGISTER', 'UNDO'}
+
+ matorg = StringProperty(name = 'Material to Replace',
+ description = 'Name of Material to Assign',
+ default = "", maxlen = 21)
+
+ matrep = StringProperty(name = 'Replacement material',
+ description = 'Name of Material to Assign',
+ default = "", maxlen = 21)
+
+ all_objects = BoolProperty(name ='all_objects',
+ description="replace for all objects in this blend file",
+ default = True)
+
+ @classmethod
+ def poll(cls, context):
+ return context.active_object != None
+
+ def execute(self, context):
+ m1 = self.matorg
+ m2 = self.matrep
+ all = self.all_objects
+ replace_material(m1,m2,all)
+ return {'FINISHED'}
+
+#menu classes
+#-------------------------------------------------------------------------------
+class VIEW3D_MT_master_material(bpy.types.Menu):
+ bl_label = "Master Material Menu"
+
+ def draw(self, context):
+ layout = self.layout
+ layout.operator_context = 'INVOKE_REGION_WIN'
+
+ layout.menu("VIEW3D_MT_assign_material", icon='ZOOMIN')
+ layout.menu("VIEW3D_MT_select_material", icon='HAND')
+ layout.separator()
+ layout.operator("clean_material_slots",
+ text = 'Clean Material Slots', icon='CANCEL')
+ layout.operator("material_to_texface",
+ text = 'Material to Texface',icon='FACESEL_HLT')
+ layout.operator("texface_to_material",
+ text = 'Texface to Material',icon='FACESEL_HLT')
+
+ layout.separator()
+ layout.operator("replace_material",
+ text = 'Replace Material', icon='ARROW_LEFTRIGHT')
+
+
+
+class VIEW3D_MT_assign_material(bpy.types.Menu):
+ bl_label = "Assign Material"
+
+ def draw(self, context):
+ layout = self.layout
+ layout.operator_context = 'INVOKE_REGION_WIN'
+
+ ob = context
+ layout.label
+ for i in range (len(bpy.data.materials)):
+
+ layout.operator("assign_material",
+ text=bpy.data.materials[i].name,
+ icon='MATERIAL_DATA').matname = bpy.data.materials[i].name
+
+ layout.operator("assign_material",text="Add New",
+ icon='ZOOMIN')
+
+class VIEW3D_MT_select_material(bpy.types.Menu):
+ bl_label = "Select by Material"
+
+ def draw(self, context):
+ layout = self.layout
+ layout.operator_context = 'INVOKE_REGION_WIN'
+
+ ob = context.object
+ layout.label
+ if ob.mode == 'OBJECT':
+ #show all materials in entire blend file
+ for i in range (len(bpy.data.materials)):
+
+ layout.operator("select_material_by_name",
+ text=bpy.data.materials[i].name,
+ icon='MATERIAL_DATA').matname = bpy.data.materials[i].name
+
+
+ elif ob.mode == 'EDIT':
+ #show only the materials on this object
+ mats = ob.material_slots.keys()
+ for m in mats:
+ layout.operator("select_material_by_name",
+ text=m,
+ icon='MATERIAL_DATA').matname = m
+
+
+def register():
+ km = bpy.context.window_manager.keyconfigs.default.keymaps['3D View']
+ kmi = km.items.new('wm.call_menu', 'Q', 'PRESS')
+ kmi.properties.name = "VIEW3D_MT_master_material"
+
+def unregister():
+ km = bpy.context.window_manager.keyconfigs.default.keymaps['3D View']
+ for kmi in km.items:
+ if kmi.idname == 'wm.call_menu':
+ if kmi.properties.name == "VIEW3D_MT_master_material":
+ km.items.remove(kmi)
+ break
+
+if __name__ == "__main__":
+ register()