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 "wiki_url": "https://docs.blender.org/manual/en/dev/addons/"
29 "animation/corrective_shape_keys.html",
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
44 - Converted to Blender 2.8 by Tokikake
45 ("fast" option was removed, add new "delta" option
46 which count currently used shape key values of armature mesh when transfer)
48 Limitations and new delta option for 2.8
49 - Target mesh may not have any transformation at object level,
50 it will be set to zero.
52 - new "delta" option usage, when you hope to make new shape-key with keep currently visible other shape keys value.
53 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.
55 - If overwrite shape key,<select active shape key of target as non "base shape">
56 current shape key value is ignored and turn as 1.00.
58 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.
59 if active-shape key have no driver, you can overwrite it (but as 1.00 value )
64 from mathutils
import Vector
, Matrix
70 depth
= bpy
.context
.evaluated_depsgraph_get()
73 bpy
.context
.view_layer
.update()
77 def reset_transform(ob
):
78 ob
.matrix_local
.identity()
80 # this version is for shape_key data
81 def extract_vert_coords(verts
):
82 return [v
.co
.copy() for v
in verts
]
84 def extract_mapped_coords(ob
, shape_verts
):
85 depth
= bpy
.context
.evaluated_depsgraph_get()
86 eobj
= ob
.evaluated_get(depth
)
87 mesh
= bpy
.data
.meshes
.new_from_object(eobj
)
89 # cheating, the original mapped verts happen
90 # to be at the end of the vertex array
92 #arr = [verts[i].co.copy() for i in range(len(verts) - totvert, len(verts))]
93 arr
= [verts
[i
].co
.copy() for i
in range(0, len(verts
))]
95 bpy
.data
.meshes
.remove(mesh
)
101 def apply_vert_coords(ob
, mesh
, x
):
102 for i
, v
in enumerate(mesh
):
107 def func_add_corrective_pose_shape(source
, target
, flag
):
114 reset_transform(target
)
116 # If target object doesn't have Base shape key, create it.
117 if not mesh_1
.shape_keys
:
118 basis
= ob_1
.shape_key_add()
121 ob_1
.active_shape_key_index
= 0
122 ob_1
.show_only_shape_key
= False
123 key_index
= ob_1
.active_shape_key_index
125 print(ob_1
.active_shape_key
)
126 active_key_name
= ob_1
.active_shape_key
.name
129 # Make mix shape key from currently used shape keys
130 if not key_index
== 0:
131 ob_1
.active_shape_key
.value
= 0
132 mix_shape
= ob_1
.shape_key_add(from_mix
= True)
133 mix_shape
.name
= "Mix_shape"
135 keys
= ob_1
.data
.shape_keys
.key_blocks
.keys()
136 ob_1
.active_shape_key_index
= keys
.index(active_key_name
)
138 print("active_key_name: ", active_key_name
)
141 new_shapekey
= ob_1
.shape_key_add()
142 new_shapekey
.name
= "Shape_" + ob_2
.name
144 keys
= ob_1
.data
.shape_keys
.key_blocks
.keys()
145 ob_1
.active_shape_key_index
= keys
.index(new_shapekey
.name
)
147 # else, the active shape will be used (updated)
149 ob_1
.show_only_shape_key
= True
151 vgroup
= ob_1
.active_shape_key
.vertex_group
152 ob_1
.active_shape_key
.vertex_group
= ""
154 #mesh_1_key_verts = mesh_1.shape_keys.key_blocks[key_index].data
155 mesh_1_key_verts
= ob_1
.active_shape_key
.data
157 x
= extract_vert_coords(mesh_1_key_verts
)
159 targetx
= extract_vert_coords(mesh_2
.vertices
)
161 for iteration
in range(0, iterations
):
162 dx
= [[], [], [], [], [], []]
164 mapx
= extract_mapped_coords(ob_1
, mesh_1_key_verts
)
166 # finite differencing in X/Y/Z to get approximate gradient
167 for i
in range(0, len(mesh_1
.vertices
)):
168 epsilon
= (targetx
[i
] - mapx
[i
]).length
170 if epsilon
< threshold
:
173 dx
[0] += [x
[i
] + 0.5 * epsilon
* Vector((1, 0, 0))]
174 dx
[1] += [x
[i
] + 0.5 * epsilon
* Vector((-1, 0, 0))]
175 dx
[2] += [x
[i
] + 0.5 * epsilon
* Vector((0, 1, 0))]
176 dx
[3] += [x
[i
] + 0.5 * epsilon
* Vector((0, -1, 0))]
177 dx
[4] += [x
[i
] + 0.5 * epsilon
* Vector((0, 0, 1))]
178 dx
[5] += [x
[i
] + 0.5 * epsilon
* Vector((0, 0, -1))]
180 for j
in range(0, 6):
181 apply_vert_coords(ob_1
, mesh_1_key_verts
, dx
[j
])
182 dx
[j
] = extract_mapped_coords(ob_1
, mesh_1_key_verts
)
184 # take a step in the direction of the gradient
185 for i
in range(0, len(mesh_1
.vertices
)):
186 epsilon
= (targetx
[i
] - mapx
[i
]).length
188 if epsilon
>= threshold
:
189 Gx
= list((dx
[0][i
] - dx
[1][i
]) / epsilon
)
190 Gy
= list((dx
[2][i
] - dx
[3][i
]) / epsilon
)
191 Gz
= list((dx
[4][i
] - dx
[5][i
]) / epsilon
)
192 G
= Matrix((Gx
, Gy
, Gz
))
193 Delmorph
= (targetx
[i
] - mapx
[i
])
196 apply_vert_coords(ob_1
, mesh_1_key_verts
, x
)
198 ob_1
.show_only_shape_key
= True
201 # remove delta of mix-shape key values from new shape key
202 key_index
= ob_1
.active_shape_key_index
203 active_key_name
= ob_1
.active_shape_key
.name
204 shape_data
= ob_1
.active_shape_key
.data
205 mix_data
= mix_shape
.data
206 for i
in range(0, len(mesh_1
.vertices
)):
207 shape_data
[i
].co
= mesh_1
.vertices
[i
].co
+ shape_data
[i
].co
- mix_data
[i
].co
210 ob_1
.active_shape_key_index
= ob_1
.data
.shape_keys
.key_blocks
.keys().index("Mix_shape")
211 bpy
.ops
.object.shape_key_remove()
212 ob_1
.active_shape_key_index
= ob_1
.data
.shape_keys
.key_blocks
.keys().index(active_key_name
)
214 ob_1
.show_only_shape_key
= False
216 ob_1
.active_shape_key
.vertex_group
= vgroup
218 # set the new shape key value to 1.0, so we see the result instantly
219 ob_1
.active_shape_key
.value
= 1.0
224 class add_corrective_pose_shape(bpy
.types
.Operator
):
225 """Adds first object as shape to second object for the current pose """ \
226 """while maintaining modifiers """ \
227 """(i.e. anisculpt, avoiding crazy space) Beware of slowness!"""
229 bl_idname
= "object.add_corrective_pose_shape"
230 bl_label
= "Add object as corrective pose shape"
233 def poll(cls
, context
):
234 return context
.active_object
is not None
236 def execute(self
, context
):
237 selection
= context
.selected_objects
238 if len(selection
) != 2:
239 self
.report({'ERROR'}, "Select source and target objects")
242 target
= context
.active_object
243 if context
.active_object
== selection
[0]:
244 source
= selection
[1]
246 source
= selection
[0]
250 func_add_corrective_pose_shape(source
, target
, delta_flag
)
254 class add_corrective_pose_shape_delta (bpy
.types
.Operator
):
255 """Adds first object as shape to second object for the current pose """ \
256 """while maintaining modifiers and currently used other shape keys""" \
257 """with keep other shape key value, generate new shape key which deform to source shape """
259 bl_idname
= "object.add_corrective_pose_shape_delta"
260 bl_label
= "Add object as corrective pose shape delta"
263 def poll(cls
, context
):
264 return context
.active_object
is not None
266 def execute(self
, context
):
267 selection
= context
.selected_objects
268 if len(selection
) != 2:
269 self
.report({'ERROR'}, "Select source and target objects")
272 target
= context
.active_object
273 if context
.active_object
== selection
[0]:
274 source
= selection
[1]
276 source
= selection
[0]
280 func_add_corrective_pose_shape(source
, target
, delta_flag
)
285 def func_object_duplicate_flatten_modifiers(context
, ob
):
286 depth
= bpy
.context
.evaluated_depsgraph_get()
287 eobj
= ob
.evaluated_get(depth
)
288 mesh
= bpy
.data
.meshes
.new_from_object(eobj
)
289 name
= ob
.name
+ "_clean"
290 new_object
= bpy
.data
.objects
.new(name
, mesh
)
291 new_object
.data
= mesh
292 bpy
.context
.collection
.objects
.link(new_object
)
296 class object_duplicate_flatten_modifiers(bpy
.types
.Operator
):
297 #Duplicates the selected object with modifiers applied
299 bl_idname
= "object.object_duplicate_flatten_modifiers"
300 bl_label
= "Duplicate and apply all"
303 def poll(cls
, context
):
304 return context
.active_object
is not None
306 def execute(self
, context
):
307 obj_act
= context
.active_object
309 new_object
= func_object_duplicate_flatten_modifiers(context
, obj_act
)
312 bpy
.ops
.object.select_all(action
='DESELECT')
314 context
.view_layer
.objects
.active
= new_object
315 new_object
.select_set(True)
319 #these old functions and class not work correctly just keep code for others try to edit
321 def unposeMesh(meshObToUnpose
, obj
, armatureOb
):
322 psdMeshData
= meshObToUnpose
324 psdMesh
= psdMeshData
325 I
= Matrix() # identity matrix
330 armData
= armatureOb
.data
332 pose
= armatureOb
.pose
335 for index
, v
in enumerate(mesh
.vertices
):
336 # above is python shortcut for:index goes up from 0 to tot num of
337 # verts in mesh, with index incrementing by 1 each iteration
339 psdMeshVert
= psdMesh
[index
]
341 listOfBoneNameWeightPairs
= []
342 for n
in mesh
.vertices
[index
].groups
:
344 name
= obj
.vertex_groups
[n
.group
].name
347 for i
in armData
.bones
:
351 # ignore non-bone vertex groups
353 listOfBoneNameWeightPairs
.append([name
, weight
])
358 weightedAverageDictionary
= {}
360 for pair
in listOfBoneNameWeightPairs
:
361 totalWeight
+= pair
[1]
363 for pair
in listOfBoneNameWeightPairs
:
364 if totalWeight
> 0: # avoid divide by zero!
365 weightedAverageDictionary
[pair
[0]] = pair
[1] / totalWeight
367 weightedAverageDictionary
[pair
[0]] = 0
369 # Matrix filled with zeros
379 if pbone
.name
in weightedAverageDictionary
:
380 #~ print("found key %s", pbone.name)
381 vertexWeight
= weightedAverageDictionary
[pbone
.name
]
382 m
= pbone
.matrix_channel
.copy()
384 sigma
+= (m
- I
) * vertexWeight
388 #~ print("no key for bone " + pbone.name)
392 psdMeshVert
.co
= sigma
@ psdMeshVert
.co
394 bpy
.context
.view_layer
.update()
398 def func_add_corrective_pose_shape_fast(source
, target
):
399 reset_transform(target
)
401 # If target object doesn't have Basis shape key, create it.
402 if not target
.data
.shape_keys
:
403 basis
= target
.shape_key_add()
407 key_index
= target
.active_shape_key_index
411 # Insert new shape key
412 new_shapekey
= target
.shape_key_add()
413 new_shapekey
.name
= "Shape_" + source
.name
415 key_index
= len(target
.data
.shape_keys
.key_blocks
) - 1
416 target
.active_shape_key_index
= key_index
418 # else, the active shape will be used (updated)
420 target
.show_only_shape_key
= True
422 shape_key_verts
= target
.data
.shape_keys
.key_blocks
[key_index
].data
425 vgroup
= target
.active_shape_key
.vertex_group
426 target
.active_shape_key
.vertex_group
= ''
430 # copy the local vertex positions to the new shape
431 verts
= source
.data
.vertices
432 for n
in range(len(verts
)):
433 shape_key_verts
[n
].co
= verts
[n
].co
435 bpy
.context
.view_layer
.update()
436 # go to all armature modifies and unpose the shape
437 for n
in target
.modifiers
:
438 if n
.type == 'ARMATURE' and n
.show_viewport
:
440 n
.use_bone_envelopes
= False
441 n
.use_deform_preserve_volume
= False
442 n
.use_vertex_groups
= True
444 unposeMesh(shape_key_verts
, target
, armature
)
447 # set the new shape key value to 1.0, so we see the result instantly
448 target
.active_shape_key
.value
= 1.0
451 target
.active_shape_key
.vertex_group
= vgroup
455 target
.show_only_shape_key
= False
457 bpy
.context
.view_layer
.update()
465 class add_corrective_pose_shape_fast(bpy
.types
.Operator
):
466 #Adds 1st object as shape to 2nd object as pose shape (only 1 armature)
468 bl_idname
= "object.add_corrective_pose_shape_fast"
469 bl_label
= "Add object as corrective shape faster"
472 def poll(cls
, context
):
473 return context
.active_object
is not None
475 def execute(self
, context
):
476 selection
= context
.selected_objects
477 if len(selection
) != 2:
478 self
.report({'ERROR'}, "Select source and target objects")
481 target
= context
.active_object
482 if context
.active_object
== selection
[0]:
483 source
= selection
[1]
485 source
= selection
[0]
487 func_add_corrective_pose_shape_fast(source
, target
)
493 # -----------------------------------------------------------------------------
496 def vgroups_draw(self
, context
):
499 layout
.operator("object.object_duplicate_flatten_modifiers",
500 text
='Create duplicate for editing')
501 layout
.operator("object.add_corrective_pose_shape",
502 text
='Add as corrective pose-shape (slow, all modifiers)',
503 icon
='COPY_ID') # icon is not ideal
504 layout
.operator("object.add_corrective_pose_shape_delta",
505 text
='Add as corrective pose-shape delta" (slow, all modifiers + other shape key values)',
506 icon
='COPY_ID') # icon is not ideal
509 def modifiers_draw(self
, context
):
512 classes
= (add_corrective_pose_shape
, add_corrective_pose_shape_delta
, object_duplicate_flatten_modifiers
, add_corrective_pose_shape_fast
)
514 from bpy
.utils
import register_class
517 bpy
.types
.MESH_MT_shape_key_context_menu
.append(vgroups_draw
)
518 bpy
.types
.DATA_PT_modifiers
.append(modifiers_draw
)
522 from bpy
.utils
import unregister_class
523 for cls
in reversed(classes
):
524 unregister_class(cls
)
525 bpy
.types
.MESH_MT_shape_key_context_menu
.remove(vgroups_draw
)
526 bpy
.types
.DATA_PT_modifiers
.remove(modifiers_draw
)
528 if __name__
== "__main__":