Created property systems for multiple retargets on a single armature, for this type of use and animation stitching. Also contains some placeholder UI and code for animation stitching.

This commit is contained in:
2011-08-08 11:09:56 +00:00
parent 8883702f8a
commit 60eec89cda
4 changed files with 126 additions and 27 deletions

View File

@@ -59,7 +59,7 @@ def addNewConstraint(m_constraint, cons_obj):
c_type = "LIMIT_LOCATION" c_type = "LIMIT_LOCATION"
#create and store the new constraint within m_constraint #create and store the new constraint within m_constraint
real_constraint = cons_obj.constraints.new(c_type) real_constraint = cons_obj.constraints.new(c_type)
real_constraint.name = "Mocap fix " + str(len(cons_obj.constraints)) real_constraint.name = "Auto fixes " + str(len(cons_obj.constraints))
m_constraint.real_constraint_bone = consObjToBone(cons_obj) m_constraint.real_constraint_bone = consObjToBone(cons_obj)
m_constraint.real_constraint = real_constraint.name m_constraint.real_constraint = real_constraint.name
#set the rest of the constraint properties #set the rest of the constraint properties
@@ -364,7 +364,8 @@ def bakeAllConstraints(obj, s_frame, e_frame, bones):
simpleBake += [end_bone] simpleBake += [end_bone]
for bone in selectedBones: for bone in selectedBones:
bone.bone.select = True bone.bone.select = True
constraintTrack = obj.animation_data.nla_tracks["Mocap fixes"] tracks = [track for track in obj.data.mocapNLATracks if track.active][0]
constraintTrack = obj.animation_data.nla_tracks[tracks.auto_fix_track]
constraintStrip = constraintTrack.strips[0] constraintStrip = constraintTrack.strips[0]
constraintStrip.action_frame_start = s_frame constraintStrip.action_frame_start = s_frame
constraintStrip.action_frame_end = e_frame constraintStrip.action_frame_end = e_frame
@@ -403,7 +404,8 @@ def unbakeConstraints(context):
obj = context.active_object obj = context.active_object
bones = obj.pose.bones bones = obj.pose.bones
scene = bpy.context.scene scene = bpy.context.scene
constraintTrack = obj.animation_data.nla_tracks["Mocap fixes"] tracks = obj.data.mocapNLATracks[obj.animation_data.action]
constraintTrack = obj.animation_data.nla_tracks[tracks.auto_fix_track]
constraintStrip = constraintTrack.strips[0] constraintStrip = constraintTrack.strips[0]
action = constraintStrip.action action = constraintStrip.action
# delete the fcurves on the strip # delete the fcurves on the strip

View File

@@ -757,3 +757,11 @@ def path_editing(context, stride_obj, path):
eval_time_fcurve.keyframe_points.insert(frame=t, value=parameterization[t]) eval_time_fcurve.keyframe_points.insert(frame=t, value=parameterization[t])
y_fcurve.mute = True y_fcurve.mute = True
print("finished path editing") print("finished path editing")
def anim_stitch(context, enduser_obj):
stitch_settings = enduser_obj.data.stitch_settings
action_1 = stitch_settings.first_action
action_2 = stitch_settings.second_action
TrackNamesA = enduser_obj.data.mocapNLATracks[action_1]
TrackNamesB = enduser_obj.data.mocapNLATracks[action_2]

View File

