forked from bartvdbraak/blender
Some optimizations and coding style improvements across the retargeting and constraint scripts
This commit is contained in:
parent
daddbc62df
commit
c749a42a8e
@ -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__)
|
||||||
|
Loading…
Reference in New Issue
Block a user