[Bf-extensions-cvs] SVN commit: /data/svn/bf-extensions [632] contrib/py/scripts/addons/ add_corrective_shape_key.py: contrib/py/scripts/addons/ add_corrective_shape_key.py

Brendon Murphy meta.androcto1 at gmail.com
Thu Apr 22 23:53:15 CEST 2010


Revision: 632
          http://projects.blender.org/plugins/scmsvn/viewcvs.php?view=rev&root=bf-extensions&revision=632
Author:   meta-androcto
Date:     2010-04-22 23:53:15 +0200 (Thu, 22 Apr 2010)

Log Message:
-----------
contrib/py/scripts/addons/add_corrective_shape_key.py
added to contrib

Added Paths:
-----------
    contrib/py/scripts/addons/add_corrective_shape_key.py

Added: contrib/py/scripts/addons/add_corrective_shape_key.py
===================================================================
--- contrib/py/scripts/addons/add_corrective_shape_key.py	                        (rev 0)
+++ contrib/py/scripts/addons/add_corrective_shape_key.py	2010-04-22 21:53:15 UTC (rev 632)
@@ -0,0 +1,455 @@
+# This script transfer the shape from an object (base mesh without
+# modifiers) to another object with modifiers (i.e. posed Armature).
+# Only two objects must be selected.
+# The first selected object will be added to the second selected
+# object as a new shape key.
+#
+# Original 2.4x script by ? (brecht?)
+# Unpose-function reused from a script by Tal Trachtman in 2007 http://www.apexbow.com/randd.html
+# Converted to Blender 2.5 by Ivo Grigull
+
+# blender 1 line description
+""
+
+bl_addon_info = {
+    'name': 'Corrective shape keys',
+    'author': 'Ivo Grigull, Tal Trachtman',
+    'version': '1.0',
+    'blender': (2, 5, 3),
+    'location': 'Object Data > Shape Keys (Search: corrective)',
+    'description': 'transfer shape from object w/out modifier to object with modifier',
+    'url': 'http://wiki.blender.org/index.php/Extensions:2.5/Py/' \
+	    'Scripts/Animation/Corrective_Shape_Key',
+    'category': 'Animation'}
+
+
+# ##### 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 #####
+
+
+import bpy
+import mathutils
+
+
+iterations = 20
+threshold = 1e-6
+
+
+
+# flips rotation matrix
+def flip_matrix_direction(m):
+	mat = mathutils.Matrix()
+	
+	mat[0][0] = m[0][0]
+	mat[0][1] = m[1][0]
+	mat[0][2] = m[2][0]
+	
+	mat[1][0] = m[0][1]
+	mat[1][1] = m[1][1]
+	mat[1][2] = m[2][1]
+	
+	mat[2][0] = m[0][2]
+	mat[2][1] = m[1][2]
+	mat[2][2] = m[2][2]
+	
+	return mat 
+
+# this version is for shape_key data
+def extractX(ob, mesh):
+	x = []
+	
+	for i in range(0, len(mesh)):
+		v = mesh[i]
+		x += [mathutils.Vector(v.co)]
+	
+	return x
+
+# this version is for mesh data
+def extractX_2(ob, mesh):
+	x = []
+	
+	for i in range(0, len(mesh.verts)):
+		v = mesh.verts[i]
+		x += [mathutils.Vector(v.co)]
+	
+	return x
+
+def extractMappedX(ob, mesh):
+	totvert = len(mesh)
+	
+	mesh = ob.create_mesh( bpy.context.scene, True, 'PREVIEW' )
+
+	x = []
+
+	# cheating, the original mapped verts happen
+	# to be at the end of the vertex array
+	for i in range(len(mesh.verts)-totvert, len(mesh.verts)):
+		v = mesh.verts[i]
+		x += [mathutils.Vector(v.co)]
+
+	mesh.user_clear()
+	bpy.data.meshes.remove(mesh)    
+	
+	return x
+
+def applyX(ob, mesh, x ):
+	for i in range(0, len(mesh)):
+		v = mesh[i]
+		v.co = x[i]
+	
+	ob.data.update()
+	
+	return x
+
+
+def func_add_corrective_pose_shape( source, target):
+	
+	ob_1   = target
+	mesh_1 = target.data
+	ob_2   = source
+	mesh_2 = source.data
+
+	# If target object doesn't have Basis shape key, create it.
+	try:
+		num_keys = len( mesh_1.shape_keys.keys )
+	except:
+		basis = ob_1.add_shape_key()
+		basis.name = "Basis"
+		ob_1.data.update()
+		
+	
+	key_index = ob_1.active_shape_key_index
+	# Insert new shape key
+	if key_index == 0:
+		new_shapekey = ob_1.add_shape_key()
+		new_shapekey.name = "Shape_" + ob_2.name
+		new_shapekey_name = new_shapekey.name
+		
+		key_index = len(mesh_1.shape_keys.keys)-1
+		ob_1.active_shape_key_index = key_index
+		
+	# else, the active shape will be used (updated)
+				
+	ob_1.shape_key_lock = True
+	
+	mesh_1_key_verts = mesh_1.shape_keys.keys[ key_index ].data
+	
+	
+	x = extractX(ob_1, mesh_1_key_verts)
+	
+	targetx = extractX_2(ob_2, mesh_2)
+	
+	for iteration in range(0, iterations):
+		dx = [[], [], [], [], [], []]
+	
+		mapx = extractMappedX(ob_1, mesh_1_key_verts)
+		
+		# finite differencing in X/Y/Z to get approximate gradient
+		for i in range(0, len(mesh_1.verts)):
+			epsilon = (targetx[i] - mapx[i]).length
+			
+			if epsilon < threshold:
+				epsilon = 0.0
+			
+			dx[0] += [x[i] + 0.5*epsilon*mathutils.Vector([1, 0, 0])]
+			dx[1] += [x[i] + 0.5*epsilon*mathutils.Vector([-1, 0, 0])]
+			dx[2] += [x[i] + 0.5*epsilon*mathutils.Vector([0, 1, 0])]
+			dx[3] += [x[i] + 0.5*epsilon*mathutils.Vector([0, -1, 0])]
+			dx[4] += [x[i] + 0.5*epsilon*mathutils.Vector([0, 0, 1])]
+			dx[5] += [x[i] + 0.5*epsilon*mathutils.Vector([0, 0, -1])]
+	
+		for j in range(0, 6):
+			applyX(ob_1, mesh_1_key_verts, dx[j] )
+			dx[j] = extractMappedX(ob_1, mesh_1_key_verts)
+	
+		# take a step in the direction of the gradient
+		for i in range(0, len(mesh_1.verts)):
+			epsilon = (targetx[i] - mapx[i]).length
+			
+			if epsilon >= threshold:
+				Gx = list((dx[0][i] - dx[1][i])/epsilon)
+				Gy = list((dx[2][i] - dx[3][i])/epsilon)
+				Gz = list((dx[4][i] - dx[5][i])/epsilon)
+				G = mathutils.Matrix(Gx, Gy, Gz)
+				G = flip_matrix_direction(G)
+	
+				x[i] += G*(targetx[i] - mapx[i])
+		
+		applyX(ob_1, mesh_1_key_verts, x )
+	
+	
+	# set the new shape key value to 1.0, so we see the result instantly
+	mesh_1.shape_keys.keys[ob_1.active_shape_key_index].value = 1.0
+	
+	#mesh_1.update()
+	ob_1.shape_key_lock = False
+	
+
+class add_corrective_pose_shape(bpy.types.Operator):	
+	'''Adds first object as shape to second object for the current pose while maintaining modifiers (i.e. anisculpt, avoiding crazy space) Beware of slowness!!!'''
+	
+	bl_idname = "object.add_corrective_pose_shape"
+	bl_label = "Add object as corrective pose shape"
+
+	def poll(self, context):
+		return context.active_object != None
+
+	def execute(self, context):
+	
+		if len(context.selected_objects) > 2:
+			print("Select source and target objects please")
+			return {'FINISHED'}
+
+		selection = context.selected_objects
+		target = context.active_object
+		if context.active_object == selection[0]:
+			source = selection[1]
+		else:
+			source = selection[0]
+
+		print(source)
+		print(target)
+		func_add_corrective_pose_shape( source, target)
+
+		return {'FINISHED'}
+
+def func_object_duplicate_flatten_modifiers(ob, scene):
+	mesh = ob.create_mesh( bpy.context.scene, True, 'PREVIEW' )
+	name = ob.name + "_clean"
+	new_object = bpy.data.objects.new( name, mesh)
+	new_object.data = mesh
+	scene.objects.link(new_object)
+	return new_object
+
+class object_duplicate_flatten_modifiers(bpy.types.Operator):   
+	'''Duplicates the selected object with modifiers applied'''
+	
+	bl_idname = "object.object_duplicate_flatten_modifiers"
+	bl_label = "Duplicate and apply all"
+
+	def poll(self, context):
+		return context.active_object != None
+
+	def execute(self, context):
+		new_object = func_object_duplicate_flatten_modifiers( context.active_object, context.scene )
+		context.scene.objects.active = new_object
+
+		for n in bpy.data.objects:
+			if n != new_object:
+				n.selected = False
+			else:
+				n.selected = True
+		return {'FINISHED'}
+
+
+
+
+def flip_matrix_direction_4x4(m):
+	mat = mathutils.Matrix()
+	
+	mat[0][0] = m[0][0]
+	mat[0][1] = m[1][0]
+	mat[0][2] = m[2][0]
+	mat[0][3] = m[3][0]
+	
+	mat[1][0] = m[0][1]
+	mat[1][1] = m[1][1]
+	mat[1][2] = m[2][1]
+	mat[1][3] = m[3][1]
+	
+	mat[2][0] = m[0][2]
+	mat[2][1] = m[1][2]
+	mat[2][2] = m[2][2]
+	mat[2][3] = m[3][2]
+	
+	mat[3][0] = m[0][3]
+	mat[3][1] = m[1][3]
+	mat[3][2] = m[2][3]
+	mat[3][3] = m[3][3]
+	return mat 
+
+	
+def unposeMesh(meshObToUnpose, meshObToUnposeWeightSrc, armatureOb):
+	psdMeshData = meshObToUnpose
+
+	psdMesh = psdMeshData
+	I = mathutils.Matrix() #identity matrix
+	
+	meshData = meshObToUnposeWeightSrc.data
+	mesh = meshData
+	
+	armData = armatureOb.data
+
+	pose = armatureOb.pose
+	pbones =  pose.bones
+	
+
+	for index, v in enumerate(mesh.verts):
+		# above is python shortcut for:index goes up from 0 to tot num of verts in mesh,
+		# with index incrementing by 1 each iteration
+		
+		psdMeshVert = psdMesh[index]
+
+		listOfBoneNameWeightPairs = []
+		for n in mesh.verts[index].groups:		  
+			try:
+				name = meshObToUnposeWeightSrc.vertex_groups[n.group].name
+				weight = n.weight
+				listOfBoneNameWeightPairs.append( [name, weight] )
+			except:
+				pass
+
+		weightedAverageDictionary = {}
+		totalWeight = 0
+		for pair in listOfBoneNameWeightPairs:
+			totalWeight += pair[1]
+
+		for pair in listOfBoneNameWeightPairs:
+			if (totalWeight>0): #avoid divide by zero!
+				weightedAverageDictionary[pair[0]] = pair[1]/totalWeight
+			else:
+				weightedAverageDictionary[pair[0]] = 0
+				
+		sigma = mathutils.Matrix(I-I) #Matrix filled with zeros
+		
+		list = []
+		for n in pbones:
+			list.append(n)
+		list.reverse()
+
+		for pbone in list:
+			if pbone.name in weightedAverageDictionary:
+				vertexWeight = weightedAverageDictionary[pbone.name]
+				m = pbone.matrix_channel.copy()
+				m = flip_matrix_direction_4x4(m)
+				sigma = sigma + (m - I) * vertexWeight
+				
+			else:
+				pass
+				#print("no key for bone " + pbone.name)
+
+		sigma = I + sigma
+		sigma.invert()
+		psdMeshVert.co = psdMeshVert.co * sigma
+
+
+
+def func_add_corrective_pose_shape_fast(source, target):
+	# If target object doesn't have Basis shape key, create it.
+	try:
+		num_keys = len( target.data.shape_keys.keys )
+	except:
+		basis = target.add_shape_key()
+		basis.name = "Basis"
+		target.data.update()
+	
+	key_index = target.active_shape_key_index
+
+	if key_index == 0:
+		
+		# Insert new shape key
+		new_shapekey = target.add_shape_key()
+		new_shapekey.name = "Shape_" + source.name
+		new_shapekey_name = new_shapekey.name
+		
+		key_index = len(target.data.shape_keys.keys)-1
+		target.active_shape_key_index = key_index
+		
+	# else, the active shape will be used (updated)
+		
+	target.shape_key_lock = True
+	
+	shape_key_verts = target.data.shape_keys.keys[ key_index ].data
+
+	# copy the local vertex positions to the new shape
+	verts = source.data.verts
+	for n in range( len(verts)):
+		shape_key_verts[n].co = verts[n].co
+
+	# go to all armature modifies and unpose the shape
+	for n in target.modifiers:
+		if n.type == 'ARMATURE' and n.realtime:

@@ Diff output truncated at 10240 characters. @@



More information about the Bf-extensions-cvs mailing list