@@ -272,21 +272,31 @@ def copyTranslation(performer_obj, enduser_obj, perfFeet, root, s_frame, e_frame
if endV.length != 0: if endV.length != 0:
linearAvg.append(hipV.length / endV.length) linearAvg.append(hipV.length / endV.length)
action_name = performer_obj.animation_data.action.name
#if you have a parent, and that parent is a previously created stride bone
if enduser_obj.parent:
stride_action = bpy.data.actions.new("Stride Bone " + action_name)
stride_bone = enduser_obj.parent
stride_bone.animation_data.action = stride_action
else:
bpy.ops.object.add() bpy.ops.object.add()
stride_bone = bpy.context.active_object stride_bone = bpy.context.active_object
stride_bone.name = "stride_bone" stride_bone.name = "stride_bone"
print(stride_bone)
stride_bone.location = Vector((0, 0, 0))
if linearAvg: if linearAvg:
#determine the average change in scale needed #determine the average change in scale needed
avg = sum(linearAvg) / len(linearAvg) avg = sum(linearAvg) / len(linearAvg)
scene.frame_set(s_frame) scene.frame_set(s_frame)
initialPos = (tailLoc(perf_bones[perfRoot]) / avg) initialPos = (tailLoc(perf_bones[perfRoot]) / avg) + stride_bone.location
for t in range(s_frame, e_frame): for t in range(s_frame, e_frame):
scene.frame_set(t) scene.frame_set(t)
#calculate the new position, by dividing by the found ratio between performer and enduser #calculate the new position, by dividing by the found ratio between performer and enduser
newTranslation = (tailLoc(perf_bones[perfRoot]) / avg) newTranslation = (tailLoc(perf_bones[perfRoot]) / avg)
stride_bone.location = enduser_obj_mat * (newTranslation - initialPos) stride_bone.location = enduser_obj_mat * (newTranslation - initialPos)
stride_bone.keyframe_insert("location") stride_bone.keyframe_insert("location")
stride_bone.animation_data.action.name = ("Stride Bone " + action_name)
return stride_bone return stride_bone
@@ -299,7 +309,7 @@ def IKRetarget(performer_obj, enduser_obj, s_frame, e_frame, scene):
# set constraint target to corresponding empty if targetless, # set constraint target to corresponding empty if targetless,
# if not, keyframe current target to corresponding empty # if not, keyframe current target to corresponding empty
perf_bone = pose_bone.bone.reverseMap[-1].name perf_bone = pose_bone.bone.reverseMap[-1].name
orgLocTrg = originalLocationTarget(pose_bone) orgLocTrg = originalLocationTarget(pose_bone, enduser_obj)
if not ik_constraint.target: if not ik_constraint.target:
ik_constraint.target = orgLocTrg ik_constraint.target = orgLocTrg
target = orgLocTrg target = orgLocTrg
@@ -322,6 +332,7 @@ def IKRetarget(performer_obj, enduser_obj, s_frame, e_frame, scene):
target.location = final_loc target.location = final_loc
target.keyframe_insert("location") target.keyframe_insert("location")
ik_constraint.mute = False ik_constraint.mute = False
scene.frame_set(s_frame)
def turnOffIK(enduser_obj): def turnOffIK(enduser_obj):
@@ -358,44 +369,59 @@ def restoreObjMat(performer_obj, enduser_obj, perf_obj_mat, enduser_obj_mat, str
empty = bpy.data.objects[pose_bone.name + "Org"] empty = bpy.data.objects[pose_bone.name + "Org"]
empty.parent = stride_bone empty.parent = stride_bone
performer_obj.matrix_world = perf_obj_mat performer_obj.matrix_world = perf_obj_mat
enduser_obj.matrix_world = enduser_obj_mat
enduser_obj.parent = stride_bone enduser_obj.parent = stride_bone
enduser_obj.matrix_world = enduser_obj_mat
#create (or return if exists) the related IK empty to the bone #create (or return if exists) the related IK empty to the bone
def originalLocationTarget(end_bone): def originalLocationTarget(end_bone, enduser_obj):
if not end_bone.name + "Org" in bpy.data.objects: if not end_bone.name + "Org" in bpy.data.objects:
bpy.ops.object.add() bpy.ops.object.add()
empty = bpy.context.active_object empty = bpy.context.active_object
empty.name = end_bone.name + "Org" empty.name = end_bone.name + "Org"
empty.empty_draw_size = 0.1 empty.empty_draw_size = 0.1
#empty.parent = enduser_obj empty.parent = enduser_obj
empty = bpy.data.objects[end_bone.name + "Org"] empty = bpy.data.objects[end_bone.name + "Org"]
return empty return empty
#create the specified NLA setup for base animation, constraints and tweak layer. #create the specified NLA setup for base animation, constraints and tweak layer.
def NLASystemInitialize(enduser_obj, s_frame): def NLASystemInitialize(enduser_obj, s_frame, name):
anim_data = enduser_obj.animation_data anim_data = enduser_obj.animation_data
if not name in enduser_obj.data.mocapNLATracks:
NLATracks = enduser_obj.data.mocapNLATracks.add()
NLATracks.name = name
else:
NLATracks = enduser_obj.data.mocapNLATracks[name]
for track in enduser_obj.data.mocapNLATracks:
track.active = False
mocapAction = anim_data.action mocapAction = anim_data.action
mocapAction.name = "Base Mocap" mocapAction.name = "Base " + name
anim_data.use_nla = True anim_data.use_nla = True
for track in anim_data.nla_tracks:
anim_data.nla_tracks.remove(track)
mocapTrack = anim_data.nla_tracks.new() mocapTrack = anim_data.nla_tracks.new()
mocapTrack.name = "Base Mocap Track" mocapTrack.name = "Base " + name
mocapStrip = mocapTrack.strips.new("Base Mocap", s_frame, mocapAction) NLATracks.base_track = mocapTrack.name
mocapStrip = mocapTrack.strips.new("Base " + name, s_frame, mocapAction)
constraintTrack = anim_data.nla_tracks.new() constraintTrack = anim_data.nla_tracks.new()
constraintTrack.name = "Mocap fixes" constraintTrack.name = "Auto fixes " + name
constraintAction = bpy.data.actions.new("Mocap fixes") NLATracks.auto_fix_track = constraintTrack.name
constraintStrip = constraintTrack.strips.new("Mocap fixes", s_frame, constraintAction) constraintAction = bpy.data.actions.new("Auto fixes " + name)
constraintStrip = constraintTrack.strips.new("Auto fixes " + name, s_frame, constraintAction)
constraintStrip.extrapolation = "NOTHING" constraintStrip.extrapolation = "NOTHING"
userTrack = anim_data.nla_tracks.new() userTrack = anim_data.nla_tracks.new()
userTrack.name = "Mocap manual fix" userTrack.name = "Manual fixes " + name
userAction = bpy.data.actions.new("Mocap manual fix") NLATracks.manual_fix_track = userTrack.name
userStrip = userTrack.strips.new("Mocap manual fix", s_frame, userAction) if enduser_obj.parent.animation_data:
NLATracks.stride_action = enduser_obj.parent.animation_data.action.name
userAction = bpy.data.actions.new("Manual fixes " + name)
userStrip = userTrack.strips.new("Manual fixes " + name, s_frame, userAction)
userStrip.extrapolation = "HOLD" userStrip.extrapolation = "HOLD"
#userStrip.blend_type = "MULITPLY" - doesn't work due to work, will be activated soon #userStrip.blend_type = "MULITPLY" - doesn't work due to work, will be activated soon
anim_data.nla_tracks.active = constraintTrack anim_data.nla_tracks.active = constraintTrack
anim_data.action = constraintAction NLATracks.active = True
#anim_data.action = constraintAction
anim_data.action_extrapolation = "NOTHING" anim_data.action_extrapolation = "NOTHING"
@@ -419,7 +445,7 @@ def totalRetarget(performer_obj, enduser_obj, scene, s_frame, e_frame):
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()
NLASystemInitialize(enduser_obj, s_frame) NLASystemInitialize(enduser_obj, s_frame, performer_obj.animation_data.action.name)
print("retargeting done!") print("retargeting done!")
if __name__ == "__main__": if __name__ == "__main__":

View File

@@ -110,6 +110,35 @@ bpy.utils.register_class(MocapConstraint)
bpy.types.Armature.mocap_constraints = bpy.props.CollectionProperty(type=MocapConstraint) bpy.types.Armature.mocap_constraints = bpy.props.CollectionProperty(type=MocapConstraint)
class AnimationStitchSettings(bpy.types.PropertyGroup):
first_action = bpy.props.StringProperty(name="Action 1",
description="First action in stitch")
second_action = bpy.props.StringProperty(name="Action 2",
description="Second action in stitch")
blend_frame = bpy.props.IntProperty(name="Stitch frame",
description="Frame to locate stitch on")
blend_amount = bpy.props.IntProperty(name="Blend amount",
description="Size of blending transitiion, on both sides of the stitch",
default=10)
bpy.utils.register_class(AnimationStitchSettings)
class MocapNLATracks(bpy.types.PropertyGroup):
name = bpy.props.StringProperty()
active = bpy.props.BoolProperty()
base_track = bpy.props.StringProperty()
auto_fix_track = bpy.props.StringProperty()
manual_fix_track = bpy.props.StringProperty()
stride_action = bpy.props.StringProperty()
bpy.utils.register_class(MocapNLATracks)
bpy.types.Armature.stitch_settings = bpy.props.PointerProperty(type=AnimationStitchSettings)
bpy.types.Armature.mocapNLATracks = bpy.props.CollectionProperty(type=MocapNLATracks)
#Update function for IK functionality. Is called when IK prop checkboxes are toggled. #Update function for IK functionality. Is called when IK prop checkboxes are toggled.
@@ -246,6 +275,7 @@ class MocapPanel(bpy.types.Panel):
mapRow = self.layout.row() mapRow = self.layout.row()
mapRow.operator("mocap.savemapping", text='Save mapping') mapRow.operator("mocap.savemapping", text='Save mapping')
mapRow.operator("mocap.loadmapping", text='Load mapping') mapRow.operator("mocap.loadmapping", text='Load mapping')
self.layout.prop(data=performer_obj.animation_data.action, property='name', text='Action Name')
self.layout.operator("mocap.retarget", text='RETARGET!') self.layout.operator("mocap.retarget", text='RETARGET!')
@@ -315,6 +345,16 @@ class ExtraToolsPanel(bpy.types.Panel):
def draw(self, context): def draw(self, context):
layout = self.layout layout = self.layout
layout.operator('mocap.pathediting', text="Follow Path") layout.operator('mocap.pathediting', text="Follow Path")
layout.label("Animation Stitching")
activeIsArmature = isinstance(context.active_object.data, bpy.types.Armature)
if activeIsArmature:
enduser_arm = context.active_object.data
settings = enduser_arm.stitch_settings
layout.prop_search(settings, "first_action", enduser_arm, "mocapNLATracks")
layout.prop_search(settings, "second_action", enduser_arm, "mocapNLATracks")
layout.prop(settings, "blend_frame")
layout.prop(settings, "blend_amount")
layout.operator('mocap.animstitch', text="Stitch Animations")
class OBJECT_OT_RetargetButton(bpy.types.Operator): class OBJECT_OT_RetargetButton(bpy.types.Operator):
@@ -323,15 +363,18 @@ class OBJECT_OT_RetargetButton(bpy.types.Operator):
bl_label = "Retargets active action from Performer to Enduser" bl_label = "Retargets active action from Performer to Enduser"
def execute(self, context): def execute(self, context):
scene = context.scene
s_frame = scene.frame_start
e_frame = scene.frame_end
enduser_obj = context.active_object enduser_obj = context.active_object
performer_obj = [obj for obj in context.selected_objects if obj != enduser_obj] performer_obj = [obj for obj in context.selected_objects if obj != enduser_obj]
if enduser_obj is None or len(performer_obj) != 1: if enduser_obj is None or len(performer_obj) != 1:
print("Need active and selected armatures") print("Need active and selected armatures")
else: else:
performer_obj = performer_obj[0] performer_obj = performer_obj[0]
scene = context.scene s_frame, e_frame = performer_obj.animation_data.action.frame_range
s_frame = scene.frame_start s_frame = int(s_frame)
e_frame = scene.frame_end e_frame = int(e_frame)
retarget.totalRetarget(performer_obj, enduser_obj, scene, s_frame, e_frame) retarget.totalRetarget(performer_obj, enduser_obj, scene, s_frame, e_frame)
return {"FINISHED"} return {"FINISHED"}
@@ -645,6 +688,26 @@ class OBJECT_OT_PathEditing(bpy.types.Operator):
return False return False
class OBJECT_OT_AnimationStitchingButton(bpy.types.Operator):
'''Stitches two defined animations into a single one via alignment of NLA Tracks'''
bl_idname = "mocap.animstitch"
bl_label = "Stitches two defined animations into a single one via alignment of NLA Tracks"
def execute(self, context):
mocap_tools.anim_stitch(context, context.active_object)
return {"FINISHED"}
@classmethod
def poll(cls, context):
activeIsArmature = False
if context.active_object:
activeIsArmature = isinstance(context.active_object.data, bpy.types.Armature)
if activeIsArmature:
stitch_settings = context.active_object.data.stitch_settings
return (stitch_settings.first_action and stitch_settings.second_action)
return False
def register(): def register():
bpy.utils.register_module(__name__) bpy.utils.register_module(__name__)