1 # ##### BEGIN GPL LICENSE BLOCK #####
3 # This program is free software; you can redistribute it and/or
4 # modify it under the terms of the GNU General Public License
5 # as published by the Free Software Foundation; either version 2
6 # of the License, or (at your option) any later version.
8 # This program is distributed in the hope that it will be useful,
9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # GNU General Public License for more details.
13 # You should have received a copy of the GNU General Public License
14 # along with this program; if not, write to the Free Software Foundation,
15 # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 # ##### END GPL LICENSE BLOCK #####
22 "name": "Corrective shape keys",
23 "author": "Ivo Grigull (loolarge), Tal Trachtman",
25 "blender": (2, 57, 0),
26 "location": "Object Data > Shape Keys (Search: corrective) ",
27 "description": "Creates a corrective shape key for the current pose",
28 "wiki_url": "http://wiki.blender.org/index.php/Extensions:2.6/Py/"
29 "Scripts/Animation/Corrective_Shape_Key",
30 "category": "Animation",
34 This script transfer the shape from an object (base mesh without
35 modifiers) to another object with modifiers (i.e. posed Armature).
36 Only two objects must be selected.
37 The first selected object will be added to the second selected
38 object as a new shape key.
40 - Original 2.4x script by Brecht
41 - Unpose-function reused from a script by Tal Trachtman in 2007
42 http://www.apexbow.com/randd.html
43 - Converted to Blender 2.5 by Ivo Grigull
46 - Target mesh may not have any transformation at object level,
47 it will be set to zero.
48 - Fast/Armature method does not work with Bone envelopes or dual quaternions,
49 both settings will be disabled in the modifier
54 from mathutils
import Vector
, Matrix
61 def reset_transform(ob
):
62 ob
.matrix_local
.identity()
65 # this version is for shape_key data
66 def extract_vert_coords(ob
, verts
):
67 return [v
.co
.copy() for v
in verts
]
70 def extract_mapped_coords(ob
, shape_verts
):
71 totvert
= len(shape_verts
)
73 mesh
= ob
.to_mesh(bpy
.context
.scene
, True, 'PREVIEW')
75 # cheating, the original mapped verts happen
76 # to be at the end of the vertex array
78 arr
= [verts
[i
].co
.copy() for i
in range(len(verts
) - totvert
, len(verts
))]
81 bpy
.data
.meshes
.remove(mesh
)
86 def apply_vert_coords(ob
, mesh
, x
):
87 for i
, v
in enumerate(mesh
):
92 def func_add_corrective_pose_shape(source
, target
):
99 reset_transform(target
)
101 # If target object doesn't have Basis shape key, create it.
102 if not mesh_1
.shape_keys
:
103 basis
= ob_1
.shape_key_add()
107 key_index
= ob_1
.active_shape_key_index
108 # Insert new shape key
110 new_shapekey
= ob_1
.shape_key_add()
111 new_shapekey
.name
= "Shape_" + ob_2
.name
113 key_index
= len(mesh_1
.shape_keys
.key_blocks
) - 1
114 ob_1
.active_shape_key_index
= key_index
116 # else, the active shape will be used (updated)
118 ob_1
.show_only_shape_key
= True
120 vgroup
= ob_1
.active_shape_key
.vertex_group
121 ob_1
.active_shape_key
.vertex_group
= ""
123 mesh_1_key_verts
= mesh_1
.shape_keys
.key_blocks
[key_index
].data
125 x
= extract_vert_coords(ob_1
, mesh_1_key_verts
)
127 targetx
= extract_vert_coords(ob_2
, mesh_2
.vertices
)
129 for iteration
in range(0, iterations
):
130 dx
= [[], [], [], [], [], []]
132 mapx
= extract_mapped_coords(ob_1
, mesh_1_key_verts
)
134 # finite differencing in X/Y/Z to get approximate gradient
135 for i
in range(0, len(mesh_1
.vertices
)):
136 epsilon
= (targetx
[i
] - mapx
[i
]).length
138 if epsilon
< threshold
:
141 dx
[0] += [x
[i
] + 0.5 * epsilon
* Vector((1, 0, 0))]
142 dx
[1] += [x
[i
] + 0.5 * epsilon
* Vector((-1, 0, 0))]
143 dx
[2] += [x
[i
] + 0.5 * epsilon
* Vector((0, 1, 0))]
144 dx
[3] += [x
[i
] + 0.5 * epsilon
* Vector((0, -1, 0))]
145 dx
[4] += [x
[i
] + 0.5 * epsilon
* Vector((0, 0, 1))]
146 dx
[5] += [x
[i
] + 0.5 * epsilon
* Vector((0, 0, -1))]
148 for j
in range(0, 6):
149 apply_vert_coords(ob_1
, mesh_1_key_verts
, dx
[j
])
150 dx
[j
] = extract_mapped_coords(ob_1
, mesh_1_key_verts
)
152 # take a step in the direction of the gradient
153 for i
in range(0, len(mesh_1
.vertices
)):
154 epsilon
= (targetx
[i
] - mapx
[i
]).length
156 if epsilon
>= threshold
:
157 Gx
= list((dx
[0][i
] - dx
[1][i
]) / epsilon
)
158 Gy
= list((dx
[2][i
] - dx
[3][i
]) / epsilon
)
159 Gz
= list((dx
[4][i
] - dx
[5][i
]) / epsilon
)
160 G
= Matrix((Gx
, Gy
, Gz
))
161 x
[i
] += G
* (targetx
[i
] - mapx
[i
])
163 apply_vert_coords(ob_1
, mesh_1_key_verts
, x
)
165 ob_1
.active_shape_key
.vertex_group
= vgroup
167 # set the new shape key value to 1.0, so we see the result instantly
168 ob_1
.active_shape_key
.value
= 1.0
171 ob_1
.show_only_shape_key
= False
174 class add_corrective_pose_shape(bpy
.types
.Operator
):
175 """Adds first object as shape to second object for the current pose """ \
176 """while maintaining modifiers """ \
177 """(i.e. anisculpt, avoiding crazy space) Beware of slowness!"""
179 bl_idname
= "object.add_corrective_pose_shape"
180 bl_label
= "Add object as corrective pose shape"
183 def poll(cls
, context
):
184 return context
.active_object
is not None
186 def execute(self
, context
):
187 selection
= context
.selected_objects
188 if len(selection
) != 2:
189 self
.report({'ERROR'}, "Select source and target objects")
192 target
= context
.active_object
193 if context
.active_object
== selection
[0]:
194 source
= selection
[1]
196 source
= selection
[0]
198 func_add_corrective_pose_shape(source
, target
)
203 def func_object_duplicate_flatten_modifiers(scene
, obj
):
204 mesh
= obj
.to_mesh(scene
, True, 'PREVIEW')
205 name
= obj
.name
+ "_clean"
206 new_object
= bpy
.data
.objects
.new(name
, mesh
)
207 new_object
.data
= mesh
208 scene
.objects
.link(new_object
)
212 class object_duplicate_flatten_modifiers(bpy
.types
.Operator
):
213 """Duplicates the selected object with modifiers applied"""
215 bl_idname
= "object.object_duplicate_flatten_modifiers"
216 bl_label
= "Duplicate and apply all"
219 def poll(cls
, context
):
220 return context
.active_object
is not None
222 def execute(self
, context
):
223 scene
= context
.scene
224 obj_act
= context
.active_object
226 new_object
= func_object_duplicate_flatten_modifiers(scene
, obj_act
)
229 bpy
.ops
.object.select_all(action
='DESELECT')
231 scene
.objects
.active
= new_object
232 new_object
.select
= True
237 def unposeMesh(meshObToUnpose
, meshObToUnposeWeightSrc
, armatureOb
):
238 psdMeshData
= meshObToUnpose
240 psdMesh
= psdMeshData
241 I
= Matrix() # identity matrix
243 meshData
= meshObToUnposeWeightSrc
.data
246 armData
= armatureOb
.data
248 pose
= armatureOb
.pose
251 for index
, v
in enumerate(mesh
.vertices
):
252 # above is python shortcut for:index goes up from 0 to tot num of
253 # verts in mesh, with index incrementing by 1 each iteration
255 psdMeshVert
= psdMesh
[index
]
257 listOfBoneNameWeightPairs
= []
258 for n
in mesh
.vertices
[index
].groups
:
260 name
= meshObToUnposeWeightSrc
.vertex_groups
[n
.group
].name
263 for i
in armData
.bones
:
267 # ignore non-bone vertex groups
269 listOfBoneNameWeightPairs
.append([name
, weight
])
274 weightedAverageDictionary
= {}
276 for pair
in listOfBoneNameWeightPairs
:
277 totalWeight
+= pair
[1]
279 for pair
in listOfBoneNameWeightPairs
:
280 if totalWeight
> 0: # avoid divide by zero!
281 weightedAverageDictionary
[pair
[0]] = pair
[1] / totalWeight
283 weightedAverageDictionary
[pair
[0]] = 0
285 # Matrix filled with zeros
295 if pbone
.name
in weightedAverageDictionary
:
296 #~ print("found key %s", pbone.name)
297 vertexWeight
= weightedAverageDictionary
[pbone
.name
]
298 m
= pbone
.matrix_channel
.copy()
300 sigma
+= (m
- I
) * vertexWeight
304 #~ print("no key for bone " + pbone.name)
308 psdMeshVert
.co
= psdMeshVert
.co
* sigma
311 def func_add_corrective_pose_shape_fast(source
, target
):
313 reset_transform(target
)
315 # If target object doesn't have Basis shape key, create it.
316 if not target
.data
.shape_keys
:
317 basis
= target
.shape_key_add()
321 key_index
= target
.active_shape_key_index
325 # Insert new shape key
326 new_shapekey
= target
.shape_key_add()
327 new_shapekey
.name
= "Shape_" + source
.name
329 key_index
= len(target
.data
.shape_keys
.key_blocks
) - 1
330 target
.active_shape_key_index
= key_index
332 # else, the active shape will be used (updated)
334 target
.show_only_shape_key
= True
336 shape_key_verts
= target
.data
.shape_keys
.key_blocks
[key_index
].data
339 vgroup
= target
.active_shape_key
.vertex_group
340 target
.active_shape_key
.vertex_group
= ''
344 # copy the local vertex positions to the new shape
345 verts
= source
.data
.vertices
346 for n
in range(len(verts
)):
347 shape_key_verts
[n
].co
= verts
[n
].co
349 # go to all armature modifies and unpose the shape
350 for n
in target
.modifiers
:
351 if n
.type == 'ARMATURE' and n
.show_viewport
:
353 n
.use_bone_envelopes
= False
354 n
.use_deform_preserve_volume
= False
355 n
.use_vertex_groups
= True
357 unposeMesh(shape_key_verts
, target
, armature
)
360 # set the new shape key value to 1.0, so we see the result instantly
361 target
.active_shape_key
.value
= 1.0
364 target
.active_shape_key
.vertex_group
= vgroup
368 target
.show_only_shape_key
= False
372 class add_corrective_pose_shape_fast(bpy
.types
.Operator
):
373 """Adds 1st object as shape to 2nd object as pose shape
376 bl_idname
= "object.add_corrective_pose_shape_fast"
377 bl_label
= "Add object as corrective shape faster"
380 def poll(cls
, context
):
381 return context
.active_object
is not None
383 def execute(self
, context
):
384 selection
= context
.selected_objects
385 if len(selection
) != 2:
386 self
.report({'ERROR'}, "Select source and target objects")
389 target
= context
.active_object
390 if context
.active_object
== selection
[0]:
391 source
= selection
[1]
393 source
= selection
[0]
395 func_add_corrective_pose_shape_fast(source
, target
)
400 # -----------------------------------------------------------------------------
403 def vgroups_draw(self
, context
):
406 layout
.operator("object.object_duplicate_flatten_modifiers",
407 text
='Create duplicate for editing')
408 layout
.operator("object.add_corrective_pose_shape_fast",
409 text
='Add as corrective pose-shape (fast, armatures only)',
410 icon
='COPY_ID') # icon is not ideal
411 layout
.operator("object.add_corrective_pose_shape",
412 text
='Add as corrective pose-shape (slow, all modifiers)',
413 icon
='COPY_ID') # icon is not ideal
416 def modifiers_draw(self
, context
):
421 bpy
.utils
.register_module(__name__
)
423 bpy
.types
.MESH_MT_shape_key_specials
.append(vgroups_draw
)
424 bpy
.types
.DATA_PT_modifiers
.append(modifiers_draw
)
428 bpy
.utils
.unregister_module(__name__
)
431 if __name__
== "__main__":