Early commit of mocap constraint work. Still very much a WIP, but Point constraints...
authorBenjy Cook <benjycook@hotmail.com>
Sat, 2 Jul 2011 18:24:05 +0000 (18:24 +0000)
committerBenjy Cook <benjycook@hotmail.com>
Sat, 2 Jul 2011 18:24:05 +0000 (18:24 +0000)
release/scripts/modules/mocap_constraints.py [new file with mode: 0644]
release/scripts/modules/retarget.py
release/scripts/startup/ui_mocap.py

diff --git a/release/scripts/modules/mocap_constraints.py b/release/scripts/modules/mocap_constraints.py
new file mode 100644 (file)
index 0000000..1251786
--- /dev/null
@@ -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
index 7688f9657a236f4e55cc9a36723ea8b88dce5194..3ce90f64075d3719ba9a26027db2dae58f2cfaf4 100644 (file)
@@ -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()
index 4abc777f59e15725d97e1e2a9c88ee27a87a5844..4273eb74984080d669f040de6cd8d4e364ce0c1d 100644 (file)
 # <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()