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 from mathutils
import Vector
23 from bpy_extras
import anim_utils
24 from . import retarget
31 #utility function - returns related IK target if bone has IK
32 ik
= [constraint
for constraint
in bone
.constraints
if constraint
.type == "IK"]
37 cons_obj
= ik
.target
.pose
.bones
[ik
.subtarget
]
43 def consObjToBone(cons_obj
):
44 #Utility function - returns related bone from ik object
45 if cons_obj
.name
[-3:] == "Org":
46 return cons_obj
.name
[:-3]
50 ### And and Remove Constraints (called from operators)
53 def addNewConstraint(m_constraint
, cons_obj
):
54 #Decide the correct Blender constraint according to the Mocap constraint type
55 if m_constraint
.type == "point" or m_constraint
.type == "freeze":
56 c_type
= "LIMIT_LOCATION"
57 if m_constraint
.type == "distance":
58 c_type
= "LIMIT_DISTANCE"
59 if m_constraint
.type == "floor":
60 c_type
= "LIMIT_LOCATION"
61 #create and store the new constraint within m_constraint
62 real_constraint
= cons_obj
.constraints
.new(c_type
)
63 real_constraint
.name
= "Auto fixes " + str(len(cons_obj
.constraints
))
64 m_constraint
.real_constraint_bone
= consObjToBone(cons_obj
)
65 m_constraint
.real_constraint
= real_constraint
.name
66 #set the rest of the constraint properties
67 setConstraint(m_constraint
, bpy
.context
)
70 def removeConstraint(m_constraint
, cons_obj
):
71 #remove the influence fcurve and Blender constraint
72 oldConstraint
= cons_obj
.constraints
[m_constraint
.real_constraint
]
73 removeFcurves(cons_obj
, bpy
.context
.active_object
, oldConstraint
, m_constraint
)
74 cons_obj
.constraints
.remove(oldConstraint
)
76 ### Update functions. There are 3: UpdateType/Bone
77 ### update framing (deals with changes in the desired frame range)
78 ### And setConstraint which deals with the rest
81 def updateConstraintBoneType(m_constraint
, context
):
82 #If the constraint exists, we need to remove it
84 obj
= context
.active_object
85 bones
= obj
.pose
.bones
86 if m_constraint
.real_constraint
:
87 bone
= bones
[m_constraint
.real_constraint_bone
]
88 cons_obj
= getConsObj(bone
)
89 removeConstraint(m_constraint
, cons_obj
)
90 #Regardless, after that we create a new constraint
91 if m_constraint
.constrained_bone
:
92 bone
= bones
[m_constraint
.constrained_bone
]
93 cons_obj
= getConsObj(bone
)
94 addNewConstraint(m_constraint
, cons_obj
)
97 def setConstraintFraming(m_constraint
, context
):
98 obj
= context
.active_object
99 bones
= obj
.pose
.bones
100 bone
= bones
[m_constraint
.constrained_bone
]
101 cons_obj
= getConsObj(bone
)
102 real_constraint
= cons_obj
.constraints
[m_constraint
.real_constraint
]
103 #remove the old keyframes
104 removeFcurves(cons_obj
, obj
, real_constraint
, m_constraint
)
105 #set the new ones according to the m_constraint properties
106 s
, e
= m_constraint
.s_frame
, m_constraint
.e_frame
107 s_in
, s_out
= m_constraint
.smooth_in
, m_constraint
.smooth_out
108 real_constraint
.influence
= 1
109 real_constraint
.keyframe_insert(data_path
="influence", frame
=s
)
110 real_constraint
.keyframe_insert(data_path
="influence", frame
=e
)
111 real_constraint
.influence
= 0
112 real_constraint
.keyframe_insert(data_path
="influence", frame
=s
- s_in
)
113 real_constraint
.keyframe_insert(data_path
="influence", frame
=e
+ s_out
)
116 def removeFcurves(cons_obj
, obj
, real_constraint
, m_constraint
):
117 #Determine if the constrained object is a bone or an empty
118 if isinstance(cons_obj
, bpy
.types
.PoseBone
):
119 fcurves
= obj
.animation_data
.action
.fcurves
121 fcurves
= cons_obj
.animation_data
.action
.fcurves
122 #Find the RNA data path of the constraint's influence
124 RNA_paths
.append(real_constraint
.path_from_id("influence"))
125 if m_constraint
.type == "floor" or m_constraint
.type == "point":
126 RNA_paths
+= [real_constraint
.path_from_id("max_x"), real_constraint
.path_from_id("min_x")]
127 RNA_paths
+= [real_constraint
.path_from_id("max_y"), real_constraint
.path_from_id("min_y")]
128 RNA_paths
+= [real_constraint
.path_from_id("max_z"), real_constraint
.path_from_id("min_z")]
129 #Retrieve the correct fcurve via the RNA data path and remove it
130 fcurves_del
= [fcurve
for fcurve
in fcurves
if fcurve
.data_path
in RNA_paths
]
131 #clear the fcurve and set the frames.
133 for fcurve
in fcurves_del
:
134 fcurves
.remove(fcurve
)
135 #remove armature fcurves (if user keyframed m_constraint properties)
136 if obj
.data
.animation_data
and m_constraint
.type == "point":
137 if obj
.data
.animation_data
.action
:
138 path
= m_constraint
.path_from_id("targetPoint")
139 m_fcurves
= [fcurve
for fcurve
in obj
.data
.animation_data
.action
.fcurves
if fcurve
.data_path
== path
]
140 for curve
in m_fcurves
:
141 obj
.data
.animation_data
.action
.fcurves
.remove(curve
)
143 #Utility function for copying property fcurves over
146 def copyFCurve(newCurve
, oldCurve
):
147 for point
in oldCurve
.keyframe_points
:
148 newCurve
.keyframe_points
.insert(frame
=point
.co
.x
, value
=point
.co
.y
)
150 #Creates new fcurves for the constraint properties (for floor and point)
153 def createConstraintFCurves(cons_obj
, obj
, real_constraint
):
154 if isinstance(cons_obj
, bpy
.types
.PoseBone
):
155 c_fcurves
= obj
.animation_data
.action
.fcurves
157 c_fcurves
= cons_obj
.animation_data
.action
.fcurves
158 c_x_path
= [real_constraint
.path_from_id("max_x"), real_constraint
.path_from_id("min_x")]
159 c_y_path
= [real_constraint
.path_from_id("max_y"), real_constraint
.path_from_id("min_y")]
160 c_z_path
= [real_constraint
.path_from_id("max_z"), real_constraint
.path_from_id("min_z")]
161 c_constraints_path
= c_x_path
+ c_y_path
+ c_z_path
162 existing_curves
= [fcurve
for fcurve
in c_fcurves
if fcurve
.data_path
in c_constraints_path
]
164 for curve
in existing_curves
:
165 c_fcurves
.remove(curve
)
166 xCurves
, yCurves
, zCurves
= [], [], []
167 for path
in c_constraints_path
:
168 newCurve
= c_fcurves
.new(path
)
170 xCurves
.append(newCurve
)
171 elif path
in c_y_path
:
172 yCurves
.append(newCurve
)
174 zCurves
.append(newCurve
)
175 return xCurves
, yCurves
, zCurves
178 # Function that copies all settings from m_constraint to the real Blender constraints
179 # Is only called when blender constraint already exists
182 def setConstraint(m_constraint
, context
):
183 if not m_constraint
.constrained_bone
:
185 obj
= context
.active_object
186 bones
= obj
.pose
.bones
187 bone
= bones
[m_constraint
.constrained_bone
]
188 cons_obj
= getConsObj(bone
)
189 real_constraint
= cons_obj
.constraints
[m_constraint
.real_constraint
]
190 NLATracks
= obj
.data
.mocapNLATracks
[obj
.data
.active_mocap
]
191 obj
.animation_data
.action
= bpy
.data
.actions
[NLATracks
.auto_fix_track
]
193 #frame changing section
194 setConstraintFraming(m_constraint
, context
)
195 s
, e
= m_constraint
.s_frame
, m_constraint
.e_frame
196 s_in
, s_out
= m_constraint
.smooth_in
, m_constraint
.smooth_out
199 #Set the blender constraint parameters
200 if m_constraint
.type == "point":
201 constraint_settings
= False # are fix settings keyframed?
202 if not m_constraint
.targetSpace
== "constrained_boneB":
203 real_constraint
.owner_space
= m_constraint
.targetSpace
205 real_constraint
.owner_space
= "LOCAL"
206 if obj
.data
.animation_data
:
207 if obj
.data
.animation_data
.action
:
208 path
= m_constraint
.path_from_id("targetPoint")
209 m_fcurves
= [fcurve
for fcurve
in obj
.data
.animation_data
.action
.fcurves
if fcurve
.data_path
== path
]
211 constraint_settings
= True
212 xCurves
, yCurves
, zCurves
= createConstraintFCurves(cons_obj
, obj
, real_constraint
)
213 for curve
in xCurves
:
214 copyFCurve(curve
, m_fcurves
[0])
215 for curve
in yCurves
:
216 copyFCurve(curve
, m_fcurves
[1])
217 for curve
in zCurves
:
218 copyFCurve(curve
, m_fcurves
[2])
219 if m_constraint
.targetSpace
== "constrained_boneB" and m_constraint
.constrained_boneB
:
220 c_frame
= context
.scene
.frame_current
222 src_bone
= bones
[m_constraint
.constrained_boneB
]
223 if not constraint_settings
:
224 xCurves
, yCurves
, zCurves
= createConstraintFCurves(cons_obj
, obj
, real_constraint
)
225 print("please wait a moment, calculating fix")
226 for t
in range(s
, e
):
227 context
.scene
.frame_set(t
)
228 src_bone_pos
= src_bone
.matrix
.to_translation()
229 bakedPos
[t
] = src_bone_pos
+ m_constraint
.targetPoint
# final position for constrained bone in object space
230 context
.scene
.frame_set(c_frame
)
231 for frame
in bakedPos
.keys():
232 pos
= bakedPos
[frame
]
233 for xCurve
in xCurves
:
234 xCurve
.keyframe_points
.insert(frame
=frame
, value
=pos
.x
)
235 for yCurve
in yCurves
:
236 yCurve
.keyframe_points
.insert(frame
=frame
, value
=pos
.y
)
237 for zCurve
in zCurves
:
238 zCurve
.keyframe_points
.insert(frame
=frame
, value
=pos
.z
)
240 if not constraint_settings
:
241 x
, y
, z
= m_constraint
.targetPoint
242 real_constraint
.max_x
= x
243 real_constraint
.max_y
= y
244 real_constraint
.max_z
= z
245 real_constraint
.min_x
= x
246 real_constraint
.min_y
= y
247 real_constraint
.min_z
= z
248 real_constraint
.use_max_x
= True
249 real_constraint
.use_max_y
= True
250 real_constraint
.use_max_z
= True
251 real_constraint
.use_min_x
= True
252 real_constraint
.use_min_y
= True
253 real_constraint
.use_min_z
= True
255 if m_constraint
.type == "freeze":
256 context
.scene
.frame_set(s
)
257 real_constraint
.owner_space
= m_constraint
.targetSpace
258 bpy
.context
.scene
.frame_set(m_constraint
.s_frame
)
259 if isinstance(cons_obj
, bpy
.types
.PoseBone
):
260 vec
= obj
.matrix_world
* (cons_obj
.matrix
.to_translation())
262 #~ vec = obj.parent.matrix_world * vec
265 x
, y
, z
= cons_obj
.matrix_world
.to_translation()
267 real_constraint
.max_x
= x
268 real_constraint
.max_y
= y
269 real_constraint
.max_z
= z
270 real_constraint
.min_x
= x
271 real_constraint
.min_y
= y
272 real_constraint
.min_z
= z
273 real_constraint
.use_max_x
= True
274 real_constraint
.use_max_y
= True
275 real_constraint
.use_max_z
= True
276 real_constraint
.use_min_x
= True
277 real_constraint
.use_min_y
= True
278 real_constraint
.use_min_z
= True
280 if m_constraint
.type == "distance" and m_constraint
.constrained_boneB
:
281 real_constraint
.owner_space
= "WORLD"
282 real_constraint
.target
= obj
283 real_constraint
.subtarget
= getConsObj(bones
[m_constraint
.constrained_boneB
]).name
284 real_constraint
.limit_mode
= "LIMITDIST_ONSURFACE"
285 if m_constraint
.targetDist
< 0.01:
286 m_constraint
.targetDist
= 0.01
287 real_constraint
.distance
= m_constraint
.targetDist
289 if m_constraint
.type == "floor" and m_constraint
.targetMesh
:
290 real_constraint
.mute
= True
291 real_constraint
.owner_space
= "WORLD"
292 #calculate the positions thoughout the range
293 s
, e
= m_constraint
.s_frame
, m_constraint
.e_frame
294 s_in
, s_out
= m_constraint
.smooth_in
, m_constraint
.smooth_out
298 floor
= bpy
.data
.objects
[m_constraint
.targetMesh
]
299 c_frame
= context
.scene
.frame_current
300 print("please wait a moment, calculating fix")
301 for t
in range(s
, e
):
302 context
.scene
.frame_set(t
)
303 axis
= obj
.matrix_world
.to_3x3() * Vector((0, 0, 100))
304 offset
= obj
.matrix_world
.to_3x3() * Vector((0, 0, m_constraint
.targetDist
))
305 ray_origin
= (cons_obj
.matrix
* obj
.matrix_world
).to_translation() - offset
# world position of constrained bone
306 ray_target
= ray_origin
+ axis
307 #convert ray points to floor's object space
308 ray_origin
= floor
.matrix_world
.inverted() * ray_origin
309 ray_target
= floor
.matrix_world
.inverted() * ray_target
310 hit
, nor
, ind
= floor
.ray_cast(ray_origin
, ray_target
)
311 if hit
!= Vector((0, 0, 0)):
312 bakedPos
[t
] = (floor
.matrix_world
* hit
)
313 bakedPos
[t
] += Vector((0, 0, m_constraint
.targetDist
))
315 bakedPos
[t
] = (cons_obj
.matrix
* obj
.matrix_world
).to_translation()
316 context
.scene
.frame_set(c_frame
)
317 #create keyframes for real constraint
318 xCurves
, yCurves
, zCurves
= createConstraintFCurves(cons_obj
, obj
, real_constraint
)
319 for frame
in bakedPos
.keys():
320 pos
= bakedPos
[frame
]
321 for xCurve
in xCurves
:
322 xCurve
.keyframe_points
.insert(frame
=frame
, value
=pos
.x
)
323 for yCurve
in yCurves
:
324 yCurve
.keyframe_points
.insert(frame
=frame
, value
=pos
.y
)
325 for zCurve
in zCurves
:
326 zCurve
.keyframe_points
.insert(frame
=frame
, value
=pos
.z
)
327 real_constraint
.use_max_x
= True
328 real_constraint
.use_max_y
= True
329 real_constraint
.use_max_z
= True
330 real_constraint
.use_min_x
= True
331 real_constraint
.use_min_y
= True
332 real_constraint
.use_min_z
= True
335 real_constraint
.mute
= (not m_constraint
.active
)
338 def locBake(s_frame
, e_frame
, bones
):
339 scene
= bpy
.context
.scene
342 bakeDict
[bone
.name
] = {}
343 for t
in range(s_frame
, e_frame
):
346 bakeDict
[bone
.name
][t
] = bone
.matrix
.copy()
347 for t
in range(s_frame
, e_frame
):
349 print(bone
.bone
.matrix_local
.to_translation())
350 bone
.matrix
= bakeDict
[bone
.name
][t
]
351 bone
.keyframe_insert("location", frame
=t
)
354 # Baking function which bakes all bones effected by the constraint
355 def bakeAllConstraints(obj
, s_frame
, e_frame
, bones
):
357 bone
.bone
.select
= False
358 selectedBones
= [] # Marks bones that need a full bake
359 simpleBake
= [] # Marks bones that need only a location bake
360 for end_bone
in bones
:
361 if end_bone
.name
in [m_constraint
.real_constraint_bone
for m_constraint
in obj
.data
.mocap_constraints
]:
362 #For all bones that have a constraint:
363 ik
= retarget
.hasIKConstraint(end_bone
)
364 cons_obj
= getConsObj(end_bone
)
366 #If it's an auto generated IK:
367 if ik
.chain_count
== 0:
368 selectedBones
+= bones
# Chain len 0, bake everything
370 selectedBones
+= [end_bone
] + end_bone
.parent_recursive
[:ik
.chain_count
- 1] # Bake the chain
372 #It's either an FK bone which we should just bake
373 #OR a user created IK target bone
374 simpleBake
+= [end_bone
]
375 for bone
in selectedBones
:
376 bone
.bone
.select
= True
377 NLATracks
= obj
.data
.mocapNLATracks
[obj
.data
.active_mocap
]
378 obj
.animation_data
.action
= bpy
.data
.actions
[NLATracks
.auto_fix_track
]
379 constraintTrack
= obj
.animation_data
.nla_tracks
[NLATracks
.auto_fix_track
]
380 constraintStrip
= constraintTrack
.strips
[0]
381 constraintStrip
.action_frame_start
= s_frame
382 constraintStrip
.action_frame_end
= e_frame
383 constraintStrip
.frame_start
= s_frame
384 constraintStrip
.frame_end
= e_frame
386 # Use bake function from NLA Bake Action operator
387 anim_utils
.bake_action(s_frame
,
389 action
=constraintStrip
.action
,
395 #Do a "simple" bake, location only, world space only.
396 locBake(s_frame
, e_frame
, simpleBake
)
399 #Calls the baking function and decativates releveant constraints
400 def bakeConstraints(context
):
401 obj
= context
.active_object
402 bones
= obj
.pose
.bones
403 s_frame
, e_frame
= context
.scene
.frame_start
, context
.scene
.frame_end
405 bakeAllConstraints(obj
, s_frame
, e_frame
, bones
)
406 for m_constraint
in obj
.data
.mocap_constraints
:
407 end_bone
= bones
[m_constraint
.real_constraint_bone
]
408 cons_obj
= getConsObj(end_bone
)
409 # It's a control empty: turn the ik off
410 if not isinstance(cons_obj
, bpy
.types
.PoseBone
):
411 ik_con
= retarget
.hasIKConstraint(end_bone
)
414 # Deactivate related Blender Constraint
415 m_constraint
.active
= False
418 #Deletes the baked fcurves and reactivates relevant constraints
419 def unbakeConstraints(context
):
420 # to unbake constraints we delete the whole strip
421 obj
= context
.active_object
422 bones
= obj
.pose
.bones
423 scene
= bpy
.context
.scene
424 NLATracks
= obj
.data
.mocapNLATracks
[obj
.data
.active_mocap
]
425 obj
.animation_data
.action
= bpy
.data
.actions
[NLATracks
.auto_fix_track
]
426 constraintTrack
= obj
.animation_data
.nla_tracks
[NLATracks
.auto_fix_track
]
427 constraintStrip
= constraintTrack
.strips
[0]
428 action
= constraintStrip
.action
429 # delete the fcurves on the strip
430 for fcurve
in action
.fcurves
:
431 action
.fcurves
.remove(fcurve
)
432 # reactivate relevant constraints
433 for m_constraint
in obj
.data
.mocap_constraints
:
434 end_bone
= bones
[m_constraint
.real_constraint_bone
]
435 cons_obj
= getConsObj(end_bone
)
436 # It's a control empty: turn the ik back on
437 if not isinstance(cons_obj
, bpy
.types
.PoseBone
):
438 ik_con
= retarget
.hasIKConstraint(end_bone
)
441 m_constraint
.active
= True
444 def updateConstraints(obj
, context
):
445 fixes
= obj
.data
.mocap_constraints