Some optimizations and coding style improvements across the retargeting and constraint scripts

This commit is contained in:
Benjy Cook 2011-07-09 21:52:25 +00:00
parent daddbc62df
commit c749a42a8e
3 changed files with 212 additions and 153 deletions

@ -21,15 +21,11 @@
import bpy import bpy
from mathutils import * from mathutils import *
from bl_operators import nla from bl_operators import nla
from retarget import hasIKConstraint
### Utility Functions ### Utility Functions
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 getConsObj(bone): def getConsObj(bone):
#utility function - returns related IK target if bone has IK #utility function - returns related IK target if bone has IK
ik = [constraint for constraint in bone.constraints if constraint.type == "IK"] ik = [constraint for constraint in bone.constraints if constraint.type == "IK"]
@ -63,21 +59,18 @@ def addNewConstraint(m_constraint, cons_obj):
real_constraint.name = "Mocap constraint " + str(len(cons_obj.constraints)) real_constraint.name = "Mocap constraint " + 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
setConstraint(m_constraint) setConstraint(m_constraint, bpy.context)
def removeConstraint(m_constraint, cons_obj): def removeConstraint(m_constraint, cons_obj):
oldConstraint = cons_obj.constraints[m_constraint.real_constraint] oldConstraint = cons_obj.constraints[m_constraint.real_constraint]
removeInfluenceFcurve(cons_obj, bpy.context.active_object, oldConstraint)
cons_obj.constraints.remove(oldConstraint) cons_obj.constraints.remove(oldConstraint)
### Update functions. There are 2: UpdateType/UpdateBone ### Update functions. There are 2: UpdateType/UpdateBone
### and update for the others. ### and update for the others.
def updateConstraint(self, context):
setConstraint(self)
def updateConstraintBoneType(m_constraint, context): def updateConstraintBoneType(m_constraint, context):
#If the constraint exists, we need to remove it #If the constraint exists, we need to remove it
#from the old bone #from the old bone
@ -94,22 +87,13 @@ def updateConstraintBoneType(m_constraint, context):
addNewConstraint(m_constraint, cons_obj) addNewConstraint(m_constraint, cons_obj)
# Function that copies all settings from m_constraint to the real Blender constraints def setConstraintFraming(m_constraint, context):
# Is only called when blender constraint already exists obj = context.active_object
bones = obj.pose.bones
def setConstraintFraming(m_constraint, cons_obj, obj, real_constraint): bone = bones[m_constraint.constrained_bone]
if isinstance(cons_obj, bpy.types.PoseBone): cons_obj = getConsObj(bone)
fcurves = obj.animation_data.action.fcurves real_constraint = cons_obj.constraints[m_constraint.real_constraint]
else: removeInfluenceFcurve(cons_obj, obj, real_constraint)
fcurves = cons_obj.animation_data.action.fcurves
influence_RNA = real_constraint.path_from_id("influence")
fcurve = [fcurve for fcurve in fcurves if fcurve.data_path == influence_RNA]
#clear the fcurve and set the frames.
if fcurve:
fcurve = fcurve[0]
for i in range(len(fcurve.keyframe_points) - 1, 0, -1):
fcurve.keyframe_points.remove(fcurve.keyframe_points[i])
s, e = m_constraint.s_frame, m_constraint.e_frame s, e = m_constraint.s_frame, m_constraint.e_frame
s_in, s_out = m_constraint.smooth_in, m_constraint.smooth_out s_in, s_out = m_constraint.smooth_in, m_constraint.smooth_out
real_constraint.influence = 1 real_constraint.influence = 1
@ -120,17 +104,34 @@ def setConstraintFraming(m_constraint, cons_obj, obj, real_constraint):
real_constraint.keyframe_insert(data_path="influence", frame=e + s_out) real_constraint.keyframe_insert(data_path="influence", frame=e + s_out)
def setConstraint(m_constraint): def removeInfluenceFcurve(cons_obj, obj, real_constraint):
if isinstance(cons_obj, bpy.types.PoseBone):
fcurves = obj.animation_data.action.fcurves
else:
fcurves = cons_obj.animation_data.action.fcurves
influence_RNA = real_constraint.path_from_id("influence")
fcurve = [fcurve for fcurve in fcurves if fcurve.data_path == influence_RNA]
#clear the fcurve and set the frames.
if fcurve:
fcurves.remove(fcurve[0])
# 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: if not m_constraint.constrained_bone:
return return
obj = bpy.context.active_object obj = context.active_object
bones = obj.pose.bones bones = obj.pose.bones
bone = bones[m_constraint.constrained_bone] bone = bones[m_constraint.constrained_bone]
cons_obj = getConsObj(bone) cons_obj = getConsObj(bone)
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, 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,17 +177,17 @@ def setConstraint(m_constraint):
real_constraint.limit_mode = "LIMITDIST_ONSURFACE" real_constraint.limit_mode = "LIMITDIST_ONSURFACE"
real_constraint.distance = m_constraint.targetDist real_constraint.distance = m_constraint.targetDist
# active check # active/baked check
real_constraint.mute = (not m_constraint.active) and (m_constraint.baked) real_constraint.mute = (not m_constraint.active) and (m_constraint.baked)
def updateBake(self, context): def updateBake(self, context):
if self.baked: if self.baked:
print("baking...") print("baking...")
bakeConstraint(self) bakeConstraint(self, context)
else: else:
print("unbaking...") print("unbaking...")
unbakeConstraint(self) unbakeConstraint(self, context)
def bakeTransformFK(anim_data, s_frame, e_frame, end_bone, bones, cons_obj): def bakeTransformFK(anim_data, s_frame, e_frame, end_bone, bones, cons_obj):
@ -210,14 +211,15 @@ def bakeTransformFK(anim_data, s_frame, e_frame, end_bone, bones, cons_obj):
return mute_ik return mute_ik
def bakeConstraint(m_constraint): def bakeConstraint(m_constraint, context):
obj = bpy.context.active_object obj = context.active_object
bones = obj.pose.bones bones = obj.pose.bones
end_bone = bones[m_constraint.constrained_bone] end_bone = bones[m_constraint.constrained_bone]
cons_obj = getConsObj(end_bone) cons_obj = getConsObj(end_bone)
scene = bpy.context.scene s, e = m_constraint.s_frame, m_constraint.e_frame
s_frame = scene.frame_start s_in, s_out = m_constraint.smooth_in, m_constraint.smooth_out
e_frame = scene.frame_end s_frame = s - s_in
e_frame = e + s_out
mute_ik = bakeTransformFK(obj.animation_data, s_frame, e_frame, end_bone, bones, cons_obj) mute_ik = bakeTransformFK(obj.animation_data, s_frame, e_frame, end_bone, bones, cons_obj)
if mute_ik: if mute_ik:
ik_con = hasIKConstraint(end_bone) ik_con = hasIKConstraint(end_bone)
@ -232,16 +234,14 @@ def bakeConstraint(m_constraint):
constraintStrip.frame_end = e_frame constraintStrip.frame_end = e_frame
def unbakeConstraint(m_constraint): def unbakeConstraint(m_constraint, context):
# to unbake a constraint we need to delete the whole strip # to unbake a constraint we need to delete the whole strip
# and rebake all the other constraints # and rebake all the other constraints
obj = bpy.context.active_object obj = context.active_object
bones = obj.pose.bones bones = obj.pose.bones
end_bone = bones[m_constraint.constrained_bone] end_bone = bones[m_constraint.constrained_bone]
cons_obj = getConsObj(end_bone) cons_obj = getConsObj(end_bone)
scene = bpy.context.scene scene = bpy.context.scene
s_frame = scene.frame_start
e_frame = scene.frame_end
constraintTrack = obj.animation_data.nla_tracks["Mocap constraints"] constraintTrack = obj.animation_data.nla_tracks["Mocap constraints"]
constraintStrip = constraintTrack.strips[0] constraintStrip = constraintTrack.strips[0]
action = constraintStrip.action action = constraintStrip.action
@ -257,12 +257,3 @@ def unbakeConstraint(m_constraint):
ik_con.mute = False ik_con.mute = False
real_constraint = cons_obj.constraints[m_constraint.real_constraint] real_constraint = cons_obj.constraints[m_constraint.real_constraint]
real_constraint.mute = False 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

