forked from bartvdbraak/blender
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.
This commit is contained in:
parent
6f5b5ac3c9
commit
46f938e70b
@ -20,6 +20,7 @@
|
|||||||
|
|
||||||
import bpy
|
import bpy
|
||||||
from mathutils import *
|
from mathutils import *
|
||||||
|
from bl_operators import nla
|
||||||
|
|
||||||
### Utility Functions
|
### Utility Functions
|
||||||
|
|
||||||
@ -96,7 +97,7 @@ def updateConstraintBoneType(m_constraint, context):
|
|||||||
# Function that copies all settings from m_constraint to the real Blender constraints
|
# Function that copies all settings from m_constraint to the real Blender constraints
|
||||||
# Is only called when blender constraint already exists
|
# 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):
|
if isinstance(cons_obj, bpy.types.PoseBone):
|
||||||
fcurves = obj.animation_data.action.fcurves
|
fcurves = obj.animation_data.action.fcurves
|
||||||
else:
|
else:
|
||||||
@ -129,7 +130,7 @@ def setConstraint(m_constraint):
|
|||||||
real_constraint = cons_obj.constraints[m_constraint.real_constraint]
|
real_constraint = cons_obj.constraints[m_constraint.real_constraint]
|
||||||
|
|
||||||
#frame changing section
|
#frame changing section
|
||||||
setConstraintFraming(m_constraint, cons_obj, real_constraint)
|
setConstraintFraming(m_constraint, cons_obj, obj, real_constraint)
|
||||||
|
|
||||||
#Set the blender constraint parameters
|
#Set the blender constraint parameters
|
||||||
if m_constraint.type == "point":
|
if m_constraint.type == "point":
|
||||||
@ -176,4 +177,92 @@ def setConstraint(m_constraint):
|
|||||||
real_constraint.distance = m_constraint.targetDist
|
real_constraint.distance = m_constraint.targetDist
|
||||||
|
|
||||||
# active check
|
# 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
|
||||||
|
@ -374,6 +374,21 @@ def totalRetarget():
|
|||||||
bpy.ops.object.mode_set(mode='OBJECT')
|
bpy.ops.object.mode_set(mode='OBJECT')
|
||||||
bpy.ops.object.select_name(name=inter_obj.name, extend=False)
|
bpy.ops.object.select_name(name=inter_obj.name, extend=False)
|
||||||
bpy.ops.object.delete()
|
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__":
|
if __name__ == "__main__":
|
||||||
totalRetarget()
|
totalRetarget()
|
||||||
|
@ -83,7 +83,7 @@ def bake(frame_start,
|
|||||||
do_pose=True,
|
do_pose=True,
|
||||||
do_object=True,
|
do_object=True,
|
||||||
do_constraint_clear=False,
|
do_constraint_clear=False,
|
||||||
):
|
action=None):
|
||||||
|
|
||||||
scene = bpy.context.scene
|
scene = bpy.context.scene
|
||||||
obj = bpy.context.object
|
obj = bpy.context.object
|
||||||
@ -120,6 +120,7 @@ def bake(frame_start,
|
|||||||
|
|
||||||
# incase animation data hassnt been created
|
# incase animation data hassnt been created
|
||||||
atd = obj.animation_data_create()
|
atd = obj.animation_data_create()
|
||||||
|
if action == None:
|
||||||
action = bpy.data.actions.new("Action")
|
action = bpy.data.actions.new("Action")
|
||||||
atd.action = action
|
atd.action = action
|
||||||
|
|
||||||
@ -256,6 +257,7 @@ class BakeAction(bpy.types.Operator):
|
|||||||
|
|
||||||
#################################
|
#################################
|
||||||
|
|
||||||
|
|
||||||
class ClearUselessActions(bpy.types.Operator):
|
class ClearUselessActions(bpy.types.Operator):
|
||||||
'''Mark actions with no F-Curves for deletion after save+reload of file preserving "action libraries"'''
|
'''Mark actions with no F-Curves for deletion after save+reload of file preserving "action libraries"'''
|
||||||
bl_idname = "anim.clear_useless_actions"
|
bl_idname = "anim.clear_useless_actions"
|
||||||
|
@ -80,7 +80,7 @@ class MocapConstraint(bpy.types.PropertyGroup):
|
|||||||
baked = bpy.props.BoolProperty(name="Baked / Applied",
|
baked = bpy.props.BoolProperty(name="Baked / Applied",
|
||||||
default=False,
|
default=False,
|
||||||
description="Constraint has been baked to NLA layer",
|
description="Constraint has been baked to NLA layer",
|
||||||
update=updateConstraint)
|
update=updateBake)
|
||||||
targetPoint = bpy.props.FloatVectorProperty(name="Point", size=3,
|
targetPoint = bpy.props.FloatVectorProperty(name="Point", size=3,
|
||||||
subtype="XYZ", default=(0.0, 0.0, 0.0),
|
subtype="XYZ", default=(0.0, 0.0, 0.0),
|
||||||
description="Target of Constraint - Point",
|
description="Target of Constraint - Point",
|
||||||
@ -157,11 +157,6 @@ bpy.types.PoseBone.IKRetarget = bpy.props.BoolProperty(name="IK",
|
|||||||
update=toggleIKBone, default=False)
|
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():
|
def updateIKRetarget():
|
||||||
# ensures that Blender constraints and IK properties are in sync
|
# ensures that Blender constraints and IK properties are in sync
|
||||||
# currently runs when module is loaded, should run when scene is loaded
|
# currently runs when module is loaded, should run when scene is loaded
|
||||||
@ -230,8 +225,6 @@ class MocapPanel(bpy.types.Panel):
|
|||||||
else:
|
else:
|
||||||
row.label(" ")
|
row.label(" ")
|
||||||
row.label(" ")
|
row.label(" ")
|
||||||
|
|
||||||
|
|
||||||
self.layout.operator("mocap.retarget", text='RETARGET!')
|
self.layout.operator("mocap.retarget", text='RETARGET!')
|
||||||
|
|
||||||
|
|
||||||
@ -328,6 +321,7 @@ class OBJECT_OT_LimitDOFButton(bpy.types.Operator):
|
|||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
return {"FINISHED"}
|
return {"FINISHED"}
|
||||||
|
|
||||||
|
|
||||||
class OBJECT_OT_RotateFixArmature(bpy.types.Operator):
|
class OBJECT_OT_RotateFixArmature(bpy.types.Operator):
|
||||||
bl_idname = "mocap.rotate_fix"
|
bl_idname = "mocap.rotate_fix"
|
||||||
bl_label = "Rotates selected armature 90 degrees (fix for bvh import)"
|
bl_label = "Rotates selected armature 90 degrees (fix for bvh import)"
|
||||||
|
Loading…
Reference in New Issue
Block a user