From 46f938e70b081d20da5dac2cbaca6900d9e9b2ad Mon Sep 17 00:00:00 2001 From: Benjy Cook Date: Thu, 7 Jul 2011 20:46:35 +0000 Subject: Added baking/unbaking functionality to constraint system. Retargeting now adds/manages 2 new NLA Tracks as planned. Modified bl_operatores/nla.py slightly to use it instead of creating my own bake function (now supports baking to a specific action, vs always creating a new one), but this does not break using the function in the old way. --- release/scripts/modules/mocap_constraints.py | 95 +++++++++++++++++++++++++++- release/scripts/modules/retarget.py | 15 +++++ release/scripts/startup/bl_operators/nla.py | 26 ++++---- release/scripts/startup/ui_mocap.py | 12 +--- 4 files changed, 124 insertions(+), 24 deletions(-) (limited to 'release/scripts') diff --git a/release/scripts/modules/mocap_constraints.py b/release/scripts/modules/mocap_constraints.py index ec587c987f6..07ebb01ea3d 100644 --- a/release/scripts/modules/mocap_constraints.py +++ b/release/scripts/modules/mocap_constraints.py @@ -20,6 +20,7 @@ import bpy from mathutils import * +from bl_operators import nla ### Utility Functions @@ -96,7 +97,7 @@ def updateConstraintBoneType(m_constraint, context): # Function that copies all settings from m_constraint to the real Blender constraints # Is only called when blender constraint already exists -def setConstraintFraming(m_constraint, cons_obj, real_constraint): +def setConstraintFraming(m_constraint, cons_obj, obj, real_constraint): if isinstance(cons_obj, bpy.types.PoseBone): fcurves = obj.animation_data.action.fcurves else: @@ -129,7 +130,7 @@ def setConstraint(m_constraint): real_constraint = cons_obj.constraints[m_constraint.real_constraint] #frame changing section - setConstraintFraming(m_constraint, cons_obj, real_constraint) + setConstraintFraming(m_constraint, cons_obj, obj, real_constraint) #Set the blender constraint parameters if m_constraint.type == "point": @@ -176,4 +177,92 @@ def setConstraint(m_constraint): real_constraint.distance = m_constraint.targetDist # active check - real_constraint.mute = not m_constraint.active + real_constraint.mute = (not m_constraint.active) and (m_constraint.baked) + + +def updateBake(self, context): + if self.baked: + print("baking...") + bakeConstraint(self) + else: + print("unbaking...") + unbakeConstraint(self) + + +def bakeTransformFK(anim_data, s_frame, e_frame, end_bone, bones, cons_obj): + mute_ik = False + for bone in bones: + bone.bone.select = False + ik = hasIKConstraint(end_bone) + if not isinstance(cons_obj, bpy.types.PoseBone) and ik: + if ik.chain_count == 0: + selectedBones = bones + else: + selectedBones = [end_bone] + end_bone.parent_recursive[:ik.chain_count - 1] + mute_ik = True + else: + selectedBones = [end_bone] + print(selectedBones) + for bone in selectedBones: + bone.bone.select = True + anim_data.action = nla.bake(s_frame, + e_frame, action=anim_data.action) + return mute_ik + + +def bakeConstraint(m_constraint): + obj = bpy.context.active_object + bones = obj.pose.bones + end_bone = bones[m_constraint.constrained_bone] + cons_obj = getConsObj(end_bone) + scene = bpy.context.scene + s_frame = scene.frame_start + e_frame = scene.frame_end + mute_ik = bakeTransformFK(obj.animation_data, s_frame, e_frame, end_bone, bones, cons_obj) + if mute_ik: + ik_con = hasIKConstraint(end_bone) + ik_con.mute = True + real_constraint = cons_obj.constraints[m_constraint.real_constraint] + real_constraint.mute = True + constraintTrack = obj.animation_data.nla_tracks["Mocap constraints"] + 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 + + +def unbakeConstraint(m_constraint): + # to unbake a constraint we need to delete the whole strip + # and rebake all the other constraints + obj = bpy.context.active_object + bones = obj.pose.bones + end_bone = bones[m_constraint.constrained_bone] + cons_obj = getConsObj(end_bone) + scene = bpy.context.scene + s_frame = scene.frame_start + e_frame = scene.frame_end + constraintTrack = obj.animation_data.nla_tracks["Mocap constraints"] + constraintStrip = constraintTrack.strips[0] + action = constraintStrip.action + for fcurve in action.fcurves: + action.fcurves.remove(fcurve) + for other_m_constraint in obj.data.mocap_constraints: + if m_constraint != other_m_constraint: + bakeConstraint(other_m_constraint) + # It's a control empty: turn the ik back on + if not isinstance(cons_obj, bpy.types.PoseBone): + ik_con = hasIKConstraint(end_bone) + if ik_con: + ik_con.mute = False + real_constraint = cons_obj.constraints[m_constraint.real_constraint] + real_constraint.mute = False + + +def hasIKConstraint(pose_bone): + #utility function / predicate, returns True if given bone has IK constraint + ik = [constraint for constraint in pose_bone.constraints if constraint.type == "IK"] + if ik: + return ik[0] + else: + return False diff --git a/release/scripts/modules/retarget.py b/release/scripts/modules/retarget.py index 6409c5ed535..885a457061a 100644 --- a/release/scripts/modules/retarget.py +++ b/release/scripts/modules/retarget.py @@ -374,6 +374,21 @@ def totalRetarget(): bpy.ops.object.mode_set(mode='OBJECT') bpy.ops.object.select_name(name=inter_obj.name, extend=False) bpy.ops.object.delete() + anim_data = enduser_obj.animation_data + mocapAction = anim_data.action + mocapAction.name = "Base Mocap Action" + anim_data.use_nla = True + mocapTrack = anim_data.nla_tracks.new() + mocapTrack.name = "Base Mocap Track" + mocapStrip = mocapTrack.strips.new("Base Mocap Action", s_frame, mocapAction) + constraintTrack = anim_data.nla_tracks.new() + constraintTrack.name = "Mocap constraints" + constraintAction = bpy.data.actions.new("Mocap constraints Action") + constraintStrip = constraintTrack.strips.new("Mocap constraints Action", s_frame, constraintAction) + #constraintStrip.frame_end = e_frame + anim_data.nla_tracks.active = constraintTrack + anim_data.action = constraintAction + if __name__ == "__main__": totalRetarget() diff --git a/release/scripts/startup/bl_operators/nla.py b/release/scripts/startup/bl_operators/nla.py index 7a5fa552ede..e0b5a11dc78 100644 --- a/release/scripts/startup/bl_operators/nla.py +++ b/release/scripts/startup/bl_operators/nla.py @@ -83,7 +83,7 @@ def bake(frame_start, do_pose=True, do_object=True, do_constraint_clear=False, - ): + action=None): scene = bpy.context.scene obj = bpy.context.object @@ -120,7 +120,8 @@ def bake(frame_start, # incase animation data hassnt been created atd = obj.animation_data_create() - action = bpy.data.actions.new("Action") + if action == None: + action = bpy.data.actions.new("Action") atd.action = action if do_pose: @@ -253,37 +254,38 @@ class BakeAction(bpy.types.Operator): def invoke(self, context, event): wm = context.window_manager return wm.invoke_props_dialog(self) - + ################################# + class ClearUselessActions(bpy.types.Operator): '''Mark actions with no F-Curves for deletion after save+reload of file preserving "action libraries"''' bl_idname = "anim.clear_useless_actions" bl_label = "Clear Useless Actions" bl_options = {'REGISTER', 'UNDO'} - - only_unused = BoolProperty(name="Only Unused", + + only_unused = BoolProperty(name="Only Unused", description="Only unused (Fake User only) actions get considered", default=True) - + @classmethod def poll(cls, context): return len(bpy.data.actions) != 0 - + def execute(self, context): removed = 0 - + for action in bpy.data.actions: # if only user is "fake" user... - if ((self.only_unused is False) or + if ((self.only_unused is False) or (action.use_fake_user and action.users == 1)): - - # if it has F-Curves, then it's a "action library" (i.e. walk, wave, jump, etc.) + + # if it has F-Curves, then it's a "action library" (i.e. walk, wave, jump, etc.) # and should be left alone as that's what fake users are for! if not action.fcurves: # mark action for deletion action.user_clear() removed += 1 - + self.report({'INFO'}, "Removed %d empty and/or fake-user only Actions" % (removed)) return {'FINISHED'} diff --git a/release/scripts/startup/ui_mocap.py b/release/scripts/startup/ui_mocap.py index 737f3fcfa56..d750489191f 100644 --- a/release/scripts/startup/ui_mocap.py +++ b/release/scripts/startup/ui_mocap.py @@ -80,7 +80,7 @@ class MocapConstraint(bpy.types.PropertyGroup): baked = bpy.props.BoolProperty(name="Baked / Applied", default=False, description="Constraint has been baked to NLA layer", - update=updateConstraint) + update=updateBake) targetPoint = bpy.props.FloatVectorProperty(name="Point", size=3, subtype="XYZ", default=(0.0, 0.0, 0.0), description="Target of Constraint - Point", @@ -157,11 +157,6 @@ bpy.types.PoseBone.IKRetarget = bpy.props.BoolProperty(name="IK", update=toggleIKBone, default=False) -def hasIKConstraint(pose_bone): - #utility function / predicate, returns True if given bone has IK constraint - return ("IK" in [constraint.type for constraint in pose_bone.constraints]) - - def updateIKRetarget(): # ensures that Blender constraints and IK properties are in sync # currently runs when module is loaded, should run when scene is loaded @@ -230,8 +225,6 @@ class MocapPanel(bpy.types.Panel): else: row.label(" ") row.label(" ") - - self.layout.operator("mocap.retarget", text='RETARGET!') @@ -328,6 +321,7 @@ class OBJECT_OT_LimitDOFButton(bpy.types.Operator): def execute(self, context): return {"FINISHED"} + class OBJECT_OT_RotateFixArmature(bpy.types.Operator): bl_idname = "mocap.rotate_fix" bl_label = "Rotates selected armature 90 degrees (fix for bvh import)" @@ -335,7 +329,7 @@ class OBJECT_OT_RotateFixArmature(bpy.types.Operator): def execute(self, context): mocap_tools.rotate_fix_armature(context.active_object.data) return {"FINISHED"} - + #def poll(self, context): # return context.active_object.data in bpy.data.armatures -- cgit v1.2.3