Fix error in rigify property generation
[blender-addons.git] / animation_add_corrective_shape_key.py
blob01e13cdeed8569cbfa176d3d688eecf4decb72e9
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 #####
19 # <pep8-80 compliant>
21 bl_info = {
22 "name": "Corrective Shape Keys",
23 "author": "Ivo Grigull (loolarge), Tal Trachtman", "Tokikake"
24 "version": (1, 1, 1),
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",
32 """
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 )
59 """
62 import bpy
63 from mathutils import Vector, Matrix
65 iterations = 20
66 threshold = 1e-16
68 def update_mesh(ob):
69 depth = bpy.context.evaluated_depsgraph_get()
70 depth.update()
71 ob.update_tag()
72 bpy.context.view_layer.update()
73 ob.data.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
90 verts = mesh.vertices
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))]
93 mesh.user_clear()
94 bpy.data.meshes.remove(mesh)
95 update_mesh(ob)
96 return arr
100 def apply_vert_coords(ob, mesh, x):
101 for i, v in enumerate(mesh):
102 v.co = x[i]
103 update_mesh(ob)
106 def func_add_corrective_pose_shape(source, target, flag):
108 ob_1 = target
109 mesh_1 = target.data
110 ob_2 = source
111 mesh_2 = source.data
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()
118 basis.name = "Basis"
119 update_mesh(ob_1)
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
123 print(ob_1)
124 print(ob_1.active_shape_key)
125 active_key_name = ob_1.active_shape_key.name
127 if (flag == True):
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"
133 update_mesh(ob_1)
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)
139 if key_index == 0:
140 new_shapekey = ob_1.shape_key_add()
141 new_shapekey.name = "Shape_" + ob_2.name
142 update_mesh(ob_1)
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:
170 epsilon = 0.0
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])
193 x[i] += G @ Delmorph
195 apply_vert_coords(ob_1, mesh_1_key_verts, x)
197 ob_1.show_only_shape_key = True
199 if (flag == 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
207 update_mesh(ob_1)
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)
212 ob_1.data.update()
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
219 update_mesh(ob_1)
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"
231 @classmethod
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")
239 return {'CANCELLED'}
241 target = context.active_object
242 if context.active_object == selection[0]:
243 source = selection[1]
244 else:
245 source = selection[0]
247 delta_flag = False
249 func_add_corrective_pose_shape(source, target, delta_flag)
251 return {'FINISHED'}
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"
261 @classmethod
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")
269 return {'CANCELLED'}
271 target = context.active_object
272 if context.active_object == selection[0]:
273 source = selection[1]
274 else:
275 source = selection[0]
277 delta_flag = True
279 func_add_corrective_pose_shape(source, target, delta_flag)
281 return {'FINISHED'}
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)
292 return 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"
301 @classmethod
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)
310 # setup the context
311 bpy.ops.object.select_all(action='DESELECT')
313 context.view_layer.objects.active = new_object
314 new_object.select_set(True)
316 return {'FINISHED'}
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
326 meshData =obj.data
327 mesh = meshData
329 armData = armatureOb.data
331 pose = armatureOb.pose
332 pbones = pose.bones
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:
342 try:
343 name = obj.vertex_groups[n.group].name
344 weight = n.weight
345 is_bone = False
346 for i in armData.bones:
347 if i.name == name:
348 is_bone = True
349 break
350 # ignore non-bone vertex groups
351 if is_bone:
352 listOfBoneNameWeightPairs.append([name, weight])
353 except:
354 print('error')
355 pass
357 weightedAverageDictionary = {}
358 totalWeight = 0
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
365 else:
366 weightedAverageDictionary[pair[0]] = 0
368 # Matrix filled with zeros
369 sigma = Matrix()
370 sigma.zero()
372 list = []
373 for n in pbones:
374 list.append(n)
375 list.reverse()
377 for pbone in list:
378 if pbone.name in weightedAverageDictionary:
379 #~ print("found key %s", pbone.name)
380 vertexWeight = weightedAverageDictionary[pbone.name]
381 m = pbone.matrix_channel.copy()
382 #m.transpose()
383 sigma += (m - I) * vertexWeight
385 else:
386 pass
387 #~ print("no key for bone " + pbone.name)
389 sigma = I + sigma
390 sigma.invert()
391 psdMeshVert.co = sigma @ psdMeshVert.co
392 obj.update_tag()
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()
403 basis.name = "Basis"
404 target.data.update()
406 key_index = target.active_shape_key_index
408 if key_index == 0:
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
423 try:
424 vgroup = target.active_shape_key.vertex_group
425 target.active_shape_key.vertex_group = ''
426 except:
427 pass
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
433 target.update_tag()
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:
438 #~ print("got one")
439 n.use_bone_envelopes = False
440 n.use_deform_preserve_volume = False
441 n.use_vertex_groups = True
442 armature = n.object
443 unposeMesh(shape_key_verts, target, armature)
444 break
446 # set the new shape key value to 1.0, so we see the result instantly
447 target.active_shape_key.value = 1.0
449 try:
450 target.active_shape_key.vertex_group = vgroup
451 except:
452 pass
454 target.show_only_shape_key = False
455 target.update_tag()
456 bpy.context.view_layer.update()
458 target.data.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"
470 @classmethod
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")
478 return {'CANCELLED'}
480 target = context.active_object
481 if context.active_object == selection[0]:
482 source = selection[1]
483 else:
484 source = selection[0]
486 func_add_corrective_pose_shape_fast(source, target)
488 return {'FINISHED'}
492 # -----------------------------------------------------------------------------
493 # GUI
495 def vgroups_draw(self, context):
496 layout = self.layout
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):
509 pass
511 classes = (add_corrective_pose_shape, add_corrective_pose_shape_delta, object_duplicate_flatten_modifiers, add_corrective_pose_shape_fast)
512 def register():
513 from bpy.utils import register_class
514 for cls in classes:
515 register_class(cls)
516 bpy.types.MESH_MT_shape_key_context_menu.append(vgroups_draw)
517 bpy.types.DATA_PT_modifiers.append(modifiers_draw)
520 def unregister():
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__":
528 register()