Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender-addons.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBrendon Murphy <meta.androcto1@gmail.com>2011-08-24 15:45:59 +0400
committerBrendon Murphy <meta.androcto1@gmail.com>2011-08-24 15:45:59 +0400
commit8f5fe7f748f924d504f708b89a4f143de1b636cc (patch)
tree49dbf70f627de7e1ac5358c05a8cbe41534e2cc7 /mocap/mocap_constraints.py
parentcf3881371a5c64d56f352cf09395f93072889a7c (diff)
committing Motion Capture Add-on - Final deliverable for GSoC project 2011
thanks to Benjy Cook well done!
Diffstat (limited to 'mocap/mocap_constraints.py')
-rw-r--r--mocap/mocap_constraints.py442
1 files changed, 442 insertions, 0 deletions
diff --git a/mocap/mocap_constraints.py b/mocap/mocap_constraints.py
new file mode 100644
index 00000000..5e349454
--- /dev/null
+++ b/mocap/mocap_constraints.py
@@ -0,0 +1,442 @@
+# ##### BEGIN GPL LICENSE BLOCK #####
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# ##### END GPL LICENSE BLOCK #####
+
+# <pep8 compliant>
+
+import bpy
+from mathutils import *
+from bl_operators import nla
+from . import retarget
+
+
+### Utility Functions
+
+
+def getConsObj(bone):
+ #utility function - returns related IK target if bone has IK
+ ik = [constraint for constraint in bone.constraints if constraint.type == "IK"]
+ if ik:
+ ik = ik[0]
+ cons_obj = ik.target
+ if ik.subtarget:
+ cons_obj = ik.target.pose.bones[ik.subtarget]
+ else:
+ cons_obj = bone
+ return cons_obj
+
+
+def consObjToBone(cons_obj):
+ #Utility function - returns related bone from ik object
+ if cons_obj.name[-3:] == "Org":
+ return cons_obj.name[:-3]
+ else:
+ return cons_obj.name
+
+### And and Remove Constraints (called from operators)
+
+
+def addNewConstraint(m_constraint, cons_obj):
+ #Decide the correct Blender constraint according to the Mocap constraint type
+ if m_constraint.type == "point" or m_constraint.type == "freeze":
+ c_type = "LIMIT_LOCATION"
+ if m_constraint.type == "distance":
+ c_type = "LIMIT_DISTANCE"
+ if m_constraint.type == "floor":
+ c_type = "LIMIT_LOCATION"
+ #create and store the new constraint within m_constraint
+ real_constraint = cons_obj.constraints.new(c_type)
+ real_constraint.name = "Auto fixes " + str(len(cons_obj.constraints))
+ m_constraint.real_constraint_bone = consObjToBone(cons_obj)
+ m_constraint.real_constraint = real_constraint.name
+ #set the rest of the constraint properties
+ setConstraint(m_constraint, bpy.context)
+
+
+def removeConstraint(m_constraint, cons_obj):
+ #remove the influence fcurve and Blender constraint
+ oldConstraint = cons_obj.constraints[m_constraint.real_constraint]
+ removeFcurves(cons_obj, bpy.context.active_object, oldConstraint, m_constraint)
+ cons_obj.constraints.remove(oldConstraint)
+
+### Update functions. There are 3: UpdateType/Bone
+### update framing (deals with changes in the desired frame range)
+### And setConstraint which deals with the rest
+
+
+def updateConstraintBoneType(m_constraint, context):
+ #If the constraint exists, we need to remove it
+ #from the old bone
+ obj = context.active_object
+ bones = obj.pose.bones
+ if m_constraint.real_constraint:
+ bone = bones[m_constraint.real_constraint_bone]
+ cons_obj = getConsObj(bone)
+ removeConstraint(m_constraint, cons_obj)
+ #Regardless, after that we create a new constraint
+ if m_constraint.constrained_bone:
+ bone = bones[m_constraint.constrained_bone]
+ cons_obj = getConsObj(bone)
+ addNewConstraint(m_constraint, cons_obj)
+
+
+def setConstraintFraming(m_constraint, context):
+ obj = context.active_object
+ bones = obj.pose.bones
+ bone = bones[m_constraint.constrained_bone]
+ cons_obj = getConsObj(bone)
+ real_constraint = cons_obj.constraints[m_constraint.real_constraint]
+ #remove the old keyframes
+ removeFcurves(cons_obj, obj, real_constraint, m_constraint)
+ #set the new ones according to the m_constraint properties
+ s, e = m_constraint.s_frame, m_constraint.e_frame
+ s_in, s_out = m_constraint.smooth_in, m_constraint.smooth_out
+ real_constraint.influence = 1
+ real_constraint.keyframe_insert(data_path="influence", frame=s)
+ real_constraint.keyframe_insert(data_path="influence", frame=e)
+ real_constraint.influence = 0
+ real_constraint.keyframe_insert(data_path="influence", frame=s - s_in)
+ real_constraint.keyframe_insert(data_path="influence", frame=e + s_out)
+
+
+def removeFcurves(cons_obj, obj, real_constraint, m_constraint):
+ #Determine if the constrained object is a bone or an empty
+ if isinstance(cons_obj, bpy.types.PoseBone):
+ fcurves = obj.animation_data.action.fcurves
+ else:
+ fcurves = cons_obj.animation_data.action.fcurves
+ #Find the RNA data path of the constraint's influence
+ RNA_paths = []
+ RNA_paths.append(real_constraint.path_from_id("influence"))
+ if m_constraint.type == "floor" or m_constraint.type == "point":
+ RNA_paths += [real_constraint.path_from_id("max_x"), real_constraint.path_from_id("min_x")]
+ RNA_paths += [real_constraint.path_from_id("max_y"), real_constraint.path_from_id("min_y")]
+ RNA_paths += [real_constraint.path_from_id("max_z"), real_constraint.path_from_id("min_z")]
+ #Retrieve the correct fcurve via the RNA data path and remove it
+ fcurves_del = [fcurve for fcurve in fcurves if fcurve.data_path in RNA_paths]
+ #clear the fcurve and set the frames.
+ if fcurves_del:
+ for fcurve in fcurves_del:
+ fcurves.remove(fcurve)
+ #remove armature fcurves (if user keyframed m_constraint properties)
+ if obj.data.animation_data and m_constraint.type == "point":
+ if obj.data.animation_data.action:
+ path = m_constraint.path_from_id("targetPoint")
+ m_fcurves = [fcurve for fcurve in obj.data.animation_data.action.fcurves if fcurve.data_path == path]
+ for curve in m_fcurves:
+ obj.data.animation_data.action.fcurves.remove(curve)
+
+#Utility function for copying property fcurves over
+
+
+def copyFCurve(newCurve, oldCurve):
+ for point in oldCurve.keyframe_points:
+ newCurve.keyframe_points.insert(frame=point.co.x, value=point.co.y)
+
+#Creates new fcurves for the constraint properties (for floor and point)
+
+
+def createConstraintFCurves(cons_obj, obj, real_constraint):
+ if isinstance(cons_obj, bpy.types.PoseBone):
+ c_fcurves = obj.animation_data.action.fcurves
+ else:
+ c_fcurves = cons_obj.animation_data.action.fcurves
+ c_x_path = [real_constraint.path_from_id("max_x"), real_constraint.path_from_id("min_x")]
+ c_y_path = [real_constraint.path_from_id("max_y"), real_constraint.path_from_id("min_y")]
+ c_z_path = [real_constraint.path_from_id("max_z"), real_constraint.path_from_id("min_z")]
+ c_constraints_path = c_x_path + c_y_path + c_z_path
+ existing_curves = [fcurve for fcurve in c_fcurves if fcurve.data_path in c_constraints_path]
+ if existing_curves:
+ for curve in existing_curves:
+ c_fcurves.remove(curve)
+ xCurves, yCurves, zCurves = [], [], []
+ for path in c_constraints_path:
+ newCurve = c_fcurves.new(path)
+ if path in c_x_path:
+ xCurves.append(newCurve)
+ elif path in c_y_path:
+ yCurves.append(newCurve)
+ else:
+ zCurves.append(newCurve)
+ return xCurves, yCurves, zCurves
+
+
+# Function that copies all settings from m_constraint to the real Blender constraints
+# Is only called when blender constraint already exists
+
+
+def setConstraint(m_constraint, context):
+ if not m_constraint.constrained_bone:
+ return
+ obj = context.active_object
+ bones = obj.pose.bones
+ bone = bones[m_constraint.constrained_bone]
+ cons_obj = getConsObj(bone)
+ real_constraint = cons_obj.constraints[m_constraint.real_constraint]
+ NLATracks = obj.data.mocapNLATracks[obj.data.active_mocap]
+ obj.animation_data.action = bpy.data.actions[NLATracks.auto_fix_track]
+
+ #frame changing section
+ setConstraintFraming(m_constraint, context)
+ s, e = m_constraint.s_frame, m_constraint.e_frame
+ s_in, s_out = m_constraint.smooth_in, m_constraint.smooth_out
+ s -= s_in
+ e += s_out
+ #Set the blender constraint parameters
+ if m_constraint.type == "point":
+ constraint_settings = False # are fix settings keyframed?
+ if not m_constraint.targetSpace == "constrained_boneB":
+ real_constraint.owner_space = m_constraint.targetSpace
+ else:
+ real_constraint.owner_space = "LOCAL"
+ if obj.data.animation_data:
+ if obj.data.animation_data.action:
+ path = m_constraint.path_from_id("targetPoint")
+ m_fcurves = [fcurve for fcurve in obj.data.animation_data.action.fcurves if fcurve.data_path == path]
+ if m_fcurves:
+ constraint_settings = True
+ xCurves, yCurves, zCurves = createConstraintFCurves(cons_obj, obj, real_constraint)
+ for curve in xCurves:
+ copyFCurve(curve, m_fcurves[0])
+ for curve in yCurves:
+ copyFCurve(curve, m_fcurves[1])
+ for curve in zCurves:
+ copyFCurve(curve, m_fcurves[2])
+ if m_constraint.targetSpace == "constrained_boneB" and m_constraint.constrained_boneB:
+ c_frame = context.scene.frame_current
+ bakedPos = {}
+ src_bone = bones[m_constraint.constrained_boneB]
+ if not constraint_settings:
+ xCurves, yCurves, zCurves = createConstraintFCurves(cons_obj, obj, real_constraint)
+ print("please wait a moment, calculating fix")
+ for t in range(s, e):
+ context.scene.frame_set(t)
+ src_bone_pos = src_bone.matrix.to_translation()
+ bakedPos[t] = src_bone_pos + m_constraint.targetPoint # final position for constrained bone in object space
+ context.scene.frame_set(c_frame)
+ for frame in bakedPos.keys():
+ pos = bakedPos[frame]
+ for xCurve in xCurves:
+ xCurve.keyframe_points.insert(frame=frame, value=pos.x)
+ for yCurve in yCurves:
+ yCurve.keyframe_points.insert(frame=frame, value=pos.y)
+ for zCurve in zCurves:
+ zCurve.keyframe_points.insert(frame=frame, value=pos.z)
+
+ if not constraint_settings:
+ x, y, z = m_constraint.targetPoint
+ real_constraint.max_x = x
+ real_constraint.max_y = y
+ real_constraint.max_z = z
+ real_constraint.min_x = x
+ real_constraint.min_y = y
+ real_constraint.min_z = z
+ real_constraint.use_max_x = True
+ real_constraint.use_max_y = True
+ real_constraint.use_max_z = True
+ real_constraint.use_min_x = True
+ real_constraint.use_min_y = True
+ real_constraint.use_min_z = True
+
+ if m_constraint.type == "freeze":
+ context.scene.frame_set(s)
+ real_constraint.owner_space = m_constraint.targetSpace
+ bpy.context.scene.frame_set(m_constraint.s_frame)
+ if isinstance(cons_obj, bpy.types.PoseBone):
+ vec = obj.matrix_world * (cons_obj.matrix.to_translation())
+ #~ if obj.parent:
+ #~ vec = obj.parent.matrix_world * vec
+ x, y, z = vec
+ else:
+ x, y, z = cons_obj.matrix_world.to_translation()
+
+ real_constraint.max_x = x
+ real_constraint.max_y = y
+ real_constraint.max_z = z
+ real_constraint.min_x = x
+ real_constraint.min_y = y
+ real_constraint.min_z = z
+ real_constraint.use_max_x = True
+ real_constraint.use_max_y = True
+ real_constraint.use_max_z = True
+ real_constraint.use_min_x = True
+ real_constraint.use_min_y = True
+ real_constraint.use_min_z = True
+
+ if m_constraint.type == "distance" and m_constraint.constrained_boneB:
+ real_constraint.owner_space = "WORLD"
+ real_constraint.target = obj
+ real_constraint.subtarget = getConsObj(bones[m_constraint.constrained_boneB]).name
+ real_constraint.limit_mode = "LIMITDIST_ONSURFACE"
+ if m_constraint.targetDist<0.01:
+ m_constraint.targetDist = 0.01
+ real_constraint.distance = m_constraint.targetDist
+
+ if m_constraint.type == "floor" and m_constraint.targetMesh:
+ real_constraint.mute = True
+ real_constraint.owner_space = "WORLD"
+ #calculate the positions thoughout the range
+ s, e = m_constraint.s_frame, m_constraint.e_frame
+ s_in, s_out = m_constraint.smooth_in, m_constraint.smooth_out
+ s -= s_in
+ e += s_out
+ bakedPos = {}
+ floor = bpy.data.objects[m_constraint.targetMesh]
+ c_frame = context.scene.frame_current
+ print("please wait a moment, calculating fix")
+ for t in range(s, e):
+ context.scene.frame_set(t)
+ axis = obj.matrix_world.to_3x3() * Vector((0, 0, 100))
+ offset = obj.matrix_world.to_3x3() * Vector((0, 0, m_constraint.targetDist))
+ ray_origin = (cons_obj.matrix * obj.matrix_world).to_translation() - offset # world position of constrained bone
+ ray_target = ray_origin + axis
+ #convert ray points to floor's object space
+ ray_origin = floor.matrix_world.inverted() * ray_origin
+ ray_target = floor.matrix_world.inverted() * ray_target
+ hit, nor, ind = floor.ray_cast(ray_origin, ray_target)
+ if hit != Vector((0, 0, 0)):
+ bakedPos[t] = (floor.matrix_world * hit)
+ bakedPos[t] += Vector((0, 0, m_constraint.targetDist))
+ else:
+ bakedPos[t] = (cons_obj.matrix * obj.matrix_world).to_translation()
+ context.scene.frame_set(c_frame)
+ #create keyframes for real constraint
+ xCurves, yCurves, zCurves = createConstraintFCurves(cons_obj, obj, real_constraint)
+ for frame in bakedPos.keys():
+ pos = bakedPos[frame]
+ for xCurve in xCurves:
+ xCurve.keyframe_points.insert(frame=frame, value=pos.x)
+ for yCurve in yCurves:
+ yCurve.keyframe_points.insert(frame=frame, value=pos.y)
+ for zCurve in zCurves:
+ zCurve.keyframe_points.insert(frame=frame, value=pos.z)
+ real_constraint.use_max_x = True
+ real_constraint.use_max_y = True
+ real_constraint.use_max_z = True
+ real_constraint.use_min_x = True
+ real_constraint.use_min_y = True
+ real_constraint.use_min_z = True
+
+ # active/baked check
+ real_constraint.mute = (not m_constraint.active)
+
+
+def locBake(s_frame, e_frame, bones):
+ scene = bpy.context.scene
+ bakeDict = {}
+ for bone in bones:
+ bakeDict[bone.name] = {}
+ for t in range(s_frame, e_frame):
+ scene.frame_set(t)
+ for bone in bones:
+ bakeDict[bone.name][t] = bone.matrix.copy()
+ for t in range(s_frame, e_frame):
+ for bone in bones:
+ print(bone.bone.matrix_local.to_translation())
+ bone.matrix = bakeDict[bone.name][t]
+ bone.keyframe_insert("location", frame=t)
+
+
+# Baking function which bakes all bones effected by the constraint
+def bakeAllConstraints(obj, s_frame, e_frame, bones):
+ for bone in bones:
+ bone.bone.select = False
+ selectedBones = [] # Marks bones that need a full bake
+ simpleBake = [] # Marks bones that need only a location bake
+ for end_bone in bones:
+ if end_bone.name in [m_constraint.real_constraint_bone for m_constraint in obj.data.mocap_constraints]:
+ #For all bones that have a constraint:
+ ik = retarget.hasIKConstraint(end_bone)
+ cons_obj = getConsObj(end_bone)
+ if ik:
+ #If it's an auto generated IK:
+ if ik.chain_count == 0:
+ selectedBones += bones # Chain len 0, bake everything
+ else:
+ selectedBones += [end_bone] + end_bone.parent_recursive[:ik.chain_count - 1] # Bake the chain
+ else:
+ #It's either an FK bone which we should just bake
+ #OR a user created IK target bone
+ simpleBake += [end_bone]
+ for bone in selectedBones:
+ bone.bone.select = True
+ NLATracks = obj.data.mocapNLATracks[obj.data.active_mocap]
+ obj.animation_data.action = bpy.data.actions[NLATracks.auto_fix_track]
+ constraintTrack = obj.animation_data.nla_tracks[NLATracks.auto_fix_track]
+ constraintStrip = constraintTrack.strips[0]
+ constraintStrip.action_frame_start = s_frame
+ constraintStrip.action_frame_end = e_frame
+ constraintStrip.frame_start = s_frame
+ constraintStrip.frame_end = e_frame
+ if selectedBones:
+ #Use bake function from NLA Bake Action operator
+ nla.bake(s_frame, e_frame, action=constraintStrip.action, only_selected=True, do_pose=True, do_object=False)
+ if simpleBake:
+ #Do a "simple" bake, location only, world space only.
+ locBake(s_frame, e_frame, simpleBake)
+
+
+#Calls the baking function and decativates releveant constraints
+def bakeConstraints(context):
+ obj = context.active_object
+ bones = obj.pose.bones
+ s_frame, e_frame = context.scene.frame_start, context.scene.frame_end
+ #Bake relevant bones
+ bakeAllConstraints(obj, s_frame, e_frame, bones)
+ for m_constraint in obj.data.mocap_constraints:
+ end_bone = bones[m_constraint.real_constraint_bone]
+ cons_obj = getConsObj(end_bone)
+ # It's a control empty: turn the ik off
+ if not isinstance(cons_obj, bpy.types.PoseBone):
+ ik_con = retarget.hasIKConstraint(end_bone)
+ if ik_con:
+ ik_con.mute = True
+ # Deactivate related Blender Constraint
+ m_constraint.active = False
+
+
+#Deletes the baked fcurves and reactivates relevant constraints
+def unbakeConstraints(context):
+ # to unbake constraints we delete the whole strip
+ obj = context.active_object
+ bones = obj.pose.bones
+ scene = bpy.context.scene
+ NLATracks = obj.data.mocapNLATracks[obj.data.active_mocap]
+ obj.animation_data.action = bpy.data.actions[NLATracks.auto_fix_track]
+ constraintTrack = obj.animation_data.nla_tracks[NLATracks.auto_fix_track]
+ constraintStrip = constraintTrack.strips[0]
+ action = constraintStrip.action
+ # delete the fcurves on the strip
+ for fcurve in action.fcurves:
+ action.fcurves.remove(fcurve)
+ # reactivate relevant constraints
+ for m_constraint in obj.data.mocap_constraints:
+ end_bone = bones[m_constraint.real_constraint_bone]
+ cons_obj = getConsObj(end_bone)
+ # It's a control empty: turn the ik back on
+ if not isinstance(cons_obj, bpy.types.PoseBone):
+ ik_con = retarget.hasIKConstraint(end_bone)
+ if ik_con:
+ ik_con.mute = False
+ m_constraint.active = True
+
+
+def updateConstraints(obj, context):
+ fixes = obj.data.mocap_constraints
+ for fix in fixes:
+ fix.active = False
+ fix.active = True