@ -22,40 +22,33 @@ import bpy
from mathutils import * from mathutils import *
from math import radians, acos from math import radians, acos
#TODO: Only selected bones get retargeted.
# Selected Bones/chains get original pos empties,
# if ppl want IK instead of FK
# Some "magic" numbers - frame start and end,
# eulers of all orders instead of just quats keyframed
# dictionary of mapping def hasIKConstraint(pose_bone):
# this is currently manuall input'ed, but willW #utility function / predicate, returns True if given bone has IK constraint
# be created from a more comfortable UI in the future ik = [constraint for constraint in pose_bone.constraints if constraint.type == "IK"]
if ik:
return ik[0]
else:
return False
def createDictionary(perf_arm, end_arm): def createDictionary(perf_arm, end_arm):
bonemap = {} # clear any old data
#Bonemap: performer to enduser for end_bone in end_arm.bones:
for bone in perf_arm.bones: for mapping in end_bone.reverseMap:
bonemap[bone.name] = bone.map end_bone.reverseMap.remove(0)
for perf_bone in perf_arm.bones:
#find its match and add perf_bone to the match's mapping
if perf_bone.map:
end_bone = end_arm.bones[perf_bone.map]
newMap = end_bone.reverseMap.add()
newMap.name = perf_bone.name
# creation of a reverse map
# multiple keys get mapped to list values
#Bonemapr: enduser to performer
bonemapr = {}
for key, value in bonemap.items():
if not value in bonemapr:
if isinstance(bonemap[key], tuple):
for key_x in bonemap[key]:
bonemapr[key_x] = [key]
else:
bonemapr[bonemap[key]] = [key]
else:
bonemapr[bonemap[key]].append(key)
#root is the root of the enduser #root is the root of the enduser
root = end_arm.bones[0].name root = end_arm.bones[0].name
feetBones = [bone.name for bone in perf_arm.bones if bone.foot] feetBones = [bone.name for bone in perf_arm.bones if bone.foot]
return bonemap, bonemapr, feetBones, root return feetBones, root
# list of empties created to keep track of "original" # list of empties created to keep track of "original"
# position data # position data
# in final product, these locations can be stored as custom props # in final product, these locations can be stored as custom props
@ -69,7 +62,7 @@ def createDictionary(perf_arm, end_arm):
# easily while concentrating on the hierarchy changes # easily while concentrating on the hierarchy changes
def createIntermediate(performer_obj, enduser_obj, bonemap, bonemapr, root, s_frame, e_frame, scene): def createIntermediate(performer_obj, enduser_obj, root, s_frame, e_frame, scene):
#creates and keyframes an empty with its location #creates and keyframes an empty with its location
#the original position of the tail bone #the original position of the tail bone
#useful for storing the important data in the original motion #useful for storing the important data in the original motion
@ -96,21 +89,16 @@ def createIntermediate(performer_obj, enduser_obj, bonemap, bonemapr, root, s_fr
#determines the type of hierachy change needed and calls the #determines the type of hierachy change needed and calls the
#right function #right function
def retargetPerfToInter(inter_bone): def retargetPerfToInter(inter_bone):
if inter_bone.name in bonemapr: if inter_bone.bone.reverseMap:
perf_bone_name = bonemapr[inter_bone.name] perf_bone_name = inter_bone.bone.reverseMap
#is it a 1 to many?
if isinstance(bonemap[perf_bone_name[0]], tuple):
pass
# 1 to many not supported yet # 1 to many not supported yet
else:
# then its either a many to 1 or 1 to 1 # then its either a many to 1 or 1 to 1
if len(perf_bone_name) > 1: if len(perf_bone_name) > 1:
performer_bones_s = [performer_bones[name] for name in perf_bone_name] performer_bones_s = [performer_bones[map.name] for map in perf_bone_name]
#we need to map several performance bone to a single #we need to map several performance bone to a single
inter_bone.matrix_basis = manyPerfToSingleInterRetarget(inter_bone, performer_bones_s) inter_bone.matrix_basis = manyPerfToSingleInterRetarget(inter_bone, performer_bones_s)
else: else:
perf_bone = performer_bones[perf_bone_name[0]] perf_bone = performer_bones[perf_bone_name[0].name]
inter_bone.matrix_basis = singleBoneRetarget(inter_bone, perf_bone) inter_bone.matrix_basis = singleBoneRetarget(inter_bone, perf_bone)
inter_bone.keyframe_insert("rotation_quaternion") inter_bone.keyframe_insert("rotation_quaternion")
@ -140,7 +128,7 @@ def createIntermediate(performer_obj, enduser_obj, bonemap, bonemapr, root, s_fr
inter_bone = inter_bones[root] inter_bone = inter_bones[root]
retargetPerfToInter(inter_bone) retargetPerfToInter(inter_bone)
return inter_obj, inter_arm return inter_obj
# this procedure copies the rotations over from the intermediate # this procedure copies the rotations over from the intermediate
# armature to the end user one. # armature to the end user one.
@ -176,7 +164,13 @@ def retargetEnduser(inter_obj, enduser_obj, root, s_frame, e_frame, scene):
rest_matrix_inv.invert() rest_matrix_inv.invert()
bake_matrix = rest_matrix_inv * bake_matrix bake_matrix = rest_matrix_inv * bake_matrix
trg_bone.matrix_basis = bake_matrix trg_bone.matrix_basis = bake_matrix
rot_mode = end_bone.rotation_mode
if rot_mode == "QUATERNION":
end_bone.keyframe_insert("rotation_quaternion") end_bone.keyframe_insert("rotation_quaternion")
elif rot_mode == "AXIS_ANGLE":
end_bone.keyframe_insert("rotation_axis_angle")
else:
end_bone.keyframe_insert("rotation_euler")
for bone in end_bone.children: for bone in end_bone.children:
bakeTransform(bone) bakeTransform(bone)
@ -193,13 +187,13 @@ def retargetEnduser(inter_obj, enduser_obj, root, s_frame, e_frame, scene):
# (they don't move, despite root moving) somewhere in the animation. # (they don't move, despite root moving) somewhere in the animation.
def copyTranslation(performer_obj, enduser_obj, perfFeet, bonemap, bonemapr, root, s_frame, e_frame, scene, enduser_obj_mat): def copyTranslation(performer_obj, enduser_obj, perfFeet, root, s_frame, e_frame, scene, enduser_obj_mat):
perf_bones = performer_obj.pose.bones perf_bones = performer_obj.pose.bones
end_bones = enduser_obj.pose.bones end_bones = enduser_obj.pose.bones
perfRoot = bonemapr[root][0] perfRoot = end_bones[root].bone.reverseMap[0].name
endFeet = [bonemap[perfBone] for perfBone in perfFeet] endFeet = [perf_bones[perfBone].bone.map for perfBone in perfFeet]
locDictKeys = perfFeet + endFeet + [perfRoot] locDictKeys = perfFeet + endFeet + [perfRoot]
def tailLoc(bone): def tailLoc(bone):
@ -208,7 +202,7 @@ def copyTranslation(performer_obj, enduser_obj, perfFeet, bonemap, bonemapr, roo
#Step 1 - we create a dict that contains these keys: #Step 1 - we create a dict that contains these keys:
#(Performer) Hips, Feet #(Performer) Hips, Feet
#(End user) Feet #(End user) Feet
# where the values are their world position on each (1,120) frame # where the values are their world position on each frame in range (s,e)
locDict = {} locDict = {}
for key in locDictKeys: for key in locDictKeys:
@ -231,10 +225,7 @@ def copyTranslation(performer_obj, enduser_obj, perfFeet, bonemap, bonemapr, roo
for key in locDict.keys(): for key in locDict.keys():
graph = locDict[key] graph = locDict[key]
for t in range(len(graph) - 1): locDeriv[key] = [graph[t + 1] - graph[t] for t in range(len(graph) - 1)]
x = graph[t]
xh = graph[t + 1]
locDeriv[key].append(xh - x)
# now find the plant frames, where perfFeet don't move much # now find the plant frames, where perfFeet don't move much
@ -244,7 +235,7 @@ def copyTranslation(performer_obj, enduser_obj, perfFeet, bonemap, bonemapr, roo
for i in range(len(locDeriv[key]) - 1): for i in range(len(locDeriv[key]) - 1):
v = locDeriv[key][i] v = locDeriv[key][i]
hipV = locDeriv[perfRoot][i] hipV = locDeriv[perfRoot][i]
endV = locDeriv[bonemap[key]][i] endV = locDeriv[perf_bones[key].bone.map][i]
if (v.length < 0.1): if (v.length < 0.1):
#this is a plant frame. #this is a plant frame.
#lets see what the original hip delta is, and the corresponding #lets see what the original hip delta is, and the corresponding
@ -268,18 +259,16 @@ def copyTranslation(performer_obj, enduser_obj, perfFeet, bonemap, bonemapr, roo
return stride_bone return stride_bone
def IKRetarget(bonemap, bonemapr, performer_obj, enduser_obj, s_frame, e_frame, scene): def IKRetarget(performer_obj, enduser_obj, s_frame, e_frame, scene):
end_bones = enduser_obj.pose.bones end_bones = enduser_obj.pose.bones
for pose_bone in end_bones: for pose_bone in end_bones:
if "IK" in [constraint.type for constraint in pose_bone.constraints]: ik_constraint = hasIKConstraint(pose_bone)
if ik_constraint:
target_is_bone = False target_is_bone = False
# 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 = bonemapr[pose_bone.name] perf_bone = pose_bone.bone.reverseMap[-1].name
if isinstance(perf_bone, list):
perf_bone = bonemapr[pose_bone.name][-1]
orgLocTrg = originalLocationTarget(pose_bone) orgLocTrg = originalLocationTarget(pose_bone)
ik_constraint = [constraint for constraint in pose_bone.constraints if constraint.type == "IK"][0]
if not ik_constraint.target: if not ik_constraint.target:
ik_constraint.target = orgLocTrg ik_constraint.target = orgLocTrg
target = orgLocTrg target = orgLocTrg
@ -314,8 +303,8 @@ def turnOffIK(enduser_obj):
#pose_bone.ik_stiffness_x = 0.5 #pose_bone.ik_stiffness_x = 0.5
#pose_bone.ik_stiffness_y = 0.5 #pose_bone.ik_stiffness_y = 0.5
#pose_bone.ik_stiffness_z = 0.5 #pose_bone.ik_stiffness_z = 0.5
if "IK" in [constraint.type for constraint in pose_bone.constraints]: ik_constraint = hasIKConstraint(pose_bone)
ik_constraint = [constraint for constraint in pose_bone.constraints if constraint.type == "IK"][0] if ik_constraint:
ik_constraint.mute = True ik_constraint.mute = True
@ -350,45 +339,38 @@ def originalLocationTarget(end_bone):
return empty return empty
def totalRetarget(): def NLASystemInitialize(enduser_obj, s_frame):
print("retargeting...")
enduser_obj = bpy.context.active_object
performer_obj = [obj for obj in bpy.context.selected_objects if obj != enduser_obj]
if enduser_obj is None or len(performer_obj) != 1:
print("Need active and selected armatures")
else:
performer_obj = performer_obj[0]
perf_arm = performer_obj.data
end_arm = enduser_obj.data
scene = bpy.context.scene
s_frame = scene.frame_start
e_frame = scene.frame_end
bonemap, bonemapr, feetBones, root = createDictionary(perf_arm, end_arm)
perf_obj_mat, enduser_obj_mat = cleanAndStoreObjMat(performer_obj, enduser_obj)
turnOffIK(enduser_obj)
inter_obj, inter_arm = createIntermediate(performer_obj, enduser_obj, bonemap, bonemapr, root, s_frame, e_frame, scene)
retargetEnduser(inter_obj, enduser_obj, root, s_frame, e_frame, scene)
stride_bone = copyTranslation(performer_obj, enduser_obj, feetBones, bonemap, bonemapr, root, s_frame, e_frame, scene, enduser_obj_mat)
IKRetarget(bonemap, bonemapr, performer_obj, enduser_obj, s_frame, e_frame, scene)
restoreObjMat(performer_obj, enduser_obj, perf_obj_mat, enduser_obj_mat, stride_bone)
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 anim_data = enduser_obj.animation_data
mocapAction = anim_data.action mocapAction = anim_data.action
mocapAction.name = "Base Mocap Action" mocapAction.name = "Base Mocap"
anim_data.use_nla = True anim_data.use_nla = True
mocapTrack = anim_data.nla_tracks.new() mocapTrack = anim_data.nla_tracks.new()
mocapTrack.name = "Base Mocap Track" mocapTrack.name = "Base Mocap Track"
mocapStrip = mocapTrack.strips.new("Base Mocap Action", s_frame, mocapAction) mocapStrip = mocapTrack.strips.new("Base Mocap", s_frame, mocapAction)
constraintTrack = anim_data.nla_tracks.new() constraintTrack = anim_data.nla_tracks.new()
constraintTrack.name = "Mocap constraints" constraintTrack.name = "Mocap constraints"
constraintAction = bpy.data.actions.new("Mocap constraints Action") constraintAction = bpy.data.actions.new("Mocap constraints")
constraintStrip = constraintTrack.strips.new("Mocap constraints Action", s_frame, constraintAction) constraintStrip = constraintTrack.strips.new("Mocap constraints", s_frame, constraintAction)
#constraintStrip.frame_end = e_frame
anim_data.nla_tracks.active = constraintTrack anim_data.nla_tracks.active = constraintTrack
anim_data.action = constraintAction anim_data.action = constraintAction
def totalRetarget(performer_obj, enduser_obj, scene, s_frame, e_frame):
perf_arm = performer_obj.data
end_arm = enduser_obj.data
feetBones, root = createDictionary(perf_arm, end_arm)
perf_obj_mat, enduser_obj_mat = cleanAndStoreObjMat(performer_obj, enduser_obj)
turnOffIK(enduser_obj)
inter_obj = createIntermediate(performer_obj, enduser_obj, root, s_frame, e_frame, scene)
retargetEnduser(inter_obj, enduser_obj, root, s_frame, e_frame, scene)
stride_bone = copyTranslation(performer_obj, enduser_obj, feetBones, root, s_frame, e_frame, scene, enduser_obj_mat)
IKRetarget(performer_obj, enduser_obj, s_frame, e_frame, scene)
restoreObjMat(performer_obj, enduser_obj, perf_obj_mat, enduser_obj_mat, stride_bone)
bpy.ops.object.mode_set(mode='OBJECT')
bpy.ops.object.select_name(name=inter_obj.name, extend=False)
bpy.ops.object.delete()
NLASystemInitialize(enduser_obj, s_frame)
if __name__ == "__main__": if __name__ == "__main__":
totalRetarget() totalRetarget()

@ -42,7 +42,7 @@ class MocapConstraint(bpy.types.PropertyGroup):
name = bpy.props.StringProperty(name="Name", name = bpy.props.StringProperty(name="Name",
default="Mocap Constraint", default="Mocap Constraint",
description="Name of Mocap Constraint", description="Name of Mocap Constraint",
update=updateConstraint) update=setConstraint)
constrained_bone = bpy.props.StringProperty(name="Bone", constrained_bone = bpy.props.StringProperty(name="Bone",
default="", default="",
description="Constrained Bone", description="Constrained Bone",
@ -50,33 +50,33 @@ class MocapConstraint(bpy.types.PropertyGroup):
constrained_boneB = bpy.props.StringProperty(name="Bone (2)", constrained_boneB = bpy.props.StringProperty(name="Bone (2)",
default="", default="",
description="Other Constrained Bone (optional, depends on type)", description="Other Constrained Bone (optional, depends on type)",
update=updateConstraint) update=setConstraint)
s_frame = bpy.props.IntProperty(name="S", s_frame = bpy.props.IntProperty(name="S",
default=1, default=1,
description="Start frame of constraint", description="Start frame of constraint",
update=updateConstraint) update=setConstraintFraming)
e_frame = bpy.props.IntProperty(name="E", e_frame = bpy.props.IntProperty(name="E",
default=500, default=500,
description="End frame of constrain", description="End frame of constrain",
update=updateConstraint) update=setConstraintFraming)
smooth_in = bpy.props.IntProperty(name="In", smooth_in = bpy.props.IntProperty(name="In",
default=10, default=10,
description="Amount of frames to smooth in", description="Amount of frames to smooth in",
update=updateConstraint, update=setConstraintFraming,
min=0) min=0)
smooth_out = bpy.props.IntProperty(name="Out", smooth_out = bpy.props.IntProperty(name="Out",
default=10, default=10,
description="Amount of frames to smooth out", description="Amount of frames to smooth out",
update=updateConstraint, update=setConstraintFraming,
min=0) min=0)
targetMesh = bpy.props.StringProperty(name="Mesh", targetMesh = bpy.props.StringProperty(name="Mesh",
default="", default="",
description="Target of Constraint - Mesh (optional, depends on type)", description="Target of Constraint - Mesh (optional, depends on type)",
update=updateConstraint) update=setConstraint)
active = bpy.props.BoolProperty(name="Active", active = bpy.props.BoolProperty(name="Active",
default=True, default=True,
description="Constraint is active", description="Constraint is active",
update=updateConstraint) update=setConstraint)
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",
@ -84,18 +84,18 @@ class MocapConstraint(bpy.types.PropertyGroup):
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",
update=updateConstraint) update=setConstraint)
targetDist = bpy.props.FloatProperty(name="Dist", targetDist = bpy.props.FloatProperty(name="Dist",
default=1, default=1,
description="Distance Constraint - Desired distance", description="Distance Constraint - Desired distance",
update=updateConstraint) update=setConstraint)
targetSpace = bpy.props.EnumProperty( targetSpace = bpy.props.EnumProperty(
items=[("WORLD", "World Space", "Evaluate target in global space"), items=[("WORLD", "World Space", "Evaluate target in global space"),
("LOCAL", "Object space", "Evaluate target in object space"), ("LOCAL", "Object space", "Evaluate target in object space"),
("constrained_boneB", "Other Bone Space", "Evaluate target in specified other bone space")], ("constrained_boneB", "Other Bone Space", "Evaluate target in specified other bone space")],
name="Space", name="Space",
description="In which space should Point type target be evaluated", description="In which space should Point type target be evaluated",
update=updateConstraint) update=setConstraint)
type = bpy.props.EnumProperty(name="Type of constraint", type = bpy.props.EnumProperty(name="Type of constraint",
items=[("point", "Maintain Position", "Bone is at a specific point"), items=[("point", "Maintain Position", "Bone is at a specific point"),
("freeze", "Maintain Position at frame", "Bone does not move from location specified in target frame"), ("freeze", "Maintain Position at frame", "Bone does not move from location specified in target frame"),
@ -148,7 +148,14 @@ def toggleIKBone(self, context):
if not bone.is_in_ik_chain: if not bone.is_in_ik_chain:
bone.IKRetarget = False bone.IKRetarget = False
class MocapMapping(bpy.types.PropertyGroup):
name = bpy.props.StringProperty()
bpy.utils.register_class(MocapMapping)
bpy.types.Bone.map = bpy.props.StringProperty() bpy.types.Bone.map = bpy.props.StringProperty()
bpy.types.Bone.reverseMap = bpy.props.CollectionProperty(type=MocapMapping)
bpy.types.Bone.foot = bpy.props.BoolProperty(name="Foot", bpy.types.Bone.foot = bpy.props.BoolProperty(name="Foot",
description="Marks this bone as a 'foot', which determines retargeted animation's translation", description="Marks this bone as a 'foot', which determines retargeted animation's translation",
default=False) default=False)
@ -225,6 +232,7 @@ class MocapPanel(bpy.types.Panel):
else: else:
row.label(" ") row.label(" ")
row.label(" ") row.label(" ")
self.layout.operator("mocap.savemapping", text='Save mapping')
self.layout.operator("mocap.retarget", text='RETARGET!') self.layout.operator("mocap.retarget", text='RETARGET!')
@ -283,9 +291,49 @@ 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):
retarget.totalRetarget() enduser_obj = context.active_object
performer_obj = [obj for obj in context.selected_objects if obj != enduser_obj]
if enduser_obj is None or len(performer_obj) != 1:
print("Need active and selected armatures")
else:
performer_obj = performer_obj[0]
scene = context.scene
s_frame = scene.frame_start
e_frame = scene.frame_end
retarget.totalRetarget(performer_obj, enduser_obj, scene, s_frame, e_frame)
return {"FINISHED"} return {"FINISHED"}
@classmethod
def poll(cls, context):
if context.active_object:
activeIsArmature = isinstance(context.active_object.data, bpy.types.Armature)
performer_obj = [obj for obj in context.selected_objects if obj != context.active_object]
if performer_obj:
return activeIsArmature and isinstance(performer_obj[0].data, bpy.types.Armature)
else:
return False
class OBJECT_OT_SaveMappingButton(bpy.types.Operator):
bl_idname = "mocap.savemapping"
bl_label = "Saves user generated mapping from Performer to Enduser"
def execute(self, context):
enduser_obj = bpy.context.active_object
performer_obj = [obj for obj in bpy.context.selected_objects if obj != enduser_obj][0]
retarget.createDictionary(performer_obj.data, enduser_obj.data)
return {"FINISHED"}
@classmethod
def poll(cls, context):
if context.active_object:
activeIsArmature = isinstance(context.active_object.data, bpy.types.Armature)
performer_obj = [obj for obj in context.selected_objects if obj != context.active_object]
if performer_obj:
return activeIsArmature and isinstance(performer_obj[0].data, bpy.types.Armature)
else:
return False
class OBJECT_OT_ConvertSamplesButton(bpy.types.Operator): class OBJECT_OT_ConvertSamplesButton(bpy.types.Operator):
bl_idname = "mocap.samples" bl_idname = "mocap.samples"
@ -295,6 +343,10 @@ class OBJECT_OT_ConvertSamplesButton(bpy.types.Operator):
mocap_tools.fcurves_simplify() mocap_tools.fcurves_simplify()
return {"FINISHED"} return {"FINISHED"}
@classmethod
def poll(cls, context):
return context.active_object.animation_data
class OBJECT_OT_LooperButton(bpy.types.Operator): class OBJECT_OT_LooperButton(bpy.types.Operator):
bl_idname = "mocap.looper" bl_idname = "mocap.looper"
@ -304,6 +356,10 @@ class OBJECT_OT_LooperButton(bpy.types.Operator):
mocap_tools.autoloop_anim() mocap_tools.autoloop_anim()
return {"FINISHED"} return {"FINISHED"}
@classmethod
def poll(cls, context):
return context.active_object.animation_data
class OBJECT_OT_DenoiseButton(bpy.types.Operator): class OBJECT_OT_DenoiseButton(bpy.types.Operator):
bl_idname = "mocap.denoise" bl_idname = "mocap.denoise"
@ -313,6 +369,14 @@ class OBJECT_OT_DenoiseButton(bpy.types.Operator):
mocap_tools.denoise_median() mocap_tools.denoise_median()
return {"FINISHED"} return {"FINISHED"}
@classmethod
def poll(cls, context):
return context.active_object
@classmethod
def poll(cls, context):
return context.active_object.animation_data
class OBJECT_OT_LimitDOFButton(bpy.types.Operator): class OBJECT_OT_LimitDOFButton(bpy.types.Operator):
bl_idname = "mocap.limitdof" bl_idname = "mocap.limitdof"
@ -321,6 +385,16 @@ class OBJECT_OT_LimitDOFButton(bpy.types.Operator):
def execute(self, context): def execute(self, context):
return {"FINISHED"} return {"FINISHED"}
@classmethod
def poll(cls, context):
if context.active_object:
activeIsArmature = isinstance(context.active_object.data, bpy.types.Armature)
performer_obj = [obj for obj in context.selected_objects if obj != context.active_object]
if performer_obj:
return activeIsArmature and isinstance(performer_obj[0].data, bpy.types.Armature)
else:
return False
class OBJECT_OT_RotateFixArmature(bpy.types.Operator): class OBJECT_OT_RotateFixArmature(bpy.types.Operator):
bl_idname = "mocap.rotate_fix" bl_idname = "mocap.rotate_fix"
@ -330,8 +404,10 @@ class OBJECT_OT_RotateFixArmature(bpy.types.Operator):
mocap_tools.rotate_fix_armature(context.active_object.data) mocap_tools.rotate_fix_armature(context.active_object.data)
return {"FINISHED"} return {"FINISHED"}
#def poll(self, context): @classmethod
# return context.active_object.data in bpy.data.armatures def poll(cls, context):
if context.active_object:
return isinstance(context.active_object.data, bpy.types.Armature)
class OBJECT_OT_AddMocapConstraint(bpy.types.Operator): class OBJECT_OT_AddMocapConstraint(bpy.types.Operator):
@ -344,6 +420,11 @@ class OBJECT_OT_AddMocapConstraint(bpy.types.Operator):
new_mcon = enduser_arm.mocap_constraints.add() new_mcon = enduser_arm.mocap_constraints.add()
return {"FINISHED"} return {"FINISHED"}
@classmethod
def poll(cls, context):
if context.active_object:
return isinstance(context.active_object.data, bpy.types.Armature)
class OBJECT_OT_RemoveMocapConstraint(bpy.types.Operator): class OBJECT_OT_RemoveMocapConstraint(bpy.types.Operator):
bl_idname = "mocap.removeconstraint" bl_idname = "mocap.removeconstraint"
@ -362,6 +443,11 @@ class OBJECT_OT_RemoveMocapConstraint(bpy.types.Operator):
m_constraints.remove(self.constraint) m_constraints.remove(self.constraint)
return {"FINISHED"} return {"FINISHED"}
@classmethod
def poll(cls, context):
if context.active_object:
return isinstance(context.active_object.data, bpy.types.Armature)
def register(): def register():
bpy.utils.register_module(__name__) bpy.utils.register_module(__name__)