diff options
author | Benjy Cook <benjycook@hotmail.com> | 2011-07-02 22:24:05 +0400 |
---|---|---|
committer | Benjy Cook <benjycook@hotmail.com> | 2011-07-02 22:24:05 +0400 |
commit | 8c3f2923fdc0626d4c2e7f6307c1ec4fd0e8c7f3 (patch) | |
tree | f4cd3bf908ce9c1138aeb9b4b8a35a6e9425372d /release/scripts | |
parent | 87030e6a320b6bc770ab7055e73c9d3a7dacc4f3 (diff) |
Early commit of mocap constraint work. Still very much a WIP, but Point constraints should work - but buggy.
Diffstat (limited to 'release/scripts')
-rw-r--r-- | release/scripts/modules/mocap_constraints.py | 141 | ||||
-rw-r--r-- | release/scripts/modules/retarget.py | 37 | ||||
-rw-r--r-- | release/scripts/startup/ui_mocap.py | 117 |
3 files changed, 227 insertions, 68 deletions
diff --git a/release/scripts/modules/mocap_constraints.py b/release/scripts/modules/mocap_constraints.py new file mode 100644 index 00000000000..1251786d882 --- /dev/null +++ b/release/scripts/modules/mocap_constraints.py @@ -0,0 +1,141 @@ +# ##### BEGIN GPL LICENSE BLOCK ##### +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# ##### END GPL LICENSE BLOCK ##### + +# <pep8 compliant> + +import bpy +from mathutils import * + +### 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): + #utility function - returns related IK target if bone has IK + ik = [constraint for constraint in bone.constraints if constraint.type == "IK"] + if ik: + ik = ik[0] + cons_obj = ik.target + if ik.subtarget: + cons_obj = ik.target.pose.bones[ik.subtarget] + else: + cons_obj = bone + return cons_obj + +### And and Remove Constraints (called from operators) + + +def addNewConstraint(m_constraint, cons_obj): + if m_constraint.type == "point" or m_constraint.type == "freeze": + c_type = "LIMIT_LOCATION" + if m_constraint.type == "distance": + c_type = "LIMIT_DISTANCE" + if m_constraint.type == "floor": + c_type = "FLOOR" + real_constraint = cons_obj.constraints.new(c_type) + real_constraint.name = "Mocap constraint " + str(len(cons_obj.constraints)) + m_constraint.real_constraint_bone = cons_obj.name + m_constraint.real_constraint = real_constraint.name + setConstraint(m_constraint) + + +def removeConstraint(m_constraint, cons_obj): + oldConstraint = cons_obj.constraints[m_constraint.real_constraint] + cons_obj.constraints.remove(oldConstraint) + +### Update functions. There are 3: UpdateType, UpdateBone +### and update for the others. + + +def updateConstraint(self, context): + setConstraint(self) + + +def updateConstraintType(m_constraint, context): + pass + #If the constraint exists, we need to remove it + #Then create a new one. + + +def updateConstraintTargetBone(m_constraint, context): + #If the constraint exists, we need to remove it + #from the old bone + obj = context.active_object + bones = obj.pose.bones + if m_constraint.real_constraint: + bone = bones[m_constraint.real_constraint_bone] + cons_obj = getConsObj(bone) + removeConstraint(m_constraint, cons_obj) + #Regardless, after that we create a new constraint + bone = bones[m_constraint.constrained_bone] + cons_obj = getConsObj(bone) + addNewConstraint(m_constraint, cons_obj) + + +# 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): + if not m_constraint.constrained_bone: + return + obj = bpy.context.active_object + bones = obj.pose.bones + bone = bones[m_constraint.constrained_bone] + cons_obj = getConsObj(bone) + real_constraint = cons_obj.constraints[m_constraint.real_constraint] + + #frame changing section + fcurves = 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 = bpy.context.scene.frame_start, bpy.context.scene.frame_end + real_constraint.influence = 0 + real_constraint.keyframe_insert(data_path="influence", frame=s) + real_constraint.keyframe_insert(data_path="influence", frame=e) + s, e = m_constraint.s_frame, m_constraint.e_frame + real_constraint.influence = 1 + real_constraint.keyframe_insert(data_path="influence", frame=s) + real_constraint.keyframe_insert(data_path="influence", frame=e) + real_constraint.influence = 0 + real_constraint.keyframe_insert(data_path="influence", frame=s - 10) + real_constraint.keyframe_insert(data_path="influence", frame=e + 10) + + #Set the blender constraint parameters + if m_constraint.type == "point": + real_constraint.target_space = "WORLD" # temporary for now, just World is supported + x, y, z = m_constraint.targetPoint + real_constraint.max_x = x + real_constraint.max_y = y + real_constraint.max_z = z + real_constraint.min_x = x + real_constraint.min_y = y + real_constraint.min_z = z + real_constraint.use_max_x = True + real_constraint.use_max_y = True + real_constraint.use_max_z = True + real_constraint.use_min_x = True + real_constraint.use_min_y = True + real_constraint.use_min_z = True diff --git a/release/scripts/modules/retarget.py b/release/scripts/modules/retarget.py index 7688f9657a2..3ce90f64075 100644 --- a/release/scripts/modules/retarget.py +++ b/release/scripts/modules/retarget.py @@ -71,13 +71,13 @@ def createIntermediate(performer_obj, enduser_obj, bonemap, bonemapr, root, s_fr #useful for storing the important data in the original motion #i.e. using this empty to IK the chain to that pos / DEBUG def locOfOriginal(inter_bone, perf_bone): - if not perf_bone.name + "Org" in bpy.data.objects: + if not inter_bone.name + "Org" in bpy.data.objects: bpy.ops.object.add() empty = bpy.context.active_object - empty.name = perf_bone.name + "Org" + empty.name = inter_bone.name + "Org" empty.empty_draw_size = 0.1 #empty.parent = enduser_obj - empty = bpy.data.objects[perf_bone.name + "Org"] + empty = bpy.data.objects[inter_bone.name + "Org"] offset = perf_bone.vector if inter_bone.length == 0 or perf_bone.length == 0: scaling = 1 @@ -263,17 +263,17 @@ def copyTranslation(performer_obj, enduser_obj, perfFeet, bonemap, bonemapr, roo #end bone's delta if endV.length != 0: linearAvg.append(hipV.length / endV.length) - + bpy.ops.object.add() stride_bone = bpy.context.active_object stride_bone.name = "stride_bone" - + if linearAvg: avg = sum(linearAvg) / len(linearAvg) for t in range(s_frame, e_frame): scene.frame_set(t) newTranslation = (tailLoc(perf_bones[perfRoot]) / avg) - stride_bone.location = newTranslation + stride_bone.location = newTranslation * enduser_obj.matrix_world stride_bone.keyframe_insert("location") return stride_bone @@ -287,7 +287,7 @@ def IKRetarget(bonemap, bonemapr, performer_obj, enduser_obj, s_frame, e_frame, perf_bone = bonemapr[pose_bone.name] if isinstance(perf_bone, list): perf_bone = bonemapr[pose_bone.name][-1] - end_empty = bpy.data.objects[perf_bone + "Org"] + end_empty = bpy.data.objects[pose_bone.name + "Org"] ik_constraint = [constraint for constraint in pose_bone.constraints if constraint.type == "IK"][0] if not ik_constraint.target: ik_constraint.target = end_empty @@ -326,23 +326,26 @@ def turnOffIK(enduser_obj): ik_constraint = [constraint for constraint in pose_bone.constraints if constraint.type == "IK"][0] ik_constraint.mute = True -def cleanAndStoreObjMat(performer_obj,enduser_obj): + +def cleanAndStoreObjMat(performer_obj, enduser_obj): perf_obj_mat = performer_obj.matrix_world.copy() enduser_obj_mat = enduser_obj.matrix_world.copy() - zero_mat = Matrix()#Matrix(((0,0,0,0),(0,0,0,0),(0,0,0,0),(0,0,0,0))) + zero_mat = Matrix() # Matrix(((0,0,0,0),(0,0,0,0),(0,0,0,0),(0,0,0,0))) performer_obj.matrix_world = zero_mat enduser_obj.matrix_world = zero_mat return perf_obj_mat, enduser_obj_mat -def restoreObjMat(performer_obj,enduser_obj,perf_obj_mat,enduser_obj_mat): - perf_bones = performer_obj.pose.bones - for perf_bone in perf_bones: - if perf_bone.name + "Org" in bpy.data.objects: - empty = bpy.data.objects[perf_bone.name + "Org"] + +def restoreObjMat(performer_obj, enduser_obj, perf_obj_mat, enduser_obj_mat, stride_bone): + pose_bones = enduser_obj.pose.bones + for pose_bone in pose_bones: + if pose_bone.name + "Org" in bpy.data.objects: + empty = bpy.data.objects[pose_bone.name + "Org"] empty.parent = enduser_obj performer_obj.matrix_world = perf_obj_mat enduser_obj.matrix_world = enduser_obj_mat + def totalRetarget(): print("retargeting...") enduser_obj = bpy.context.active_object @@ -357,16 +360,16 @@ def totalRetarget(): s_frame = scene.frame_start e_frame = scene.frame_end bonemap, bonemapr, root = createDictionary(perf_arm) - perf_obj_mat, enduser_obj_mat = cleanAndStoreObjMat(performer_obj,enduser_obj) + 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, ["RightFoot", "LeftFoot"], bonemap, bonemapr, root, s_frame, e_frame, scene) IKRetarget(bonemap, bonemapr, performer_obj, enduser_obj, s_frame, e_frame, scene) - restoreObjMat(performer_obj,enduser_obj,perf_obj_mat,enduser_obj_mat) + 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() if __name__ == "__main__": - totalRetarget()
\ No newline at end of file + totalRetarget() diff --git a/release/scripts/startup/ui_mocap.py b/release/scripts/startup/ui_mocap.py index 4abc777f59e..4273eb74984 100644 --- a/release/scripts/startup/ui_mocap.py +++ b/release/scripts/startup/ui_mocap.py @@ -19,12 +19,10 @@ # <pep8 compliant> import bpy -import time from bpy.props import * from bpy import * -from mathutils import Vector -from math import isfinite +from mocap_constraints import * # MocapConstraint class # Defines MocapConstraint datatype, used to add and configute mocap constraints @@ -32,49 +30,62 @@ from math import isfinite class MocapConstraint(bpy.types.PropertyGroup): - name = bpy.props.StringProperty(name = "Name", - default = "Mocap Constraint", - description = "Name of Mocap Constraint") - boneA = bpy.props.StringProperty(name = "Bone", - default = "", - description = "Constrained Bone") - boneB = bpy.props.StringProperty(name = "Bone (2)", - default = "", - description = "Other Constrained Bone (optional, depends on type)") - s_frame = bpy.props.IntProperty(name = "S", - default = 1, - description = "Start frame of constraint") - e_frame = bpy.props.IntProperty(name = "E", - default = 500, - description = "End frame of constrain") - targetMesh = bpy.props.StringProperty(name = "Mesh", - default = "", - description = "Target of Constraint - Mesh (optional, depends on type)") - active = bpy.props.BoolProperty(name = "Active", - default = True, - description = "Constraint is active") - baked = bpy.props.BoolProperty(name = "Baked / Applied", - default = False, - description = "Constraint has been baked to NLA layer") - targetFrame = bpy.props.IntProperty(name = "Frame", - default = 1, - description = "Target of Constraint - Frame (optional, depends on type)") - targetPoint = bpy.props.FloatVectorProperty(name = "Point", size = 3, - subtype = "XYZ", default = (0.0, 0.0, 0.0), - description = "Target of Constraint - Point") + name = bpy.props.StringProperty(name="Name", + default="Mocap Constraint", + description="Name of Mocap Constraint", + update=updateConstraint) + constrained_bone = bpy.props.StringProperty(name="Bone", + default="", + description="Constrained Bone", + update=updateConstraintTargetBone) + constrained_boneB = bpy.props.StringProperty(name="Bone (2)", + default="", + description="Other Constrained Bone (optional, depends on type)", + update=updateConstraint) + s_frame = bpy.props.IntProperty(name="S", + default=1, + description="Start frame of constraint", + update=updateConstraint) + e_frame = bpy.props.IntProperty(name="E", + default=500, + description="End frame of constrain", + update=updateConstraint) + targetMesh = bpy.props.StringProperty(name="Mesh", + default="", + description="Target of Constraint - Mesh (optional, depends on type)", + update=updateConstraint) + active = bpy.props.BoolProperty(name="Active", + default=True, + description="Constraint is active", + update=updateConstraint) + baked = bpy.props.BoolProperty(name="Baked / Applied", + default=False, + description="Constraint has been baked to NLA layer", + update=updateConstraint) + targetFrame = bpy.props.IntProperty(name="Frame", + default=1, + description="Target of Constraint - Frame (optional, depends on type)", + update=updateConstraint) + targetPoint = bpy.props.FloatVectorProperty(name="Point", size=3, + subtype="XYZ", default=(0.0, 0.0, 0.0), + description="Target of Constraint - Point", + update=updateConstraint) targetSpace = bpy.props.EnumProperty( - items = [("world", "World Space", "Evaluate target in global space"), + items=[("world", "World Space", "Evaluate target in global space"), ("object", "Object space", "Evaluate target in object space"), - ("boneb", "Other Bone Space", "Evaluate target in specified other bone space")], - name = "Space", - description = "In which space should Point type target be evaluated") + ("constrained_boneB", "Other Bone Space", "Evaluate target in specified other bone space")], + name="Space", + description="In which space should Point type target be evaluated", + update=updateConstraint) 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"), ("floor", "Stay above", "Bone does not cross specified mesh object eg floor"), ("distance", "Maintain distance", "Target bones maintained specified distance")], - description = "Type of constraint") - realConstraint = bpy.props.StringProperty() + description="Type of constraint", + update=updateConstraint) + real_constraint = bpy.props.StringProperty() + real_constraint_bone = bpy.props.StringProperty() bpy.utils.register_class(MocapConstraint) @@ -112,9 +123,9 @@ def toggleIKBone(self, context): bone.IKRetarget = False bpy.types.Bone.map = bpy.props.StringProperty() -bpy.types.PoseBone.IKRetarget = bpy.props.BoolProperty(name = "IK", - description = "Toggles IK Retargeting method for given bone", - update = toggleIKBone, default = False) +bpy.types.PoseBone.IKRetarget = bpy.props.BoolProperty(name="IK", + description="Toggles IK Retargeting method for given bone", + update=toggleIKBone, default=False) def hasIKConstraint(pose_bone): @@ -207,15 +218,15 @@ class MocapConstraintsPanel(bpy.types.Panel): if context.active_object.data.name in bpy.data.armatures: enduser_obj = context.active_object enduser_arm = enduser_obj.data - layout.operator("mocap.addconstraint", text = 'Add constraint') + layout.operator("mocap.addconstraint") layout.separator() for i, m_constraint in enumerate(enduser_arm.mocap_constraints): box = layout.box() box.prop(m_constraint, 'name') box.prop(m_constraint, 'type') - box.prop_search(m_constraint, 'boneA', enduser_obj.pose, "bones") + box.prop_search(m_constraint, 'constrained_bone', enduser_obj.pose, "bones") if m_constraint.type == "distance" or m_constraint.type == "point": - box.prop_search(m_constraint, 'boneB', enduser_obj.pose, "bones") + box.prop_search(m_constraint, 'constrained_boneB', enduser_obj.pose, "bones") frameRow = box.row() frameRow.label("Frame Range:") frameRow.prop(m_constraint, 's_frame') @@ -234,7 +245,7 @@ class MocapConstraintsPanel(bpy.types.Panel): checkRow = box.row() checkRow.prop(m_constraint, 'active') checkRow.prop(m_constraint, 'baked') - layout.operator("mocap.removeconstraint", text = "Remove constraint").constraint = i + layout.operator("mocap.removeconstraint", text="Remove constraint").constraint = i layout.separator() @@ -288,7 +299,7 @@ class OBJECT_OT_AddMocapConstraint(bpy.types.Operator): def execute(self, context): enduser_obj = bpy.context.active_object enduser_arm = enduser_obj.data - newCon = enduser_arm.mocap_constraints.add() + new_mcon = enduser_arm.mocap_constraints.add() return {"FINISHED"} @@ -300,8 +311,13 @@ class OBJECT_OT_RemoveMocapConstraint(bpy.types.Operator): def execute(self, context): enduser_obj = bpy.context.active_object enduser_arm = enduser_obj.data - constraints = enduser_arm.mocap_constraints - constraints.remove(self.constraint) + m_constraints = enduser_arm.mocap_constraints + m_constraint = m_constraints[self.constraint] + if m_constraint.real_constraint: + bone = enduser_obj.pose.bones[m_constraint.real_constraint_bone] + cons_obj = getConsObj(bone) + removeConstraint(m_constraint, cons_obj) + m_constraints.remove(self.constraint) return {"FINISHED"} @@ -312,6 +328,5 @@ def register(): def unregister(): bpy.utils.unregister_module(__name__) - if __name__ == "__main__": register() |