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", "Tokikake"
25 "blender": (2, 80, 0),
26 "location": "Object Data > Shape Keys Specials or Search",
27 "description": "Creates a corrective shape key for the current pose",
28 "doc_url": "{BLENDER_MANUAL_URL}/addons/animation/corrective_shape_keys.html",
29 "category": "Animation",
33 This script transfer the shape from an object (base mesh without
34 modifiers) to another object with modifiers (i.e. posed Armature).
35 Only two objects must be selected.
36 The first selected object will be added to the second selected
37 object as a new shape key.
39 - Original 2.4x script by Brecht
40 - Unpose-function reused from a script by Tal Trachtman in 2007
41 http://www.apexbow.com/randd.html
42 - Converted to Blender 2.5 by Ivo Grigull
43 - Converted to Blender 2.8 by Tokikake
44 ("fast" option was removed, add new "delta" option
45 which count currently used shape key values of armature mesh when transfer)
47 Limitations and new delta option for 2.8
48 - Target mesh may not have any transformation at object level,
49 it will be set to zero.
51 - new "delta" option usage, when you hope to make new shape-key with keep currently visible other shape keys value.
52 it can generate new shape key, with value as 1.00. then deform target shape as source shape with keep other shape key values relative.
54 - If overwrite shape key,<select active shape key of target as non "base shape">
55 current shape key value is ignored and turn as 1.00.
57 then if active shape key was driven (bone rotation etc), you may get un-expected result. When transfer, I recommend, keep set active-shape key as base. so transferred shape key do not "overwrite". but generate new shape key.
58 if active-shape key have no driver, you can overwrite it (but as 1.00 value )
63 from mathutils
import Vector
, Matrix
69 depth
= bpy
.context
.evaluated_depsgraph_get()
72 bpy
.context
.view_layer
.update()
76 def reset_transform(ob
):
77 ob
.matrix_local
.identity()
79 # this version is for shape_key data
80 def extract_vert_coords(verts
):
81 return [v
.co
.copy() for v
in verts
]
83 def extract_mapped_coords(ob
, shape_verts
):
84 depth
= bpy
.context
.evaluated_depsgraph_get()
85 eobj
= ob
.evaluated_get(depth
)
86 mesh
= bpy
.data
.meshes
.new_from_object(eobj
)
88 # cheating, the original mapped verts happen
89 # to be at the end of the vertex array
91 #arr = [verts[i].co.copy() for i in range(len(verts) - totvert, len(verts))]
92 arr
= [verts
[i
].co
.copy() for i
in range(0, len(verts
))]
94 bpy
.data
.meshes
.remove(mesh
)
100 def apply_vert_coords(ob
, mesh
, x
):
101 for i
, v
in enumerate(mesh
):
106 def func_add_corrective_pose_shape(source
, target
, flag
):
113 reset_transform(target
)
115 # If target object doesn't have Base shape key, create it.
116 if not mesh_1
.shape_keys
:
117 basis
= ob_1
.shape_key_add()
120 ob_1
.active_shape_key_index
= 0
121 ob_1
.show_only_shape_key
= False
122 key_index
= ob_1
.active_shape_key_index
124 print(ob_1
.active_shape_key
)
125 active_key_name
= ob_1
.active_shape_key
.name
128 # Make mix shape key from currently used shape keys
129 if not key_index
== 0:
130 ob_1
.active_shape_key
.value
= 0
131 mix_shape
= ob_1
.shape_key_add(from_mix
= True)
132 mix_shape
.name
= "Mix_shape"
134 keys
= ob_1
.data
.shape_keys
.key_blocks
.keys()
135 ob_1
.active_shape_key_index
= keys
.index(active_key_name
)
137 print("active_key_name: ", active_key_name
)
140 new_shapekey
= ob_1
.shape_key_add()
141 new_shapekey
.name
= "Shape_" + ob_2
.name
143 keys
= ob_1
.data
.shape_keys
.key_blocks
.keys()
144 ob_1
.active_shape_key_index
= keys
.index(new_shapekey
.name
)
146 # else, the active shape will be used (updated)
148 ob_1
.show_only_shape_key
= True
150 vgroup
= ob_1
.active_shape_key
.vertex_group
151 ob_1
.active_shape_key
.vertex_group
= ""
153 #mesh_1_key_verts = mesh_1.shape_keys.key_blocks[key_index].data
154 mesh_1_key_verts
= ob_1
.active_shape_key
.data
156 x
= extract_vert_coords(mesh_1_key_verts
)
158 targetx
= extract_vert_coords(mesh_2
.vertices
)
160 for iteration
in range(0, iterations
):
161 dx
= [[], [], [], [], [], []]
163 mapx
= extract_mapped_coords(ob_1
, mesh_1_key_verts
)
165 # finite differencing in X/Y/Z to get approximate gradient
166 for i
in range(0, len(mesh_1
.vertices
)):
167 epsilon
= (targetx
[i
] - mapx
[i
]).length
169 if epsilon
< threshold
:
172 dx
[0] += [x
[i
] + 0.5 * epsilon
* Vector((1, 0, 0))]
173 dx
[1] += [x
[i
] + 0.5 * epsilon
* Vector((-1, 0, 0))]
174 dx
[2] += [x
[i
] + 0.5 * epsilon
* Vector((0, 1, 0))]
175 dx
[3] += [x
[i
] + 0.5 * epsilon
* Vector((0, -1, 0))]
176 dx
[4] += [x
[i
] + 0.5 * epsilon
* Vector((0, 0, 1))]
177 dx
[5] += [x
[i
] + 0.5 * epsilon
* Vector((0, 0, -1))]
179 for j
in range(0, 6):
180 apply_vert_coords(ob_1
, mesh_1_key_verts
, dx
[j
])
181 dx
[j
] = extract_mapped_coords(ob_1
, mesh_1_key_verts
)
183 # take a step in the direction of the gradient
184 for i
in range(0, len(mesh_1
.vertices
)):
185 epsilon
= (targetx
[i
] - mapx
[i
]).length
187 if epsilon
>= threshold
:
188 Gx
= list((dx
[0][i
] - dx
[1][i
]) / epsilon
)
189 Gy
= list((dx
[2][i
] - dx
[3][i
]) / epsilon
)
190 Gz
= list((dx
[4][i
] - dx
[5][i
]) / epsilon
)
191 G
= Matrix((Gx
, Gy
, Gz
))
192 Delmorph
= (targetx
[i
] - mapx
[i
])
195 apply_vert_coords(ob_1
, mesh_1_key_verts
, x
)
197 ob_1
.show_only_shape_key
= True
200 # remove delta of mix-shape key values from new shape key
201 key_index
= ob_1
.active_shape_key_index
202 active_key_name
= ob_1
.active_shape_key
.name
203 shape_data
= ob_1
.active_shape_key
.data
204 mix_data
= mix_shape
.data
205 for i
in range(0, len(mesh_1
.vertices
)):
206 shape_data
[i
].co
= mesh_1
.vertices
[i
].co
+ shape_data
[i
].co
- mix_data
[i
].co
209 ob_1
.active_shape_key_index
= ob_1
.data
.shape_keys
.key_blocks
.keys().index("Mix_shape")
210 bpy
.ops
.object.shape_key_remove()
211 ob_1
.active_shape_key_index
= ob_1
.data
.shape_keys
.key_blocks
.keys().index(active_key_name
)
213 ob_1
.show_only_shape_key
= False
215 ob_1
.active_shape_key
.vertex_group
= vgroup
217 # set the new shape key value to 1.0, so we see the result instantly
218 ob_1
.active_shape_key
.value
= 1.0
223 class add_corrective_pose_shape(bpy
.types
.Operator
):
224 """Adds first object as shape to second object for the current pose """ \
225 """while maintaining modifiers """ \
226 """(i.e. anisculpt, avoiding crazy space) Beware of slowness!"""
228 bl_idname
= "object.add_corrective_pose_shape"
229 bl_label
= "Add object as corrective pose shape"
232 def poll(cls
, context
):
233 return context
.active_object
is not None
235 def execute(self
, context
):
236 selection
= context
.selected_objects
237 if len(selection
) != 2:
238 self
.report({'ERROR'}, "Select source and target objects")
241 target
= context
.active_object
242 if context
.active_object
== selection
[0]:
243 source
= selection
[1]
245 source
= selection
[0]
249 func_add_corrective_pose_shape(source
, target
, delta_flag
)
253 class add_corrective_pose_shape_delta (bpy
.types
.Operator
):
254 """Adds first object as shape to second object for the current pose """ \
255 """while maintaining modifiers and currently used other shape keys""" \
256 """with keep other shape key value, generate new shape key which deform to source shape """
258 bl_idname
= "object.add_corrective_pose_shape_delta"
259 bl_label
= "Add object as corrective pose shape delta"
262 def poll(cls
, context
):
263 return context
.active_object
is not None
265 def execute(self
, context
):
266 selection
= context
.selected_objects
267 if len(selection
) != 2:
268 self
.report({'ERROR'}, "Select source and target objects")
271 target
= context
.active_object
272 if context
.active_object
== selection
[0]:
273 source
= selection
[1]
275 source
= selection
[0]
279 func_add_corrective_pose_shape(source
, target
, delta_flag
)
284 def func_object_duplicate_flatten_modifiers(context
, ob
):
285 depth
= bpy
.context
.evaluated_depsgraph_get()
286 eobj
= ob
.evaluated_get(depth
)
287 mesh
= bpy
.data
.meshes
.new_from_object(eobj
)
288 name
= ob
.name
+ "_clean"
289 new_object
= bpy
.data
.objects
.new(name
, mesh
)
290 new_object
.data
= mesh
291 bpy
.context
.collection
.objects
.link(new_object
)
295 class object_duplicate_flatten_modifiers(bpy
.types
.Operator
):
296 #Duplicates the selected object with modifiers applied
298 bl_idname
= "object.object_duplicate_flatten_modifiers"
299 bl_label
= "Duplicate and apply all"
302 def poll(cls
, context
):
303 return context
.active_object
is not None
305 def execute(self
, context
):
306 obj_act
= context
.active_object
308 new_object
= func_object_duplicate_flatten_modifiers(context
, obj_act
)
311 bpy
.ops
.object.select_all(action
='DESELECT')
313 context
.view_layer
.objects
.active
= new_object
314 new_object
.select_set(True)
318 #these old functions and class not work correctly just keep code for others try to edit
320 def unposeMesh(meshObToUnpose
, obj
, armatureOb
):
321 psdMeshData
= meshObToUnpose
323 psdMesh
= psdMeshData
324 I
= Matrix() # identity matrix
329 armData
= armatureOb
.data
331 pose
= armatureOb
.pose
334 for index
, v
in enumerate(mesh
.vertices
):
335 # above is python shortcut for:index goes up from 0 to tot num of
336 # verts in mesh, with index incrementing by 1 each iteration
338 psdMeshVert
= psdMesh
[index
]
340 listOfBoneNameWeightPairs
= []
341 for n
in mesh
.vertices
[index
].groups
:
343 name
= obj
.vertex_groups
[n
.group
].name
346 for i
in armData
.bones
:
350 # ignore non-bone vertex groups
352 listOfBoneNameWeightPairs
.append([name
, weight
])
357 weightedAverageDictionary
= {}
359 for pair
in listOfBoneNameWeightPairs
:
360 totalWeight
+= pair
[1]
362 for pair
in listOfBoneNameWeightPairs
:
363 if totalWeight
> 0: # avoid divide by zero!
364 weightedAverageDictionary
[pair
[0]] = pair
[1] / totalWeight
366 weightedAverageDictionary
[pair
[0]] = 0
368 # Matrix filled with zeros
378 if pbone
.name
in weightedAverageDictionary
:
379 #~ print("found key %s", pbone.name)
380 vertexWeight
= weightedAverageDictionary
[pbone
.name
]
381 m
= pbone
.matrix_channel
.copy()
383 sigma
+= (m
- I
) * vertexWeight
387 #~ print("no key for bone " + pbone.name)
391 psdMeshVert
.co
= sigma
@ psdMeshVert
.co
393 bpy
.context
.view_layer
.update()
397 def func_add_corrective_pose_shape_fast(source
, target
):
398 reset_transform(target
)
400 # If target object doesn't have Basis shape key, create it.
401 if not target
.data
.shape_keys
:
402 basis
= target
.shape_key_add()
406 key_index
= target
.active_shape_key_index
410 # Insert new shape key
411 new_shapekey
= target
.shape_key_add()
412 new_shapekey
.name
= "Shape_" + source
.name
414 key_index
= len(target
.data
.shape_keys
.key_blocks
) - 1
415 target
.active_shape_key_index
= key_index
417 # else, the active shape will be used (updated)
419 target
.show_only_shape_key
= True
421 shape_key_verts
= target
.data
.shape_keys
.key_blocks
[key_index
].data
424 vgroup
= target
.active_shape_key
.vertex_group
425 target
.active_shape_key
.vertex_group
= ''
429 # copy the local vertex positions to the new shape
430 verts
= source
.data
.vertices
431 for n
in range(len(verts
)):
432 shape_key_verts
[n
].co
= verts
[n
].co
434 bpy
.context
.view_layer
.update()
435 # go to all armature modifies and unpose the shape
436 for n
in target
.modifiers
:
437 if n
.type == 'ARMATURE' and n
.show_viewport
:
439 n
.use_bone_envelopes
= False
440 n
.use_deform_preserve_volume
= False
441 n
.use_vertex_groups
= True
443 unposeMesh(shape_key_verts
, target
, armature
)
446 # set the new shape key value to 1.0, so we see the result instantly
447 target
.active_shape_key
.value
= 1.0
450 target
.active_shape_key
.vertex_group
= vgroup
454 target
.show_only_shape_key
= False
456 bpy
.context
.view_layer
.update()
464 class add_corrective_pose_shape_fast(bpy
.types
.Operator
):
465 #Adds 1st object as shape to 2nd object as pose shape (only 1 armature)
467 bl_idname
= "object.add_corrective_pose_shape_fast"
468 bl_label
= "Add object as corrective shape faster"
471 def poll(cls
, context
):
472 return context
.active_object
is not None
474 def execute(self
, context
):
475 selection
= context
.selected_objects
476 if len(selection
) != 2:
477 self
.report({'ERROR'}, "Select source and target objects")
480 target
= context
.active_object
481 if context
.active_object
== selection
[0]:
482 source
= selection
[1]
484 source
= selection
[0]
486 func_add_corrective_pose_shape_fast(source
, target
)
492 # -----------------------------------------------------------------------------
495 def vgroups_draw(self
, context
):
498 layout
.operator("object.object_duplicate_flatten_modifiers",
499 text
='Create duplicate for editing')
500 layout
.operator("object.add_corrective_pose_shape",
501 text
='Add as corrective pose-shape (slow, all modifiers)',
502 icon
='COPY_ID') # icon is not ideal
503 layout
.operator("object.add_corrective_pose_shape_delta",
504 text
='Add as corrective pose-shape delta" (slow, all modifiers + other shape key values)',
505 icon
='COPY_ID') # icon is not ideal
508 def modifiers_draw(self
, context
):
511 classes
= (add_corrective_pose_shape
, add_corrective_pose_shape_delta
, object_duplicate_flatten_modifiers
, add_corrective_pose_shape_fast
)
513 from bpy
.utils
import register_class
516 bpy
.types
.MESH_MT_shape_key_context_menu
.append(vgroups_draw
)
517 bpy
.types
.DATA_PT_modifiers
.append(modifiers_draw
)
521 from bpy
.utils
import unregister_class
522 for cls
in reversed(classes
):
523 unregister_class(cls
)
524 bpy
.types
.MESH_MT_shape_key_context_menu
.remove(vgroups_draw
)
525 bpy
.types
.DATA_PT_modifiers
.remove(modifiers_draw
)
527 if __name__
== "__main__":