From b9039168fee70db8e9809fad36be0f6209b081e4 Mon Sep 17 00:00:00 2001 From: Benjy Cook Date: Wed, 3 Aug 2011 18:13:44 +0000 Subject: Fixed coding style to conform to pep8 --- release/scripts/modules/mocap_constraints.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'release/scripts') diff --git a/release/scripts/modules/mocap_constraints.py b/release/scripts/modules/mocap_constraints.py index 7ac2387d787..8fd42d17392 100644 --- a/release/scripts/modules/mocap_constraints.py +++ b/release/scripts/modules/mocap_constraints.py @@ -195,7 +195,7 @@ def setConstraint(m_constraint, context): e += s_out #Set the blender constraint parameters if m_constraint.type == "point": - constraint_settings = False # are fix settings keyframed? + constraint_settings = False # are fix settings keyframed? if not m_constraint.targetSpace == "constrained_boneB": real_constraint.owner_space = m_constraint.targetSpace else: @@ -223,7 +223,7 @@ def setConstraint(m_constraint, context): for t in range(s, e): context.scene.frame_set(t) src_bone_pos = src_bone.matrix.to_translation() - bakedPos[t] = src_bone_pos + m_constraint.targetPoint # final position for constrained bone in object space + bakedPos[t] = src_bone_pos + m_constraint.targetPoint # final position for constrained bone in object space context.scene.frame_set(c_frame) for frame in bakedPos.keys(): pos = bakedPos[frame] @@ -233,7 +233,7 @@ def setConstraint(m_constraint, context): yCurve.keyframe_points.insert(frame=frame, value=pos.y) for zCurve in zCurves: zCurve.keyframe_points.insert(frame=frame, value=pos.z) - + if not constraint_settings: x, y, z = m_constraint.targetPoint real_constraint.max_x = x -- cgit v1.2.3 From ab3fc2fa5c98fbf98d94d9e6e464eff8ac8370fb Mon Sep 17 00:00:00 2001 From: Benjy Cook Date: Wed, 3 Aug 2011 18:16:32 +0000 Subject: Added functions for toggling DOF Constraints on user rig based on range of motion in motion capture clip. Limit Rotation constraints are added based on the min and max of each DOF of each bone in its local space --- release/scripts/modules/mocap_tools.py | 71 +++++++++++++++++++++++++++++++++- 1 file changed, 70 insertions(+), 1 deletion(-) (limited to 'release/scripts') diff --git a/release/scripts/modules/mocap_tools.py b/release/scripts/modules/mocap_tools.py index ed945d45251..703db4477ba 100644 --- a/release/scripts/modules/mocap_tools.py +++ b/release/scripts/modules/mocap_tools.py @@ -18,7 +18,7 @@ # -from math import hypot, sqrt, isfinite, radians +from math import hypot, sqrt, isfinite, radians, pi import bpy import time from mathutils import Vector, Matrix @@ -637,3 +637,72 @@ def guessMapping(performer_obj, enduser_obj): guessSingleMapping(child) guessSingleMapping(root) + + +def limit_dof(context, performer_obj, enduser_obj): + limitDict = {} + perf_bones = [bone for bone in performer_obj.pose.bones if bone.bone.map] + c_frame = context.scene.frame_current + for bone in perf_bones: + limitDict[bone.bone.map] = [1000, 1000, 1000, -1000, -1000, -1000] + for t in range(context.scene.frame_start, context.scene.frame_end): + context.scene.frame_set(t) + for bone in perf_bones: + end_bone = enduser_obj.pose.bones[bone.bone.map] + bake_matrix = bone.matrix + rest_matrix = end_bone.bone.matrix_local + + if end_bone.parent and end_bone.bone.use_inherit_rotation: + srcParent = bone.parent + parent_mat = srcParent.matrix + parent_rest = end_bone.parent.bone.matrix_local + parent_rest_inv = parent_rest.copy() + parent_rest_inv.invert() + parent_mat_inv = parent_mat.copy() + parent_mat_inv.invert() + bake_matrix = parent_mat_inv * bake_matrix + rest_matrix = parent_rest_inv * rest_matrix + + rest_matrix_inv = rest_matrix.copy() + rest_matrix_inv.invert() + bake_matrix = rest_matrix_inv * bake_matrix + + mat = bake_matrix + euler = mat.to_euler() + limitDict[bone.bone.map][0] = min(limitDict[bone.bone.map][0], euler.x) + limitDict[bone.bone.map][1] = min(limitDict[bone.bone.map][1], euler.y) + limitDict[bone.bone.map][2] = min(limitDict[bone.bone.map][2], euler.z) + limitDict[bone.bone.map][3] = max(limitDict[bone.bone.map][3], euler.x) + limitDict[bone.bone.map][4] = max(limitDict[bone.bone.map][4], euler.y) + limitDict[bone.bone.map][5] = max(limitDict[bone.bone.map][5], euler.z) + for bone in enduser_obj.pose.bones: + existingConstraint = [constraint for constraint in bone.constraints if constraint.name == "DOF Limitation"] + if existingConstraint: + bone.constraints.remove(existingConstraint[0]) + end_bones = [bone for bone in enduser_obj.pose.bones if bone.name in limitDict.keys()] + for bone in end_bones: + #~ if not bone.is_in_ik_chain: + newCons = bone.constraints.new("LIMIT_ROTATION") + newCons.name = "DOF Limitation" + newCons.owner_space = "LOCAL" + newCons.min_x, newCons.min_y, newCons.min_z, newCons.max_x, newCons.max_y, newCons.max_z = limitDict[bone.name] + newCons.use_limit_x = True + newCons.use_limit_y = True + newCons.use_limit_z = True + #~ else: + #~ bone.ik_min_x, bone.ik_min_y, bone.ik_min_z, bone.ik_max_x, bone.ik_max_y, bone.ik_max_z = limitDict[bone.name] + #~ bone.use_ik_limit_x = True + #~ bone.use_ik_limit_y = True + #~ bone.use_ik_limit_z= True + #~ bone.ik_stiffness_x = 1/((limitDict[bone.name][3] - limitDict[bone.name][0])/(2*pi))) + #~ bone.ik_stiffness_y = 1/((limitDict[bone.name][4] - limitDict[bone.name][1])/(2*pi))) + #~ bone.ik_stiffness_z = 1/((limitDict[bone.name][5] - limitDict[bone.name][2])/(2*pi))) + + context.scene.frame_set(c_frame) + + +def limit_dof_toggle_off(context, enduser_obj): + for bone in enduser_obj.pose.bones: + existingConstraint = [constraint for constraint in bone.constraints if constraint.name == "DOF Limitation"] + if existingConstraint: + bone.constraints.remove(existingConstraint[0]) -- cgit v1.2.3 From 0031950a746d6a1b632e6c57359432803e8871c9 Mon Sep 17 00:00:00 2001 From: Benjy Cook Date: Wed, 3 Aug 2011 18:17:33 +0000 Subject: Fixed issue with IK toggle buttons and added operators for the new limit rotation functions --- release/scripts/startup/ui_mocap.py | 53 ++++++++++++++++++++++++++++--------- 1 file changed, 41 insertions(+), 12 deletions(-) (limited to 'release/scripts') diff --git a/release/scripts/startup/ui_mocap.py b/release/scripts/startup/ui_mocap.py index f31b580411a..ac063a3a0cc 100644 --- a/release/scripts/startup/ui_mocap.py +++ b/release/scripts/startup/ui_mocap.py @@ -52,11 +52,11 @@ class MocapConstraint(bpy.types.PropertyGroup): description="Other Constrained Bone (optional, depends on type)", update=setConstraint) s_frame = bpy.props.IntProperty(name="S", - default=bpy.context.scene.frame_start, + default=0, description="Start frame of Fix", update=setConstraint) e_frame = bpy.props.IntProperty(name="E", - default=bpy.context.scene.frame_end, + default=100, description="End frame of Fix", update=setConstraint) smooth_in = bpy.props.IntProperty(name="In", @@ -125,24 +125,33 @@ def toggleIKBone(self, context): if hasIKConstraint(parent_bone): break deformer_children = [child for child in parent_bone.children if child.bone.use_deform] - if len(deformer_children) > 1: - break + #~ if len(deformer_children) > 1: + #~ break ik.chain_count = chainLen for bone in self.parent_recursive: if bone.is_in_ik_chain: bone.IKRetarget = True else: print(self.name + " IK toggled OFF!") - cnstrn_bone = False + cnstrn_bones = [] + newChainLength = [] if hasIKConstraint(self): - cnstrn_bone = self + cnstrn_bones = [self] elif self.is_in_ik_chain: - cnstrn_bone = [child for child in self.children_recursive if hasIKConstraint(child)][0] - if cnstrn_bone: + cnstrn_bones = [child for child in self.children_recursive if hasIKConstraint(child)] + for cnstrn_bone in cnstrn_bones: + newChainLength.append(cnstrn_bone.parent_recursive.index(self) + 1) + if cnstrn_bones: # remove constraint, and update IK retarget for all parents of cnstrn_bone up to chain_len - ik = [constraint for constraint in cnstrn_bone.constraints if constraint.type == "IK"][0] - cnstrn_bone.constraints.remove(ik) - cnstrn_bone.IKRetarget = False + for i, cnstrn_bone in enumerate(cnstrn_bones): + print(cnstrn_bone.name) + if newChainLength: + ik = hasIKConstraint(cnstrn_bone) + ik.chain_count = newChainLength[i] + else: + ik = hasIKConstraint(cnstrn_bone) + cnstrn_bone.constraints.remove(ik) + cnstrn_bone.IKRetarget = False for bone in cnstrn_bone.parent_recursive: if not bone.is_in_ik_chain: bone.IKRetarget = False @@ -198,6 +207,7 @@ class MocapPanel(bpy.types.Panel): row2 = self.layout.row(align=True) row2.operator("mocap.looper", text='Loop animation') row2.operator("mocap.limitdof", text='Constrain Rig') + row2.operator("mocap.removelimitdof", text='Unconstrain Rig') self.layout.label("Retargeting") enduser_obj = bpy.context.active_object performer_obj = [obj for obj in bpy.context.selected_objects if obj != enduser_obj] @@ -414,11 +424,13 @@ class OBJECT_OT_DenoiseButton(bpy.types.Operator): class OBJECT_OT_LimitDOFButton(bpy.types.Operator): - '''UNIMPLEMENTED: Create limit constraints on the active armature from the selected armature's animation's range of motion''' + '''Create limit constraints on the active armature from the selected armature's animation's range of motion''' bl_idname = "mocap.limitdof" bl_label = "Analyzes animations Max/Min DOF and adds hard/soft constraints" def execute(self, context): + performer_obj = [obj for obj in context.selected_objects if obj != context.active_object][0] + mocap_tools.limit_dof(context, performer_obj, context.active_object) return {"FINISHED"} @classmethod @@ -432,6 +444,23 @@ class OBJECT_OT_LimitDOFButton(bpy.types.Operator): return False +class OBJECT_OT_RemoveLimitDOFButton(bpy.types.Operator): + '''Removes previously created limit constraints on the active armature''' + bl_idname = "mocap.removelimitdof" + bl_label = "Removes previously created limit constraints on the active armature" + + def execute(self, context): + mocap_tools.limit_dof_toggle_off(context, context.active_object) + return {"FINISHED"} + + @classmethod + def poll(cls, context): + activeIsArmature = False + if context.active_object: + activeIsArmature = isinstance(context.active_object.data, bpy.types.Armature) + return activeIsArmature + + class OBJECT_OT_RotateFixArmature(bpy.types.Operator): '''Realign the active armature's axis system to match Blender (Commonly needed after bvh import)''' bl_idname = "mocap.rotate_fix" -- cgit v1.2.3 From ecd4b869828bc5ed7e3d00ac8dc9e93832f2c156 Mon Sep 17 00:00:00 2001 From: Benjy Cook Date: Wed, 3 Aug 2011 22:26:59 +0000 Subject: Initial coding of path editing operator. Still needs some work, but all the basic functionality is there. Select a path and the stride bone (as active) and it will reparameterize the path to propel the armature forward in the same magnitude of the original --- release/scripts/modules/mocap_tools.py | 61 +++++++++++++++++++++++++++++++--- release/scripts/startup/ui_mocap.py | 33 +++++++++++++++++- 2 files changed, 89 insertions(+), 5 deletions(-) (limited to 'release/scripts') diff --git a/release/scripts/modules/mocap_tools.py b/release/scripts/modules/mocap_tools.py index 703db4477ba..20163cd46b8 100644 --- a/release/scripts/modules/mocap_tools.py +++ b/release/scripts/modules/mocap_tools.py @@ -81,6 +81,9 @@ class NdVector: def y(self): return self.vec[1] + def resize_2d(self): + return Vector((self.x, self.y)) + length = property(vecLength) lengthSq = property(vecLengthSq) x = property(x) @@ -322,7 +325,10 @@ def simplifyCurves(curveGroup, error, reparaError, maxIterations, group_mode): return 0, None for pt in data_pts[s:e + 1]: bezVal = bezierEval(bez, pt.u) - tmpError = (pt.co - bezVal).length / pt.co.length + normalize_error = pt.co.length + if normalize_error == 0: + normalize_error = 1 + tmpError = (pt.co - bezVal).length / normalize_error if tmpError >= maxError: maxError = tmpError maxErrorPt = pt.index @@ -471,10 +477,8 @@ def simplifyCurves(curveGroup, error, reparaError, maxIterations, group_mode): #(e.g. a bone's x,y,z rotation) -def fcurves_simplify(sel_opt="all", error=0.002, group_mode=True): +def fcurves_simplify(context, obj, sel_opt="all", error=0.002, group_mode=True): # main vars - context = bpy.context - obj = context.active_object fcurves = obj.animation_data.action.fcurves if sel_opt == "sel": @@ -706,3 +710,52 @@ def limit_dof_toggle_off(context, enduser_obj): existingConstraint = [constraint for constraint in bone.constraints if constraint.name == "DOF Limitation"] if existingConstraint: bone.constraints.remove(existingConstraint[0]) + + +def path_editing(context, stride_obj, path): + y_fcurve = [fcurve for fcurve in stride_obj.animation_data.action.fcurves if fcurve.data_path == "location"][1] + s, e = context.scene.frame_start, context.scene.frame_end # y_fcurve.range() + s = int(s) + e = int(e) + y_s = y_fcurve.evaluate(s) + y_e = y_fcurve.evaluate(e) + direction = (y_e - y_s) / abs(y_e - y_s) + existing_cons = [constraint for constraint in stride_obj.constraints if constraint.type == "FOLLOW_PATH"] + for cons in existing_cons: + stride_obj.constraints.remove(cons) + path_cons = stride_obj.constraints.new("FOLLOW_PATH") + if direction < 0: + path_cons.forward_axis = "TRACK_NEGATIVE_Y" + else: + path_cons.forward_axis = "FORWARD_Y" + path_cons.target = path + path_cons.use_curve_follow = True + path.data.path_duration = e - s + try: + path.data.animation_data.action.fcurves + except AttributeError: + path.data.keyframe_insert("eval_time", frame=0) + eval_time_fcurve = [fcurve for fcurve in path.data.animation_data.action.fcurves if fcurve.data_path == "eval_time"] + eval_time_fcurve = eval_time_fcurve[0] + totalLength = 0 + parameterization = {} + print("evaluating curve") + for t in range(s, e - 1): + if s == t: + chordLength = 0 + else: + chordLength = (y_fcurve.evaluate(t) - y_fcurve.evaluate(t + 1)) + totalLength += chordLength + parameterization[t] = totalLength + for t in range(s + 1, e - 1): + if totalLength == 0: + print("no forward motion") + parameterization[t] /= totalLength + parameterization[t] *= e - s + parameterization[e] = e - s + for t in parameterization.keys(): + eval_time_fcurve.keyframe_points.insert(frame=t, value=parameterization[t]) + error = 0.01 + reparaError = error * 32 + maxIterations = 16 + print("finished path editing") diff --git a/release/scripts/startup/ui_mocap.py b/release/scripts/startup/ui_mocap.py index ac063a3a0cc..03d173df7a6 100644 --- a/release/scripts/startup/ui_mocap.py +++ b/release/scripts/startup/ui_mocap.py @@ -304,6 +304,18 @@ class MocapConstraintsPanel(bpy.types.Panel): layout.separator() +class ExtraToolsPanel(bpy.types.Panel): + # Motion capture retargeting panel + bl_label = "Extra Mocap Tools" + bl_space_type = "PROPERTIES" + bl_region_type = "WINDOW" + bl_context = "object" + + def draw(self, context): + layout = self.layout + layout.operator('mocap.pathediting', text="Follow Path") + + class OBJECT_OT_RetargetButton(bpy.types.Operator): '''Retarget animation from selected armature to active armature ''' bl_idname = "mocap.retarget" @@ -383,7 +395,7 @@ class OBJECT_OT_ConvertSamplesButton(bpy.types.Operator): bl_label = "Converts samples / simplifies keyframes to beziers" def execute(self, context): - mocap_tools.fcurves_simplify() + mocap_tools.fcurves_simplify(context, context.active_object) return {"FINISHED"} @classmethod @@ -598,6 +610,25 @@ class OBJECT_OT_GuessHierachyMapping(bpy.types.Operator): return False +class OBJECT_OT_PathEditing(bpy.types.Operator): + '''Sets active object (stride object) to follow the selected curve''' + bl_idname = "mocap.pathediting" + bl_label = "Sets active object (stride object) to follow the selected curve" + + def execute(self, context): + path = [obj for obj in context.selected_objects if obj != context.active_object][0] + mocap_tools.path_editing(context, context.active_object, path) + return {"FINISHED"} + + @classmethod + def poll(cls, context): + if context.active_object: + selected_objs = [obj for obj in context.selected_objects if obj != context.active_object] + return selected_objs + else: + return False + + def register(): bpy.utils.register_module(__name__) -- cgit v1.2.3 From b5e55ff44b7b9cb58c8fc92f7dd66a4443ecea5b Mon Sep 17 00:00:00 2001 From: Benjy Cook Date: Fri, 5 Aug 2011 08:40:06 +0000 Subject: Small fix to Path Editing - now mute's original forward motion curve --- release/scripts/modules/mocap_tools.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'release/scripts') diff --git a/release/scripts/modules/mocap_tools.py b/release/scripts/modules/mocap_tools.py index 20163cd46b8..17f9e590d10 100644 --- a/release/scripts/modules/mocap_tools.py +++ b/release/scripts/modules/mocap_tools.py @@ -755,7 +755,5 @@ def path_editing(context, stride_obj, path): parameterization[e] = e - s for t in parameterization.keys(): eval_time_fcurve.keyframe_points.insert(frame=t, value=parameterization[t]) - error = 0.01 - reparaError = error * 32 - maxIterations = 16 + y_fcurve.mute = True print("finished path editing") -- cgit v1.2.3 From 63c7bacc7b56dcca559d88a79190933c98017f36 Mon Sep 17 00:00:00 2001 From: Benjy Cook Date: Fri, 5 Aug 2011 08:41:16 +0000 Subject: Updated Vector/Matrix multiplication to new order as required by mathutils --- release/scripts/modules/retarget.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'release/scripts') diff --git a/release/scripts/modules/retarget.py b/release/scripts/modules/retarget.py index bec7b8aaa3e..629e363b918 100644 --- a/release/scripts/modules/retarget.py +++ b/release/scripts/modules/retarget.py @@ -285,7 +285,7 @@ def copyTranslation(performer_obj, enduser_obj, perfFeet, root, s_frame, e_frame scene.frame_set(t) #calculate the new position, by dividing by the found ratio between performer and enduser newTranslation = (tailLoc(perf_bones[perfRoot]) / avg) - stride_bone.location = (newTranslation - initialPos) * enduser_obj_mat + stride_bone.location = enduser_obj_mat * (newTranslation - initialPos) stride_bone.keyframe_insert("location") return stride_bone -- cgit v1.2.3 From 9a9330d88c83b4c7ea76cd46a1fecd0c8e4e349e Mon Sep 17 00:00:00 2001 From: Benjy Cook Date: Fri, 5 Aug 2011 08:44:16 +0000 Subject: Post Retarget fixes - added an Update Constraints button, that recalculates all fixes. Useful for when the user makes some external change to the animation --- release/scripts/modules/mocap_constraints.py | 7 +++++++ release/scripts/startup/ui_mocap.py | 22 +++++++++++++++++++--- 2 files changed, 26 insertions(+), 3 deletions(-) (limited to 'release/scripts') diff --git a/release/scripts/modules/mocap_constraints.py b/release/scripts/modules/mocap_constraints.py index 8fd42d17392..75afbe62231 100644 --- a/release/scripts/modules/mocap_constraints.py +++ b/release/scripts/modules/mocap_constraints.py @@ -419,3 +419,10 @@ def unbakeConstraints(context): if ik_con: ik_con.mute = False m_constraint.active = True + + +def updateConstraints(obj, context): + fixes = obj.data.mocap_constraints + for fix in fixes: + fix.active = False + fix.active = True diff --git a/release/scripts/startup/ui_mocap.py b/release/scripts/startup/ui_mocap.py index 03d173df7a6..8ef4c1e7591 100644 --- a/release/scripts/startup/ui_mocap.py +++ b/release/scripts/startup/ui_mocap.py @@ -264,9 +264,10 @@ class MocapConstraintsPanel(bpy.types.Panel): enduser_obj = context.active_object enduser_arm = enduser_obj.data layout.operator_menu_enum("mocap.addmocapfix", "type") + layout.operator("mocap.updateconstraints", text='Update Fixes') bakeRow = layout.row() - bakeRow.operator("mocap.bakeconstraints") - bakeRow.operator("mocap.unbakeconstraints") + bakeRow.operator("mocap.bakeconstraints", text='Bake Fixes') + bakeRow.operator("mocap.unbakeconstraints", text='Unbake Fixes') layout.separator() for i, m_constraint in enumerate(enduser_arm.mocap_constraints): box = layout.box() @@ -588,6 +589,21 @@ class OBJECT_OT_UnbakeMocapConstraints(bpy.types.Operator): return isinstance(context.active_object.data, bpy.types.Armature) +class OBJECT_OT_UpdateMocapConstraints(bpy.types.Operator): + '''Updates all post-retarget fixes - needed after changes to armature object or pose''' + bl_idname = "mocap.updateconstraints" + bl_label = "Updates all fixes to target armature - neccesary to take under consideration changes to armature object or pose" + + def execute(self, context): + updateConstraints(context.active_object, context) + return {"FINISHED"} + + @classmethod + def poll(cls, context): + if context.active_object: + return isinstance(context.active_object.data, bpy.types.Armature) + + class OBJECT_OT_GuessHierachyMapping(bpy.types.Operator): '''Attemps to auto figure out hierarchy mapping''' bl_idname = "mocap.guessmapping" @@ -623,7 +639,7 @@ class OBJECT_OT_PathEditing(bpy.types.Operator): @classmethod def poll(cls, context): if context.active_object: - selected_objs = [obj for obj in context.selected_objects if obj != context.active_object] + selected_objs = [obj for obj in context.selected_objects if obj != context.active_object and isinstance(obj.data, bpy.types.Curve)] return selected_objs else: return False -- cgit v1.2.3 From c334bf69a7282254bb80bb2896bd8716930a4adf Mon Sep 17 00:00:00 2001 From: Joerg Mueller Date: Sat, 6 Aug 2011 17:57:20 +0000 Subject: 3D Audio GSoC: Mixdown functionality. * Mixdown possible via libsndfile and ffmpeg! * Fixed some ffmpeg deprecation warnings * Mixdown UI only shows working Container, Codec and Format combinations! * Minor bugs and warnings fixed --- release/scripts/startup/bl_ui/properties_scene.py | 2 ++ 1 file changed, 2 insertions(+) (limited to 'release/scripts') diff --git a/release/scripts/startup/bl_ui/properties_scene.py b/release/scripts/startup/bl_ui/properties_scene.py index a9310fcc532..c3784b9f692 100644 --- a/release/scripts/startup/bl_ui/properties_scene.py +++ b/release/scripts/startup/bl_ui/properties_scene.py @@ -70,6 +70,8 @@ class SCENE_PT_audio(SceneButtonsPanel, bpy.types.Panel): col.prop(rd, "ffmpeg_audio_channels", text="") col.prop(rd, "ffmpeg_audio_mixrate", text="Rate") + layout.operator("sound.mixdown") + class SCENE_PT_unit(SceneButtonsPanel, bpy.types.Panel): bl_label = "Units" -- cgit v1.2.3 From 022e815fbd6d81f9b1baa177cce1abf2f42bcb21 Mon Sep 17 00:00:00 2001 From: Joshua Leung Date: Sun, 7 Aug 2011 12:27:20 +0000 Subject: Sound clip NLA Strips for Nexyon These are basically just for specifying when a speaker should fire off it's soundclip, and as such, many NLA operations are irrelevant for it. They can only be specified on object-level for speaker objects. I've still got some UI tweaks I'll need to work on in order for these to be able to be added even when the speaker doesn't have any NLA tracks yet. (EDIT: while typing this, I had an idea for how to do this, but that'll be for next commit). In the mean time, you'll need to add a single keyframe for the object, snowflake that action and delete the NLA strip before you can start editing. --- release/scripts/startup/bl_ui/space_nla.py | 1 + 1 file changed, 1 insertion(+) (limited to 'release/scripts') diff --git a/release/scripts/startup/bl_ui/space_nla.py b/release/scripts/startup/bl_ui/space_nla.py index 78489db6317..b6c2d078960 100644 --- a/release/scripts/startup/bl_ui/space_nla.py +++ b/release/scripts/startup/bl_ui/space_nla.py @@ -173,6 +173,7 @@ class NLA_MT_add(bpy.types.Menu): layout.column() layout.operator("nla.actionclip_add") layout.operator("nla.transition_add") + layout.operator("nla.soundclip_add") layout.separator() layout.operator("nla.meta_add") -- cgit v1.2.3 From 60eec89cda50360c8fc68f9d3d6dc18e5c6633b1 Mon Sep 17 00:00:00 2001 From: Benjy Cook Date: Mon, 8 Aug 2011 11:09:56 +0000 Subject: Created property systems for multiple retargets on a single armature, for this type of use and animation stitching. Also contains some placeholder UI and code for animation stitching. --- release/scripts/modules/mocap_constraints.py | 8 ++-- release/scripts/modules/mocap_tools.py | 8 ++++ release/scripts/modules/retarget.py | 68 ++++++++++++++++++--------- release/scripts/startup/ui_mocap.py | 69 ++++++++++++++++++++++++++-- 4 files changed, 126 insertions(+), 27 deletions(-) (limited to 'release/scripts') diff --git a/release/scripts/modules/mocap_constraints.py b/release/scripts/modules/mocap_constraints.py index 75afbe62231..63e46f99349 100644 --- a/release/scripts/modules/mocap_constraints.py +++ b/release/scripts/modules/mocap_constraints.py @@ -59,7 +59,7 @@ def addNewConstraint(m_constraint, cons_obj): c_type = "LIMIT_LOCATION" #create and store the new constraint within m_constraint real_constraint = cons_obj.constraints.new(c_type) - real_constraint.name = "Mocap fix " + str(len(cons_obj.constraints)) + real_constraint.name = "Auto fixes " + str(len(cons_obj.constraints)) m_constraint.real_constraint_bone = consObjToBone(cons_obj) m_constraint.real_constraint = real_constraint.name #set the rest of the constraint properties @@ -364,7 +364,8 @@ def bakeAllConstraints(obj, s_frame, e_frame, bones): simpleBake += [end_bone] for bone in selectedBones: bone.bone.select = True - constraintTrack = obj.animation_data.nla_tracks["Mocap fixes"] + tracks = [track for track in obj.data.mocapNLATracks if track.active][0] + constraintTrack = obj.animation_data.nla_tracks[tracks.auto_fix_track] constraintStrip = constraintTrack.strips[0] constraintStrip.action_frame_start = s_frame constraintStrip.action_frame_end = e_frame @@ -403,7 +404,8 @@ def unbakeConstraints(context): obj = context.active_object bones = obj.pose.bones scene = bpy.context.scene - constraintTrack = obj.animation_data.nla_tracks["Mocap fixes"] + tracks = obj.data.mocapNLATracks[obj.animation_data.action] + constraintTrack = obj.animation_data.nla_tracks[tracks.auto_fix_track] constraintStrip = constraintTrack.strips[0] action = constraintStrip.action # delete the fcurves on the strip diff --git a/release/scripts/modules/mocap_tools.py b/release/scripts/modules/mocap_tools.py index 17f9e590d10..fa307a36a57 100644 --- a/release/scripts/modules/mocap_tools.py +++ b/release/scripts/modules/mocap_tools.py @@ -757,3 +757,11 @@ def path_editing(context, stride_obj, path): eval_time_fcurve.keyframe_points.insert(frame=t, value=parameterization[t]) y_fcurve.mute = True print("finished path editing") + + +def anim_stitch(context, enduser_obj): + stitch_settings = enduser_obj.data.stitch_settings + action_1 = stitch_settings.first_action + action_2 = stitch_settings.second_action + TrackNamesA = enduser_obj.data.mocapNLATracks[action_1] + TrackNamesB = enduser_obj.data.mocapNLATracks[action_2] diff --git a/release/scripts/modules/retarget.py b/release/scripts/modules/retarget.py index 629e363b918..b3c386e6c4d 100644 --- a/release/scripts/modules/retarget.py +++ b/release/scripts/modules/retarget.py @@ -272,21 +272,31 @@ def copyTranslation(performer_obj, enduser_obj, perfFeet, root, s_frame, e_frame 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" - + action_name = performer_obj.animation_data.action.name + #if you have a parent, and that parent is a previously created stride bone + if enduser_obj.parent: + stride_action = bpy.data.actions.new("Stride Bone " + action_name) + stride_bone = enduser_obj.parent + stride_bone.animation_data.action = stride_action + else: + bpy.ops.object.add() + stride_bone = bpy.context.active_object + stride_bone.name = "stride_bone" + print(stride_bone) + stride_bone.location = Vector((0, 0, 0)) if linearAvg: #determine the average change in scale needed avg = sum(linearAvg) / len(linearAvg) scene.frame_set(s_frame) - initialPos = (tailLoc(perf_bones[perfRoot]) / avg) + initialPos = (tailLoc(perf_bones[perfRoot]) / avg) + stride_bone.location for t in range(s_frame, e_frame): scene.frame_set(t) #calculate the new position, by dividing by the found ratio between performer and enduser newTranslation = (tailLoc(perf_bones[perfRoot]) / avg) stride_bone.location = enduser_obj_mat * (newTranslation - initialPos) stride_bone.keyframe_insert("location") + stride_bone.animation_data.action.name = ("Stride Bone " + action_name) + return stride_bone @@ -299,7 +309,7 @@ def IKRetarget(performer_obj, enduser_obj, s_frame, e_frame, scene): # set constraint target to corresponding empty if targetless, # if not, keyframe current target to corresponding empty perf_bone = pose_bone.bone.reverseMap[-1].name - orgLocTrg = originalLocationTarget(pose_bone) + orgLocTrg = originalLocationTarget(pose_bone, enduser_obj) if not ik_constraint.target: ik_constraint.target = orgLocTrg target = orgLocTrg @@ -322,6 +332,7 @@ def IKRetarget(performer_obj, enduser_obj, s_frame, e_frame, scene): target.location = final_loc target.keyframe_insert("location") ik_constraint.mute = False + scene.frame_set(s_frame) def turnOffIK(enduser_obj): @@ -358,44 +369,59 @@ def restoreObjMat(performer_obj, enduser_obj, perf_obj_mat, enduser_obj_mat, str empty = bpy.data.objects[pose_bone.name + "Org"] empty.parent = stride_bone performer_obj.matrix_world = perf_obj_mat - enduser_obj.matrix_world = enduser_obj_mat enduser_obj.parent = stride_bone + enduser_obj.matrix_world = enduser_obj_mat #create (or return if exists) the related IK empty to the bone -def originalLocationTarget(end_bone): +def originalLocationTarget(end_bone, enduser_obj): if not end_bone.name + "Org" in bpy.data.objects: bpy.ops.object.add() empty = bpy.context.active_object empty.name = end_bone.name + "Org" empty.empty_draw_size = 0.1 - #empty.parent = enduser_obj + empty.parent = enduser_obj empty = bpy.data.objects[end_bone.name + "Org"] return empty #create the specified NLA setup for base animation, constraints and tweak layer. -def NLASystemInitialize(enduser_obj, s_frame): +def NLASystemInitialize(enduser_obj, s_frame, name): anim_data = enduser_obj.animation_data + if not name in enduser_obj.data.mocapNLATracks: + NLATracks = enduser_obj.data.mocapNLATracks.add() + NLATracks.name = name + else: + NLATracks = enduser_obj.data.mocapNLATracks[name] + for track in enduser_obj.data.mocapNLATracks: + track.active = False mocapAction = anim_data.action - mocapAction.name = "Base Mocap" + mocapAction.name = "Base " + name anim_data.use_nla = True + for track in anim_data.nla_tracks: + anim_data.nla_tracks.remove(track) mocapTrack = anim_data.nla_tracks.new() - mocapTrack.name = "Base Mocap Track" - mocapStrip = mocapTrack.strips.new("Base Mocap", s_frame, mocapAction) + mocapTrack.name = "Base " + name + NLATracks.base_track = mocapTrack.name + mocapStrip = mocapTrack.strips.new("Base " + name, s_frame, mocapAction) constraintTrack = anim_data.nla_tracks.new() - constraintTrack.name = "Mocap fixes" - constraintAction = bpy.data.actions.new("Mocap fixes") - constraintStrip = constraintTrack.strips.new("Mocap fixes", s_frame, constraintAction) + constraintTrack.name = "Auto fixes " + name + NLATracks.auto_fix_track = constraintTrack.name + constraintAction = bpy.data.actions.new("Auto fixes " + name) + constraintStrip = constraintTrack.strips.new("Auto fixes " + name, s_frame, constraintAction) constraintStrip.extrapolation = "NOTHING" userTrack = anim_data.nla_tracks.new() - userTrack.name = "Mocap manual fix" - userAction = bpy.data.actions.new("Mocap manual fix") - userStrip = userTrack.strips.new("Mocap manual fix", s_frame, userAction) + userTrack.name = "Manual fixes " + name + NLATracks.manual_fix_track = userTrack.name + if enduser_obj.parent.animation_data: + NLATracks.stride_action = enduser_obj.parent.animation_data.action.name + userAction = bpy.data.actions.new("Manual fixes " + name) + userStrip = userTrack.strips.new("Manual fixes " + name, s_frame, userAction) userStrip.extrapolation = "HOLD" #userStrip.blend_type = "MULITPLY" - doesn't work due to work, will be activated soon anim_data.nla_tracks.active = constraintTrack - anim_data.action = constraintAction + NLATracks.active = True + #anim_data.action = constraintAction anim_data.action_extrapolation = "NOTHING" @@ -419,7 +445,7 @@ def totalRetarget(performer_obj, enduser_obj, scene, s_frame, e_frame): bpy.ops.object.mode_set(mode='OBJECT') bpy.ops.object.select_name(name=inter_obj.name, extend=False) bpy.ops.object.delete() - NLASystemInitialize(enduser_obj, s_frame) + NLASystemInitialize(enduser_obj, s_frame, performer_obj.animation_data.action.name) print("retargeting done!") if __name__ == "__main__": diff --git a/release/scripts/startup/ui_mocap.py b/release/scripts/startup/ui_mocap.py index 8ef4c1e7591..3d034b0047a 100644 --- a/release/scripts/startup/ui_mocap.py +++ b/release/scripts/startup/ui_mocap.py @@ -110,6 +110,35 @@ bpy.utils.register_class(MocapConstraint) bpy.types.Armature.mocap_constraints = bpy.props.CollectionProperty(type=MocapConstraint) + +class AnimationStitchSettings(bpy.types.PropertyGroup): + first_action = bpy.props.StringProperty(name="Action 1", + description="First action in stitch") + second_action = bpy.props.StringProperty(name="Action 2", + description="Second action in stitch") + blend_frame = bpy.props.IntProperty(name="Stitch frame", + description="Frame to locate stitch on") + blend_amount = bpy.props.IntProperty(name="Blend amount", + description="Size of blending transitiion, on both sides of the stitch", + default=10) + +bpy.utils.register_class(AnimationStitchSettings) + + +class MocapNLATracks(bpy.types.PropertyGroup): + name = bpy.props.StringProperty() + active = bpy.props.BoolProperty() + base_track = bpy.props.StringProperty() + auto_fix_track = bpy.props.StringProperty() + manual_fix_track = bpy.props.StringProperty() + stride_action = bpy.props.StringProperty() + +bpy.utils.register_class(MocapNLATracks) + +bpy.types.Armature.stitch_settings = bpy.props.PointerProperty(type=AnimationStitchSettings) + +bpy.types.Armature.mocapNLATracks = bpy.props.CollectionProperty(type=MocapNLATracks) + #Update function for IK functionality. Is called when IK prop checkboxes are toggled. @@ -246,6 +275,7 @@ class MocapPanel(bpy.types.Panel): mapRow = self.layout.row() mapRow.operator("mocap.savemapping", text='Save mapping') mapRow.operator("mocap.loadmapping", text='Load mapping') + self.layout.prop(data=performer_obj.animation_data.action, property='name', text='Action Name') self.layout.operator("mocap.retarget", text='RETARGET!') @@ -315,6 +345,16 @@ class ExtraToolsPanel(bpy.types.Panel): def draw(self, context): layout = self.layout layout.operator('mocap.pathediting', text="Follow Path") + layout.label("Animation Stitching") + activeIsArmature = isinstance(context.active_object.data, bpy.types.Armature) + if activeIsArmature: + enduser_arm = context.active_object.data + settings = enduser_arm.stitch_settings + layout.prop_search(settings, "first_action", enduser_arm, "mocapNLATracks") + layout.prop_search(settings, "second_action", enduser_arm, "mocapNLATracks") + layout.prop(settings, "blend_frame") + layout.prop(settings, "blend_amount") + layout.operator('mocap.animstitch', text="Stitch Animations") class OBJECT_OT_RetargetButton(bpy.types.Operator): @@ -323,15 +363,18 @@ class OBJECT_OT_RetargetButton(bpy.types.Operator): bl_label = "Retargets active action from Performer to Enduser" def execute(self, context): + scene = context.scene + s_frame = scene.frame_start + e_frame = scene.frame_end 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 + s_frame, e_frame = performer_obj.animation_data.action.frame_range + s_frame = int(s_frame) + e_frame = int(e_frame) retarget.totalRetarget(performer_obj, enduser_obj, scene, s_frame, e_frame) return {"FINISHED"} @@ -645,6 +688,26 @@ class OBJECT_OT_PathEditing(bpy.types.Operator): return False +class OBJECT_OT_AnimationStitchingButton(bpy.types.Operator): + '''Stitches two defined animations into a single one via alignment of NLA Tracks''' + bl_idname = "mocap.animstitch" + bl_label = "Stitches two defined animations into a single one via alignment of NLA Tracks" + + def execute(self, context): + mocap_tools.anim_stitch(context, context.active_object) + return {"FINISHED"} + + @classmethod + def poll(cls, context): + activeIsArmature = False + if context.active_object: + activeIsArmature = isinstance(context.active_object.data, bpy.types.Armature) + if activeIsArmature: + stitch_settings = context.active_object.data.stitch_settings + return (stitch_settings.first_action and stitch_settings.second_action) + return False + + def register(): bpy.utils.register_module(__name__) -- cgit v1.2.3 From a672ab5e737202bede956a88357a96cf2728df15 Mon Sep 17 00:00:00 2001 From: Joerg Mueller Date: Tue, 9 Aug 2011 14:10:32 +0000 Subject: 3D Audio GSoC: Improved waveform drawing in the sequencer. * Drawing the waveform of a sequencer strip is now independent from whether the sound is cached or not. * Improved drawing of the waveform in the sequencer (especially speed!). * Making it possible to vertically zoom more in the sequencer to better see the waveform for lipsync. * Fixed a bug which crashed blender on loading a sound file via ffmpeg. --- release/scripts/startup/bl_ui/space_sequencer.py | 1 + 1 file changed, 1 insertion(+) (limited to 'release/scripts') diff --git a/release/scripts/startup/bl_ui/space_sequencer.py b/release/scripts/startup/bl_ui/space_sequencer.py index 5320297dce2..19202088faf 100644 --- a/release/scripts/startup/bl_ui/space_sequencer.py +++ b/release/scripts/startup/bl_ui/space_sequencer.py @@ -638,6 +638,7 @@ class SEQUENCER_PT_sound(SequencerButtonsPanel, bpy.types.Panel): row.prop(strip.sound, "use_memory_cache") + layout.prop(strip, "waveform") layout.prop(strip, "volume") layout.prop(strip, "attenuation") layout.prop(strip, "pitch") -- cgit v1.2.3 From 8afad10f986f60e5ccfaed2798067ddcea9b5fcf Mon Sep 17 00:00:00 2001 From: Benjy Cook Date: Wed, 10 Aug 2011 18:40:14 +0000 Subject: Continued changes to storing of retargeted animation data, making it possible to easily switch between all retargeted clips, and stitch them with the future operator --- release/scripts/modules/mocap_constraints.py | 12 ++++-- release/scripts/modules/retarget.py | 64 ++++++++++++++++++++-------- release/scripts/startup/ui_mocap.py | 5 ++- 3 files changed, 57 insertions(+), 24 deletions(-) (limited to 'release/scripts') diff --git a/release/scripts/modules/mocap_constraints.py b/release/scripts/modules/mocap_constraints.py index 63e46f99349..540e8fa06db 100644 --- a/release/scripts/modules/mocap_constraints.py +++ b/release/scripts/modules/mocap_constraints.py @@ -186,6 +186,8 @@ def setConstraint(m_constraint, context): bone = bones[m_constraint.constrained_bone] cons_obj = getConsObj(bone) real_constraint = cons_obj.constraints[m_constraint.real_constraint] + NLATracks = obj.data.mocapNLATracks[obj.data.active_mocap] + obj.animation_data.action = bpy.data.actions[NLATracks.auto_fix_track] #frame changing section setConstraintFraming(m_constraint, context) @@ -364,8 +366,9 @@ def bakeAllConstraints(obj, s_frame, e_frame, bones): simpleBake += [end_bone] for bone in selectedBones: bone.bone.select = True - tracks = [track for track in obj.data.mocapNLATracks if track.active][0] - constraintTrack = obj.animation_data.nla_tracks[tracks.auto_fix_track] + NLATracks = obj.data.mocapNLATracks[obj.data.active_mocap] + obj.animation_data.action = bpy.data.actions[NLATracks.auto_fix_track] + constraintTrack = obj.animation_data.nla_tracks[NLATracks.auto_fix_track] constraintStrip = constraintTrack.strips[0] constraintStrip.action_frame_start = s_frame constraintStrip.action_frame_end = e_frame @@ -404,8 +407,9 @@ def unbakeConstraints(context): obj = context.active_object bones = obj.pose.bones scene = bpy.context.scene - tracks = obj.data.mocapNLATracks[obj.animation_data.action] - constraintTrack = obj.animation_data.nla_tracks[tracks.auto_fix_track] + NLATracks = obj.data.mocapNLATracks[obj.data.active_mocap] + obj.animation_data.action = bpy.data.actions[NLATracks.auto_fix_track] + constraintTrack = obj.animation_data.nla_tracks[NLATracks.auto_fix_track] constraintStrip = constraintTrack.strips[0] action = constraintStrip.action # delete the fcurves on the strip diff --git a/release/scripts/modules/retarget.py b/release/scripts/modules/retarget.py index b3c386e6c4d..a0d89ae6e8b 100644 --- a/release/scripts/modules/retarget.py +++ b/release/scripts/modules/retarget.py @@ -52,7 +52,6 @@ def createDictionary(perf_arm, end_arm): feetBones = [bone.name for bone in perf_arm.bones if bone.foot] return feetBones, root - def loadMapping(perf_arm, end_arm): for end_bone in end_arm.bones: @@ -273,8 +272,8 @@ def copyTranslation(performer_obj, enduser_obj, perfFeet, root, s_frame, e_frame linearAvg.append(hipV.length / endV.length) action_name = performer_obj.animation_data.action.name - #if you have a parent, and that parent is a previously created stride bone - if enduser_obj.parent: + #is there a stride_bone? + if "stride_bone" in bpy.data.objects: stride_action = bpy.data.actions.new("Stride Bone " + action_name) stride_bone = enduser_obj.parent stride_bone.animation_data.action = stride_action @@ -386,17 +385,17 @@ def originalLocationTarget(end_bone, enduser_obj): #create the specified NLA setup for base animation, constraints and tweak layer. -def NLASystemInitialize(enduser_obj, s_frame, name): +def NLASystemInitialize(enduser_arm, context):#enduser_obj, name): + enduser_obj = context.active_object + NLATracks = enduser_arm.mocapNLATracks[enduser_obj.data.active_mocap] + name = NLATracks.name anim_data = enduser_obj.animation_data - if not name in enduser_obj.data.mocapNLATracks: - NLATracks = enduser_obj.data.mocapNLATracks.add() - NLATracks.name = name + s_frame = 0 + print(name) + if ("Base " + name) in bpy.data.actions: + mocapAction = bpy.data.actions[("Base " + name)] else: - NLATracks = enduser_obj.data.mocapNLATracks[name] - for track in enduser_obj.data.mocapNLATracks: - track.active = False - mocapAction = anim_data.action - mocapAction.name = "Base " + name + print("That retargeted anim has no base action") anim_data.use_nla = True for track in anim_data.nla_tracks: anim_data.nla_tracks.remove(track) @@ -407,28 +406,46 @@ def NLASystemInitialize(enduser_obj, s_frame, name): constraintTrack = anim_data.nla_tracks.new() constraintTrack.name = "Auto fixes " + name NLATracks.auto_fix_track = constraintTrack.name - constraintAction = bpy.data.actions.new("Auto fixes " + name) + if ("Auto fixes " + name) in bpy.data.actions: + constraintAction = bpy.data.actions[("Auto fixes " + name)] + else: + constraintAction = bpy.data.actions.new("Auto fixes " + name) constraintStrip = constraintTrack.strips.new("Auto fixes " + name, s_frame, constraintAction) constraintStrip.extrapolation = "NOTHING" userTrack = anim_data.nla_tracks.new() userTrack.name = "Manual fixes " + name NLATracks.manual_fix_track = userTrack.name - if enduser_obj.parent.animation_data: - NLATracks.stride_action = enduser_obj.parent.animation_data.action.name - userAction = bpy.data.actions.new("Manual fixes " + name) + if ("Manual fixes " + name) in bpy.data.actions: + userAction = bpy.data.actions[("Manual fixes " + name)] + else: + userAction = bpy.data.actions.new("Manual fixes " + name) userStrip = userTrack.strips.new("Manual fixes " + name, s_frame, userAction) userStrip.extrapolation = "HOLD" #userStrip.blend_type = "MULITPLY" - doesn't work due to work, will be activated soon anim_data.nla_tracks.active = constraintTrack - NLATracks.active = True #anim_data.action = constraintAction anim_data.action_extrapolation = "NOTHING" + #set the stride_bone's action + if "stride_bone" in bpy.data.objects: + stride_bone = bpy.data.objects["stride_bone"] + if NLATracks.stride_action: + stride_bone.animation_data.action = bpy.data.actions[NLATracks.stride_action] + else: + NLATracks.stride_action = stride_bone.animation_data.action.name + anim_data.action = None #Main function that runs the retargeting sequence. def totalRetarget(performer_obj, enduser_obj, scene, s_frame, e_frame): perf_arm = performer_obj.data end_arm = enduser_obj.data + + try: + enduser_obj.animation_data.action = bpy.data.actions.new("temp") + except: + print("no need to create new action") + + print("creating Dictionary") feetBones, root = createDictionary(perf_arm, end_arm) print("cleaning stuff up") @@ -437,7 +454,11 @@ def totalRetarget(performer_obj, enduser_obj, scene, s_frame, e_frame): print("Creating intermediate armature (for first pass)") inter_obj = createIntermediate(performer_obj, enduser_obj, root, s_frame, e_frame, scene) print("First pass: retargeting from intermediate to end user") + + retargetEnduser(inter_obj, enduser_obj, root, s_frame, e_frame, scene) + name = performer_obj.animation_data.action.name + enduser_obj.animation_data.action.name = "Base " + name print("Second pass: retargeting root translation and clean up") 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) @@ -445,7 +466,14 @@ def totalRetarget(performer_obj, enduser_obj, scene, s_frame, e_frame): 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, performer_obj.animation_data.action.name) + bpy.ops.object.select_name(name=enduser_obj.name, extend=False) + + if not name in [tracks.name for tracks in end_arm.mocapNLATracks]: + NLATracks = end_arm.mocapNLATracks.add() + NLATracks.name = name + else: + NLATracks = end_arm.mocapNLATracks[name] + end_arm.active_mocap = name print("retargeting done!") if __name__ == "__main__": diff --git a/release/scripts/startup/ui_mocap.py b/release/scripts/startup/ui_mocap.py index 3d034b0047a..01802893c4c 100644 --- a/release/scripts/startup/ui_mocap.py +++ b/release/scripts/startup/ui_mocap.py @@ -127,7 +127,6 @@ bpy.utils.register_class(AnimationStitchSettings) class MocapNLATracks(bpy.types.PropertyGroup): name = bpy.props.StringProperty() - active = bpy.props.BoolProperty() base_track = bpy.props.StringProperty() auto_fix_track = bpy.props.StringProperty() manual_fix_track = bpy.props.StringProperty() @@ -136,7 +135,7 @@ class MocapNLATracks(bpy.types.PropertyGroup): bpy.utils.register_class(MocapNLATracks) bpy.types.Armature.stitch_settings = bpy.props.PointerProperty(type=AnimationStitchSettings) - +bpy.types.Armature.active_mocap = bpy.props.StringProperty(update=retarget.NLASystemInitialize) bpy.types.Armature.mocapNLATracks = bpy.props.CollectionProperty(type=MocapNLATracks) #Update function for IK functionality. Is called when IK prop checkboxes are toggled. @@ -349,6 +348,8 @@ class ExtraToolsPanel(bpy.types.Panel): activeIsArmature = isinstance(context.active_object.data, bpy.types.Armature) if activeIsArmature: enduser_arm = context.active_object.data + layout.label("Retargeted Animations:") + layout.prop_search(enduser_arm, "active_mocap",enduser_arm, "mocapNLATracks") settings = enduser_arm.stitch_settings layout.prop_search(settings, "first_action", enduser_arm, "mocapNLATracks") layout.prop_search(settings, "second_action", enduser_arm, "mocapNLATracks") -- cgit v1.2.3 From b5d556d432044f8b6f74962b228024afa00bc882 Mon Sep 17 00:00:00 2001 From: Benjy Cook Date: Wed, 10 Aug 2011 18:41:04 +0000 Subject: Initial programming of stitch animation operator. WIP --- release/scripts/modules/mocap_tools.py | 56 ++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) (limited to 'release/scripts') diff --git a/release/scripts/modules/mocap_tools.py b/release/scripts/modules/mocap_tools.py index fa307a36a57..0e296faf9f8 100644 --- a/release/scripts/modules/mocap_tools.py +++ b/release/scripts/modules/mocap_tools.py @@ -760,8 +760,64 @@ def path_editing(context, stride_obj, path): def anim_stitch(context, enduser_obj): + selected_bone = enduser_obj.pose.bones["Toes.L"] + second_offset = 5 + stitch_settings = enduser_obj.data.stitch_settings action_1 = stitch_settings.first_action action_2 = stitch_settings.second_action + scene = context.scene TrackNamesA = enduser_obj.data.mocapNLATracks[action_1] TrackNamesB = enduser_obj.data.mocapNLATracks[action_2] + enduser_obj.data.active_mocap = action_1 + anim_data = enduser_obj.animation_data + # add tracks for action 2 + mocapAction = bpy.data.actions[TrackNamesB.base_track] + mocapTrack = anim_data.nla_tracks.new() + mocapTrack.name = TrackNamesB.base_track + mocapStrip = mocapTrack.strips.new(TrackNamesB.base_track, stitch_settings.blend_frame, mocapAction) + mocapStrip.extrapolation = "HOLD_FORWARD" + mocapStrip.action_frame_start+=second_offset + mocapStrip.action_frame_end+=second_offset + constraintTrack = anim_data.nla_tracks.new() + constraintTrack.name = TrackNamesB.auto_fix_track + constraintAction = bpy.data.actions[TrackNamesB.auto_fix_track] + constraintStrip = constraintTrack.strips.new(TrackNamesB.auto_fix_track, stitch_settings.blend_frame, constraintAction) + constraintStrip.extrapolation = "HOLD_FORWARD" + userTrack = anim_data.nla_tracks.new() + userTrack.name = TrackNamesB.manual_fix_track + userAction = bpy.data.actions[TrackNamesB.manual_fix_track] + userStrip = userTrack.strips.new(TrackNamesB.manual_fix_track, stitch_settings.blend_frame, userAction) + userStrip.extrapolation = "HOLD_FORWARD" + #stride bone + if enduser_obj.parent: + if enduser_obj.parent.name == "stride_bone": + stride_bone = enduser_obj.parent + stride_anim_data = stride_bone.animation_data + stride_anim_data.use_nla = True + stride_anim_data.action = None + for track in stride_anim_data.nla_tracks: + stride_anim_data.nla_tracks.remove(track) + actionATrack = stride_anim_data.nla_tracks.new() + actionATrack.name = TrackNamesA.stride_action + actionAStrip = actionATrack.strips.new(TrackNamesA.stride_action, 0, bpy.data.actions[TrackNamesA.stride_action]) + actionAStrip.extrapolation = "NOTHING" + actionBTrack = stride_anim_data.nla_tracks.new() + actionBTrack.name = TrackNamesB.stride_action + actionBStrip = actionBTrack.strips.new(TrackNamesB.stride_action, stitch_settings.blend_frame, bpy.data.actions[TrackNamesB.stride_action]) + actionBStrip.extrapolation = "NOTHING" + #we need to change the stride_bone's action to add the offset + scene.frame_set(stitch_settings.blend_frame - 1) + desired_pos = selected_bone.tail * enduser_obj.matrix_world + scene.frame_set(stitch_settings.blend_frame) + actual_pos = selected_bone.tail * enduser_obj.matrix_world + offset = actual_pos - desired_pos + print(offset) + + for i,fcurve in enumerate([fcurve for fcurve in bpy.data.actions[TrackNamesB.stride_action].fcurves if fcurve.data_path=="location"]): + print(offset[i],i,fcurve.array_index) + for pt in fcurve.keyframe_points: + pt.co.y-=offset[i] + pt.handle_left.y-=offset[i] + pt.handle_right.y-=offset[i] + -- cgit v1.2.3 From 1eaeaf8cd8ea7ee923b6aeb26b031368ff6d679a Mon Sep 17 00:00:00 2001 From: Benjy Cook Date: Wed, 10 Aug 2011 20:36:52 +0000 Subject: Fix for previous commit, now a fake user flag is added when switching between retargeted animations, so they don't get lost on save --- release/scripts/modules/retarget.py | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'release/scripts') diff --git a/release/scripts/modules/retarget.py b/release/scripts/modules/retarget.py index a0d89ae6e8b..9415553d4ff 100644 --- a/release/scripts/modules/retarget.py +++ b/release/scripts/modules/retarget.py @@ -275,6 +275,7 @@ def copyTranslation(performer_obj, enduser_obj, perfFeet, root, s_frame, e_frame #is there a stride_bone? if "stride_bone" in bpy.data.objects: stride_action = bpy.data.actions.new("Stride Bone " + action_name) + stride_action.use_fake_user = True stride_bone = enduser_obj.parent stride_bone.animation_data.action = stride_action else: @@ -410,6 +411,7 @@ def NLASystemInitialize(enduser_arm, context):#enduser_obj, name): constraintAction = bpy.data.actions[("Auto fixes " + name)] else: constraintAction = bpy.data.actions.new("Auto fixes " + name) + constraintAction.use_fake_user = True constraintStrip = constraintTrack.strips.new("Auto fixes " + name, s_frame, constraintAction) constraintStrip.extrapolation = "NOTHING" userTrack = anim_data.nla_tracks.new() @@ -419,6 +421,7 @@ def NLASystemInitialize(enduser_arm, context):#enduser_obj, name): userAction = bpy.data.actions[("Manual fixes " + name)] else: userAction = bpy.data.actions.new("Manual fixes " + name) + userAction.use_fake_user = True userStrip = userTrack.strips.new("Manual fixes " + name, s_frame, userAction) userStrip.extrapolation = "HOLD" #userStrip.blend_type = "MULITPLY" - doesn't work due to work, will be activated soon @@ -432,6 +435,7 @@ def NLASystemInitialize(enduser_arm, context):#enduser_obj, name): stride_bone.animation_data.action = bpy.data.actions[NLATracks.stride_action] else: NLATracks.stride_action = stride_bone.animation_data.action.name + stride_bone.animation_data.action.use_fake_user = True anim_data.action = None @@ -442,6 +446,7 @@ def totalRetarget(performer_obj, enduser_obj, scene, s_frame, e_frame): try: enduser_obj.animation_data.action = bpy.data.actions.new("temp") + enduser_obj.animation_data.action.use_fake_user = True except: print("no need to create new action") -- cgit v1.2.3 From fba1f50d0af22199f8d73aa6eb5e95e75c3f3d6a Mon Sep 17 00:00:00 2001 From: Benjy Cook Date: Wed, 10 Aug 2011 20:37:57 +0000 Subject: Mostly finished implementation of animation stitching, with lock bone functionality, allowing the user to choose a bone that maintains its position during the blend --- release/scripts/modules/mocap_tools.py | 22 ++++++++++++++-------- release/scripts/startup/ui_mocap.py | 8 ++++++++ 2 files changed, 22 insertions(+), 8 deletions(-) (limited to 'release/scripts') diff --git a/release/scripts/modules/mocap_tools.py b/release/scripts/modules/mocap_tools.py index 0e296faf9f8..b490d571dd8 100644 --- a/release/scripts/modules/mocap_tools.py +++ b/release/scripts/modules/mocap_tools.py @@ -760,12 +760,13 @@ def path_editing(context, stride_obj, path): def anim_stitch(context, enduser_obj): - selected_bone = enduser_obj.pose.bones["Toes.L"] - second_offset = 5 - stitch_settings = enduser_obj.data.stitch_settings action_1 = stitch_settings.first_action action_2 = stitch_settings.second_action + if stitch_settings.stick_bone!="": + selected_bone = enduser_obj.pose.bones[stitch_settings.stick_bone] + else: + selected_bone = enduser_obj.pose.bones[0] scene = context.scene TrackNamesA = enduser_obj.data.mocapNLATracks[action_1] TrackNamesB = enduser_obj.data.mocapNLATracks[action_2] @@ -777,18 +778,21 @@ def anim_stitch(context, enduser_obj): mocapTrack.name = TrackNamesB.base_track mocapStrip = mocapTrack.strips.new(TrackNamesB.base_track, stitch_settings.blend_frame, mocapAction) mocapStrip.extrapolation = "HOLD_FORWARD" - mocapStrip.action_frame_start+=second_offset - mocapStrip.action_frame_end+=second_offset + mocapStrip.blend_in = stitch_settings.blend_amount + mocapStrip.action_frame_start+=stitch_settings.second_offset + mocapStrip.action_frame_end+=stitch_settings.second_offset constraintTrack = anim_data.nla_tracks.new() constraintTrack.name = TrackNamesB.auto_fix_track constraintAction = bpy.data.actions[TrackNamesB.auto_fix_track] constraintStrip = constraintTrack.strips.new(TrackNamesB.auto_fix_track, stitch_settings.blend_frame, constraintAction) constraintStrip.extrapolation = "HOLD_FORWARD" + constraintStrip.blend_in = stitch_settings.blend_amount userTrack = anim_data.nla_tracks.new() userTrack.name = TrackNamesB.manual_fix_track userAction = bpy.data.actions[TrackNamesB.manual_fix_track] userStrip = userTrack.strips.new(TrackNamesB.manual_fix_track, stitch_settings.blend_frame, userAction) userStrip.extrapolation = "HOLD_FORWARD" + userStrip.blend_in = stitch_settings.blend_amount #stride bone if enduser_obj.parent: if enduser_obj.parent.name == "stride_bone": @@ -805,14 +809,16 @@ def anim_stitch(context, enduser_obj): actionBTrack = stride_anim_data.nla_tracks.new() actionBTrack.name = TrackNamesB.stride_action actionBStrip = actionBTrack.strips.new(TrackNamesB.stride_action, stitch_settings.blend_frame, bpy.data.actions[TrackNamesB.stride_action]) + actionBStrip.action_frame_start+=stitch_settings.second_offset + actionBStrip.action_frame_end+=stitch_settings.second_offset + actionBStrip.blend_in = stitch_settings.blend_amount actionBStrip.extrapolation = "NOTHING" #we need to change the stride_bone's action to add the offset scene.frame_set(stitch_settings.blend_frame - 1) - desired_pos = selected_bone.tail * enduser_obj.matrix_world + desired_pos = (selected_bone.matrix.to_translation() * enduser_obj.matrix_world) scene.frame_set(stitch_settings.blend_frame) - actual_pos = selected_bone.tail * enduser_obj.matrix_world + actual_pos = (selected_bone.matrix.to_translation() * enduser_obj.matrix_world) offset = actual_pos - desired_pos - print(offset) for i,fcurve in enumerate([fcurve for fcurve in bpy.data.actions[TrackNamesB.stride_action].fcurves if fcurve.data_path=="location"]): print(offset[i],i,fcurve.array_index) diff --git a/release/scripts/startup/ui_mocap.py b/release/scripts/startup/ui_mocap.py index 01802893c4c..0788366547e 100644 --- a/release/scripts/startup/ui_mocap.py +++ b/release/scripts/startup/ui_mocap.py @@ -121,6 +121,12 @@ class AnimationStitchSettings(bpy.types.PropertyGroup): blend_amount = bpy.props.IntProperty(name="Blend amount", description="Size of blending transitiion, on both sides of the stitch", default=10) + second_offset = bpy.props.IntProperty(name="Second offset", + description="Frame offset for 2nd animation, where it should start", + default=10) + stick_bone = bpy.props.StringProperty(name="Stick Bone", + description="Bone to freeze during transition", + default="") bpy.utils.register_class(AnimationStitchSettings) @@ -355,6 +361,8 @@ class ExtraToolsPanel(bpy.types.Panel): layout.prop_search(settings, "second_action", enduser_arm, "mocapNLATracks") layout.prop(settings, "blend_frame") layout.prop(settings, "blend_amount") + layout.prop(settings, "second_offset") + layout.prop_search(settings, "stick_bone", context.active_object.pose, "bones") layout.operator('mocap.animstitch', text="Stitch Animations") -- cgit v1.2.3 From fee7337249342c3d5a332358883af9afe961f38d Mon Sep 17 00:00:00 2001 From: Joerg Mueller Date: Thu, 11 Aug 2011 11:41:24 +0000 Subject: 3D Audio GSoC: Adding a mono flag to mixdown non-mono sounds for 3D audio. * Added mono sound loading. * Bugfix: AUD_COMPARE_SPECS usage was wrong. * Bugfix: JOS resampler = instead of ==. * Bugfix: Change of a sound should apply settings in AUD_SequencerHandle. * Bugfix: Memory leak when canceling open sound operator. --- release/scripts/startup/bl_ui/properties_data_speaker.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'release/scripts') diff --git a/release/scripts/startup/bl_ui/properties_data_speaker.py b/release/scripts/startup/bl_ui/properties_data_speaker.py index 45f2fa5d1f7..fe9f798af0c 100644 --- a/release/scripts/startup/bl_ui/properties_data_speaker.py +++ b/release/scripts/startup/bl_ui/properties_data_speaker.py @@ -63,7 +63,7 @@ class DATA_PT_speaker(DataButtonsPanel, bpy.types.Panel): split = layout.split(percentage=0.75) - split.template_ID(speaker, "sound", open="sound.open") + split.template_ID(speaker, "sound", open="sound.open_mono") split.prop(speaker, "muted") split = layout.split() -- cgit v1.2.3 From 87e9c0ffaa5f8f64ccdea5c2ce74dfbd0edf0e43 Mon Sep 17 00:00:00 2001 From: Benjy Cook Date: Thu, 11 Aug 2011 13:47:49 +0000 Subject: Advanced Retargeting option: If the end user armature is complex, on the level of Sintel/Mancandy rigs, the user is requested to mark Advanced Retargeting, and constraints will be semi automatically configured to retarget the animation and then Retargeting will bake and remove these constraints --- release/scripts/modules/retarget.py | 89 ++++++++++++++++++++++++++++++++----- release/scripts/startup/ui_mocap.py | 51 ++++++++++++++++++++- 2 files changed, 127 insertions(+), 13 deletions(-) (limited to 'release/scripts') diff --git a/release/scripts/modules/retarget.py b/release/scripts/modules/retarget.py index 9415553d4ff..88a5c3a620a 100644 --- a/release/scripts/modules/retarget.py +++ b/release/scripts/modules/retarget.py @@ -21,6 +21,7 @@ import bpy from mathutils import * from math import radians, acos +from bl_operators import nla import cProfile @@ -264,6 +265,7 @@ def copyTranslation(performer_obj, enduser_obj, perfFeet, root, s_frame, e_frame v = locDeriv[key][i] hipV = locDeriv[perfRoot][i] endV = locDeriv[perf_bones[key].bone.map][i] + print(v.length,) if (v.length < 0.1): #this is a plant frame. #lets see what the original hip delta is, and the corresponding @@ -284,6 +286,7 @@ def copyTranslation(performer_obj, enduser_obj, perfFeet, root, s_frame, e_frame stride_bone.name = "stride_bone" print(stride_bone) stride_bone.location = Vector((0, 0, 0)) + print(linearAvg) if linearAvg: #determine the average change in scale needed avg = sum(linearAvg) / len(linearAvg) @@ -295,6 +298,8 @@ def copyTranslation(performer_obj, enduser_obj, perfFeet, root, s_frame, e_frame newTranslation = (tailLoc(perf_bones[perfRoot]) / avg) stride_bone.location = enduser_obj_mat * (newTranslation - initialPos) stride_bone.keyframe_insert("location") + else: + stride_bone.keyframe_insert("location") stride_bone.animation_data.action.name = ("Stride Bone " + action_name) return stride_bone @@ -439,10 +444,66 @@ def NLASystemInitialize(enduser_arm, context):#enduser_obj, name): anim_data.action = None +def preAdvancedRetargeting(performer_obj, enduser_obj): + createDictionary(performer_obj.data, enduser_obj.data) + bones = enduser_obj.pose.bones + map_bones = [bone for bone in bones if bone.bone.reverseMap] + for bone in map_bones: + perf_bone = bone.bone.reverseMap[0].name + addLocalRot = False; + if bone.bone.use_connect or not bone.constraints: + locks = bone.lock_location + if not (locks[0] or locks[1] or locks[2]): + cons = bone.constraints.new('COPY_LOCATION') + cons.name = "retargetTemp" + cons.use_x = not locks[0] + cons.use_y = not locks[1] + cons.use_z = not locks[2] + cons.target = performer_obj + cons.subtarget = perf_bone + addLocalRot = True + + + cons2 = bone.constraints.new('COPY_ROTATION') + cons2.name = "retargetTemp" + locks = bone.lock_rotation + cons2.use_x = not locks[0] + cons2.use_y = not locks[1] + cons2.use_z = not locks[2] + cons2.target = performer_obj + cons2.subtarget = perf_bone + + if addLocalRot: + for constraint in bone.constraints: + if constraint.type == 'COPY_ROTATION': + constraint.target_space = 'LOCAL' + constraint.owner_space = 'LOCAL_WITH_PARENT' + + +def prepareForBake(enduser_obj): + bones = enduser_obj.pose.bones + for bone in bones: + bone.bone.select = False + map_bones = [bone for bone in bones if bone.bone.reverseMap] + for bone in map_bones: + for cons in bone.constraints: + if "retargetTemp" in cons.name: + bone.bone.select = True + +def cleanTempConstraints(enduser_obj): + bones = enduser_obj.pose.bones + map_bones = [bone for bone in bones if bone.bone.reverseMap] + for bone in map_bones: + for cons in bone.constraints: + if "retargetTemp" in cons.name: + bone.constraints.remove(cons) + #Main function that runs the retargeting sequence. +#If advanced == True, we assume constraint's were already created def totalRetarget(performer_obj, enduser_obj, scene, s_frame, e_frame): perf_arm = performer_obj.data end_arm = enduser_obj.data + advanced = end_arm.advancedRetarget try: enduser_obj.animation_data.action = bpy.data.actions.new("temp") @@ -450,27 +511,33 @@ def totalRetarget(performer_obj, enduser_obj, scene, s_frame, e_frame): except: print("no need to create new action") - print("creating Dictionary") feetBones, root = createDictionary(perf_arm, end_arm) print("cleaning stuff up") perf_obj_mat, enduser_obj_mat = cleanAndStoreObjMat(performer_obj, enduser_obj) - turnOffIK(enduser_obj) - print("Creating intermediate armature (for first pass)") - inter_obj = createIntermediate(performer_obj, enduser_obj, root, s_frame, e_frame, scene) - print("First pass: retargeting from intermediate to end user") - - - retargetEnduser(inter_obj, enduser_obj, root, s_frame, e_frame, scene) + if not advanced: + turnOffIK(enduser_obj) + print("Creating intermediate armature (for first pass)") + inter_obj = createIntermediate(performer_obj, enduser_obj, root, s_frame, e_frame, scene) + print("First pass: retargeting from intermediate to end user") + retargetEnduser(inter_obj, enduser_obj, root, s_frame, e_frame, scene) + else: + prepareForBake(enduser_obj) + print("Retargeting pose (Advanced Retarget)") + nla.bake(s_frame, e_frame, action=enduser_obj.animation_data.action, only_selected=True, do_pose=True, do_object=False) name = performer_obj.animation_data.action.name enduser_obj.animation_data.action.name = "Base " + name print("Second pass: retargeting root translation and clean up") 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) + if not advanced: + 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() + if not advanced: + bpy.ops.object.select_name(name=inter_obj.name, extend=False) + bpy.ops.object.delete() + else: + cleanTempConstraints(enduser_obj) bpy.ops.object.select_name(name=enduser_obj.name, extend=False) if not name in [tracks.name for tracks in end_arm.mocapNLATracks]: diff --git a/release/scripts/startup/ui_mocap.py b/release/scripts/startup/ui_mocap.py index 0788366547e..06d0bb0b415 100644 --- a/release/scripts/startup/ui_mocap.py +++ b/release/scripts/startup/ui_mocap.py @@ -140,9 +140,26 @@ class MocapNLATracks(bpy.types.PropertyGroup): bpy.utils.register_class(MocapNLATracks) + +def advancedRetargetToggle(self, context): + 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") + return + else: + performer_obj = performer_obj[0] + if self.advancedRetarget: + retarget.preAdvancedRetargeting(performer_obj, enduser_obj) + else: + retarget.cleanTempConstraints(enduser_obj) + + + bpy.types.Armature.stitch_settings = bpy.props.PointerProperty(type=AnimationStitchSettings) bpy.types.Armature.active_mocap = bpy.props.StringProperty(update=retarget.NLASystemInitialize) bpy.types.Armature.mocapNLATracks = bpy.props.CollectionProperty(type=MocapNLATracks) +bpy.types.Armature.advancedRetarget = bpy.props.BoolProperty(default=False, update=advancedRetargetToggle) #Update function for IK functionality. Is called when IK prop checkboxes are toggled. @@ -189,7 +206,7 @@ def toggleIKBone(self, context): for bone in cnstrn_bone.parent_recursive: if not bone.is_in_ik_chain: bone.IKRetarget = False - + class MocapMapping(bpy.types.PropertyGroup): name = bpy.props.StringProperty() @@ -281,6 +298,7 @@ class MocapPanel(bpy.types.Panel): mapRow.operator("mocap.savemapping", text='Save mapping') mapRow.operator("mocap.loadmapping", text='Load mapping') self.layout.prop(data=performer_obj.animation_data.action, property='name', text='Action Name') + self.layout.prop(enduser_arm, "advancedRetarget", text='Advanced Retarget') self.layout.operator("mocap.retarget", text='RETARGET!') @@ -396,6 +414,35 @@ class OBJECT_OT_RetargetButton(bpy.types.Operator): return activeIsArmature and isinstance(performer_obj[0].data, bpy.types.Armature) else: return False + + + #~ class OBJECT_OT_AdvancedRetargetButton(bpy.types.Operator): + #~ '''Prepare for advanced retargeting ''' + #~ bl_idname = "mocap.preretarget" + #~ bl_label = "Prepares retarget of active action from Performer to Enduser" + + #~ def execute(self, context): + #~ scene = context.scene + #~ s_frame = scene.frame_start + #~ e_frame = scene.frame_end + #~ 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] + #~ retarget.preAdvancedRetargeting(performer_obj, enduser_obj) + #~ 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): @@ -715,7 +762,7 @@ class OBJECT_OT_AnimationStitchingButton(bpy.types.Operator): stitch_settings = context.active_object.data.stitch_settings return (stitch_settings.first_action and stitch_settings.second_action) return False - + def register(): bpy.utils.register_module(__name__) -- cgit v1.2.3 From 05b7ccb736b88b806d4df3c2f83aff9773756099 Mon Sep 17 00:00:00 2001 From: Benjy Cook Date: Thu, 11 Aug 2011 14:50:19 +0000 Subject: Optimizations following intensive profiling of retarget and other lengthy functions. Retargeting now takes ~30% less time --- release/scripts/modules/mocap_tools.py | 9 ++---- release/scripts/modules/retarget.py | 55 +++++++++++++++++++++------------- release/scripts/startup/ui_mocap.py | 1 + 3 files changed, 39 insertions(+), 26 deletions(-) (limited to 'release/scripts') diff --git a/release/scripts/modules/mocap_tools.py b/release/scripts/modules/mocap_tools.py index b490d571dd8..3f821270e3c 100644 --- a/release/scripts/modules/mocap_tools.py +++ b/release/scripts/modules/mocap_tools.py @@ -660,15 +660,12 @@ def limit_dof(context, performer_obj, enduser_obj): srcParent = bone.parent parent_mat = srcParent.matrix parent_rest = end_bone.parent.bone.matrix_local - parent_rest_inv = parent_rest.copy() - parent_rest_inv.invert() - parent_mat_inv = parent_mat.copy() - parent_mat_inv.invert() + parent_rest_inv = parent_rest.inverted() + parent_mat_inv = parent_mat.inverted() bake_matrix = parent_mat_inv * bake_matrix rest_matrix = parent_rest_inv * rest_matrix - rest_matrix_inv = rest_matrix.copy() - rest_matrix_inv.invert() + rest_matrix_inv = rest_matrix.inverted() bake_matrix = rest_matrix_inv * bake_matrix mat = bake_matrix diff --git a/release/scripts/modules/retarget.py b/release/scripts/modules/retarget.py index 88a5c3a620a..0235bfc1474 100644 --- a/release/scripts/modules/retarget.py +++ b/release/scripts/modules/retarget.py @@ -79,8 +79,7 @@ def createIntermediate(performer_obj, enduser_obj, root, s_frame, e_frame, scene def singleBoneRetarget(inter_bone, perf_bone): perf_world_rotation = perf_bone.matrix * performer_obj.matrix_world inter_world_base_rotation = inter_bone.bone.matrix_local * inter_obj.matrix_world - inter_world_base_inv = Matrix(inter_world_base_rotation) - inter_world_base_inv.invert() + inter_world_base_inv = inter_world_base_rotation.inverted() return (inter_world_base_inv.to_3x3() * perf_world_rotation.to_3x3()).to_4x4() #uses 1to1 and interpolation/averaging to match many to 1 retarget @@ -178,15 +177,12 @@ def retargetEnduser(inter_obj, enduser_obj, root, s_frame, e_frame, scene): srcParent = srcParent.parent parent_mat = srcParent.matrix parent_rest = trg_bone.parent.bone.matrix_local - parent_rest_inv = parent_rest.copy() - parent_rest_inv.invert() - parent_mat_inv = parent_mat.copy() - parent_mat_inv.invert() + parent_rest_inv = parent_rest.inverted() + parent_mat_inv = parent_mat.inverted() bake_matrix = parent_mat_inv * bake_matrix rest_matrix = parent_rest_inv * rest_matrix - rest_matrix_inv = rest_matrix.copy() - rest_matrix_inv.invert() + rest_matrix_inv = rest_matrix.inverted() bake_matrix = rest_matrix_inv * bake_matrix end_bone.matrix_basis = bake_matrix rot_mode = end_bone.rotation_mode @@ -247,26 +243,29 @@ def copyTranslation(performer_obj, enduser_obj, perfFeet, root, s_frame, e_frame # now we take our locDict and analyze it. # we need to derive all chains + + def locDeriv(key, t): + graph = locDict[key] + return graph[t + 1] - graph[t] - locDeriv = {} - for key in locDictKeys: - locDeriv[key] = [] + #~ locDeriv = {} + #~ for key in locDictKeys: + #~ locDeriv[key] = [] - for key in locDict.keys(): - graph = locDict[key] - locDeriv[key] = [graph[t + 1] - graph[t] for t in range(len(graph) - 1)] + #~ for key in locDict.keys(): + #~ graph = locDict[key] + #~ locDeriv[key] = [graph[t + 1] - graph[t] for t in range(len(graph) - 1)] # now find the plant frames, where perfFeet don't move much linearAvg = [] for key in perfFeet: - for i in range(len(locDeriv[key]) - 1): - v = locDeriv[key][i] - hipV = locDeriv[perfRoot][i] - endV = locDeriv[perf_bones[key].bone.map][i] - print(v.length,) + for i in range(len(locDict[key]) - 1): + v = locDeriv(key,i) if (v.length < 0.1): + hipV = locDeriv(perfRoot,i) + endV = locDeriv(perf_bones[key].bone.map,i) #this is a plant frame. #lets see what the original hip delta is, and the corresponding #end bone's delta @@ -547,6 +546,22 @@ def totalRetarget(performer_obj, enduser_obj, scene, s_frame, e_frame): NLATracks = end_arm.mocapNLATracks[name] end_arm.active_mocap = name print("retargeting done!") + +def profileWrapper(): + context = bpy.context + scene = context.scene + s_frame = scene.frame_start + e_frame = scene.frame_end + 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] + s_frame, e_frame = performer_obj.animation_data.action.frame_range + s_frame = int(s_frame) + e_frame = int(e_frame) + totalRetarget(performer_obj, enduser_obj, scene, s_frame, e_frame) if __name__ == "__main__": - totalRetarget() + cProfile.run("profileWrapper()") diff --git a/release/scripts/startup/ui_mocap.py b/release/scripts/startup/ui_mocap.py index 06d0bb0b415..dd5e6fa5d6d 100644 --- a/release/scripts/startup/ui_mocap.py +++ b/release/scripts/startup/ui_mocap.py @@ -25,6 +25,7 @@ from bpy import * import mocap_constraints import retarget import mocap_tools + ### reloads modules (for testing purposes only) from imp import reload reload(mocap_constraints) -- cgit v1.2.3 From f1a8c26aa3a8e3e3628d279ad32f3e1ea261a695 Mon Sep 17 00:00:00 2001 From: Benjy Cook Date: Thu, 11 Aug 2011 16:46:27 +0000 Subject: Additional work on animation stitching, now with auto-guess capability. Only a few bugs left, regarding animations translation --- release/scripts/modules/mocap_tools.py | 90 ++++++++++++++++++++++------------ release/scripts/modules/retarget.py | 27 ++++++---- release/scripts/startup/ui_mocap.py | 20 ++++++++ 3 files changed, 96 insertions(+), 41 deletions(-) (limited to 'release/scripts') diff --git a/release/scripts/modules/mocap_tools.py b/release/scripts/modules/mocap_tools.py index 3f821270e3c..f4b6a93f531 100644 --- a/release/scripts/modules/mocap_tools.py +++ b/release/scripts/modules/mocap_tools.py @@ -105,64 +105,75 @@ class dataPoint: self.u = u -def autoloop_anim(): - context = bpy.context - obj = context.active_object - fcurves = [x for x in obj.animation_data.action.fcurves if x.select] - - data = [] - end = len(fcurves[0].keyframe_points) +def crossCorrelationMatch(curvesA, curvesB, margin): + dataA = [] + dataB = [] + end = len(curvesA[0].keyframe_points) for i in range(1, end): vec = [] - for fcurve in fcurves: + for fcurve in curvesA: vec.append(fcurve.evaluate(i)) - data.append(NdVector(vec)) + dataA.append(NdVector(vec)) + vec = [] + for fcurve in curvesB: + vec.append(fcurve.evaluate(i)) + dataB.append(NdVector(vec)) def comp(a, b): return a * b - N = len(data) + N = len(dataA) Rxy = [0.0] * N for i in range(N): for j in range(i, min(i + N, N)): - Rxy[i] += comp(data[j], data[j - i]) + Rxy[i] += comp(dataA[j], dataB[j - i]) for j in range(i): - Rxy[i] += comp(data[j], data[j - i + N]) + Rxy[i] += comp(dataA[j], dataB[j - i + N]) Rxy[i] /= float(N) - def bestLocalMaximum(Rxy): Rxyd = [Rxy[i] - Rxy[i - 1] for i in range(1, len(Rxy))] maxs = [] for i in range(1, len(Rxyd) - 1): a = Rxyd[i - 1] b = Rxyd[i] - print(a, b) #sign change (zerocrossing) at point i, denoting max point (only) if (a >= 0 and b < 0) or (a < 0 and b >= 0): maxs.append((i, max(Rxy[i], Rxy[i - 1]))) - return max(maxs, key=lambda x: x[1])[0] - flm = bestLocalMaximum(Rxy[0:int(len(Rxy))]) - - diff = [] - - for i in range(len(data) - flm): - diff.append((data[i] - data[i + flm]).lengthSq) + return [x[0] for x in maxs] + #~ return max(maxs, key=lambda x: x[1])[0] + + flms = bestLocalMaximum(Rxy[0:int(len(Rxy))]) + ss = [] + for flm in flms: + diff = [] + + for i in range(len(dataA) - flm): + diff.append((dataA[i] - dataB[i + flm]).lengthSq) + + def lowerErrorSlice(diff, e): + #index, error at index + bestSlice = (0, 100000) + for i in range(e, len(diff) - e): + errorSlice = sum(diff[i - e:i + e + 1]) + if errorSlice < bestSlice[1]: + bestSlice = (i, errorSlice, flm) + return bestSlice + + s = lowerErrorSlice(diff, margin) + ss.append(s) - def lowerErrorSlice(diff, e): - #index, error at index - bestSlice = (0, 100000) - for i in range(e, len(diff) - e): - errorSlice = sum(diff[i - e:i + e + 1]) - if errorSlice < bestSlice[1]: - bestSlice = (i, errorSlice) - return bestSlice[0] + ss.sort(key = lambda x: x[1]) + return ss[0][2], ss[0][0], dataA - margin = 2 +def autoloop_anim(): + context = bpy.context + obj = context.active_object + fcurves = [x for x in obj.animation_data.action.fcurves if x.select] - s = lowerErrorSlice(diff, margin) + margin = 10 - print(flm, s) + flm, s, data = crossCorrelationMatch(fcurves, fcurves, margin) loop = data[s:s + flm + margin] #find *all* loops, s:s+flm, s+flm:s+2flm, etc... @@ -824,3 +835,18 @@ def anim_stitch(context, enduser_obj): pt.handle_left.y-=offset[i] pt.handle_right.y-=offset[i] + +def guess_anim_stitch(context, enduser_obj): + stitch_settings = enduser_obj.data.stitch_settings + action_1 = stitch_settings.first_action + action_2 = stitch_settings.second_action + TrackNamesA = enduser_obj.data.mocapNLATracks[action_1] + TrackNamesB = enduser_obj.data.mocapNLATracks[action_2] + mocapA = bpy.data.actions[TrackNamesA.base_track] + mocapB = bpy.data.actions[TrackNamesB.base_track] + curvesA = mocapA.fcurves + curvesB = mocapB.fcurves + flm, s, data = crossCorrelationMatch(curvesA, curvesB, 10) + print(flm,s) + enduser_obj.data.stitch_settings.blend_frame = flm + enduser_obj.data.stitch_settings.second_offset = s \ No newline at end of file diff --git a/release/scripts/modules/retarget.py b/release/scripts/modules/retarget.py index 0235bfc1474..827d3d11ddc 100644 --- a/release/scripts/modules/retarget.py +++ b/release/scripts/modules/retarget.py @@ -305,6 +305,7 @@ def copyTranslation(performer_obj, enduser_obj, perfFeet, root, s_frame, e_frame def IKRetarget(performer_obj, enduser_obj, s_frame, e_frame, scene): + bpy.ops.object.select_name(name=enduser_obj.name, extend=False) end_bones = enduser_obj.pose.bones for pose_bone in end_bones: ik_constraint = hasIKConstraint(pose_bone) @@ -313,9 +314,12 @@ def IKRetarget(performer_obj, enduser_obj, s_frame, e_frame, scene): # set constraint target to corresponding empty if targetless, # if not, keyframe current target to corresponding empty perf_bone = pose_bone.bone.reverseMap[-1].name + bpy.ops.object.mode_set(mode='EDIT') orgLocTrg = originalLocationTarget(pose_bone, enduser_obj) + bpy.ops.object.mode_set(mode='OBJECT') if not ik_constraint.target: - ik_constraint.target = orgLocTrg + ik_constraint.target = enduser_obj + ik_constraint.subtarget = pose_bone.name+"IK" target = orgLocTrg # There is a target now @@ -337,6 +341,7 @@ def IKRetarget(performer_obj, enduser_obj, s_frame, e_frame, scene): target.keyframe_insert("location") ik_constraint.mute = False scene.frame_set(s_frame) + bpy.ops.object.mode_set(mode='OBJECT') def turnOffIK(enduser_obj): @@ -379,14 +384,17 @@ def restoreObjMat(performer_obj, enduser_obj, perf_obj_mat, enduser_obj_mat, str #create (or return if exists) the related IK empty to the bone def originalLocationTarget(end_bone, enduser_obj): - if not end_bone.name + "Org" in bpy.data.objects: - bpy.ops.object.add() - empty = bpy.context.active_object - empty.name = end_bone.name + "Org" - empty.empty_draw_size = 0.1 - empty.parent = enduser_obj - empty = bpy.data.objects[end_bone.name + "Org"] - return empty + if not end_bone.name + "IK" in enduser_obj.data.bones: + newBone = enduser_obj.data.edit_bones.new(end_bone.name + "IK") + newBone.head = end_bone.tail + newBone.tail = end_bone.tail + Vector((0,0.1,0)) + #~ empty = bpy.context.active_object + #~ empty.name = end_bone.name + "Org" + #~ empty.empty_draw_size = 0.1 + #~ empty.parent = enduser_obj + else: + newBone = enduser_obj.pose.bones[end_bone.name + "IK"] + return newBone #create the specified NLA setup for base animation, constraints and tweak layer. @@ -530,6 +538,7 @@ def totalRetarget(performer_obj, enduser_obj, scene, s_frame, e_frame): stride_bone = copyTranslation(performer_obj, enduser_obj, feetBones, root, s_frame, e_frame, scene, enduser_obj_mat) if not advanced: IKRetarget(performer_obj, enduser_obj, s_frame, e_frame, scene) + bpy.ops.object.select_name(name=stride_bone.name, extend=False) restoreObjMat(performer_obj, enduser_obj, perf_obj_mat, enduser_obj_mat, stride_bone) bpy.ops.object.mode_set(mode='OBJECT') if not advanced: diff --git a/release/scripts/startup/ui_mocap.py b/release/scripts/startup/ui_mocap.py index dd5e6fa5d6d..19a96750e49 100644 --- a/release/scripts/startup/ui_mocap.py +++ b/release/scripts/startup/ui_mocap.py @@ -382,6 +382,7 @@ class ExtraToolsPanel(bpy.types.Panel): layout.prop(settings, "blend_amount") layout.prop(settings, "second_offset") layout.prop_search(settings, "stick_bone", context.active_object.pose, "bones") + layout.operator('mocap.animstitchguess', text="Guess Settings") layout.operator('mocap.animstitch', text="Stitch Animations") @@ -765,6 +766,25 @@ class OBJECT_OT_AnimationStitchingButton(bpy.types.Operator): return False +class OBJECT_OT_GuessAnimationStitchingButton(bpy.types.Operator): + '''Guesses the stitch frame and second offset for animation stitch''' + bl_idname = "mocap.animstitchguess" + bl_label = "Guesses the stitch frame and second offset for animation stitch" + + def execute(self, context): + mocap_tools.guess_anim_stitch(context, context.active_object) + return {"FINISHED"} + + @classmethod + def poll(cls, context): + activeIsArmature = False + if context.active_object: + activeIsArmature = isinstance(context.active_object.data, bpy.types.Armature) + if activeIsArmature: + stitch_settings = context.active_object.data.stitch_settings + return (stitch_settings.first_action and stitch_settings.second_action) + return False + def register(): bpy.utils.register_module(__name__) -- cgit v1.2.3 From 24b18fd154d17373525a12e0f9a58c78143262ee Mon Sep 17 00:00:00 2001 From: Benjy Cook Date: Fri, 12 Aug 2011 18:10:31 +0000 Subject: More work on Advanced Retargeting and some stride bone bugs --- release/scripts/modules/retarget.py | 62 +++++++++++++++++++++++++------------ release/scripts/startup/ui_mocap.py | 6 +++- 2 files changed, 47 insertions(+), 21 deletions(-) (limited to 'release/scripts') diff --git a/release/scripts/modules/retarget.py b/release/scripts/modules/retarget.py index 827d3d11ddc..d4deb159b22 100644 --- a/release/scripts/modules/retarget.py +++ b/release/scripts/modules/retarget.py @@ -277,20 +277,20 @@ def copyTranslation(performer_obj, enduser_obj, perfFeet, root, s_frame, e_frame if "stride_bone" in bpy.data.objects: stride_action = bpy.data.actions.new("Stride Bone " + action_name) stride_action.use_fake_user = True - stride_bone = enduser_obj.parent + #~ stride_bone = enduser_obj.parent stride_bone.animation_data.action = stride_action else: bpy.ops.object.add() stride_bone = bpy.context.active_object stride_bone.name = "stride_bone" print(stride_bone) - stride_bone.location = Vector((0, 0, 0)) + stride_bone.location = enduser_obj_mat.to_translation() print(linearAvg) if linearAvg: #determine the average change in scale needed avg = sum(linearAvg) / len(linearAvg) scene.frame_set(s_frame) - initialPos = (tailLoc(perf_bones[perfRoot]) / avg) + stride_bone.location + initialPos = (tailLoc(perf_bones[perfRoot]) / avg) #+ stride_bone.location for t in range(s_frame, e_frame): scene.frame_set(t) #calculate the new position, by dividing by the found ratio between performer and enduser @@ -298,6 +298,7 @@ def copyTranslation(performer_obj, enduser_obj, perfFeet, root, s_frame, e_frame stride_bone.location = enduser_obj_mat * (newTranslation - initialPos) stride_bone.keyframe_insert("location") else: + stride_bone.keyframe_insert("location") stride_bone.animation_data.action.name = ("Stride Bone " + action_name) @@ -371,7 +372,7 @@ def cleanAndStoreObjMat(performer_obj, enduser_obj): #restore the object matrixes after parenting the auto generated IK empties -def restoreObjMat(performer_obj, enduser_obj, perf_obj_mat, enduser_obj_mat, stride_bone): +def restoreObjMat(performer_obj, enduser_obj, perf_obj_mat, enduser_obj_mat, stride_bone, scene, s_frame): pose_bones = enduser_obj.pose.bones for pose_bone in pose_bones: if pose_bone.name + "Org" in bpy.data.objects: @@ -379,6 +380,8 @@ def restoreObjMat(performer_obj, enduser_obj, perf_obj_mat, enduser_obj_mat, str empty.parent = stride_bone performer_obj.matrix_world = perf_obj_mat enduser_obj.parent = stride_bone + scene.frame_set(s_frame) + enduser_obj_mat = enduser_obj_mat.to_3x3().to_4x4() * Matrix.Translation(stride_bone.matrix_world.to_translation()) enduser_obj.matrix_world = enduser_obj_mat @@ -455,20 +458,23 @@ def preAdvancedRetargeting(performer_obj, enduser_obj): createDictionary(performer_obj.data, enduser_obj.data) bones = enduser_obj.pose.bones map_bones = [bone for bone in bones if bone.bone.reverseMap] + perf_root = performer_obj.pose.bones[0].name for bone in map_bones: perf_bone = bone.bone.reverseMap[0].name addLocalRot = False; - if bone.bone.use_connect or not bone.constraints: + if (not bone.bone.use_connect) and (perf_bone!=perf_root): locks = bone.lock_location - if not (locks[0] or locks[1] or locks[2]): - cons = bone.constraints.new('COPY_LOCATION') - cons.name = "retargetTemp" - cons.use_x = not locks[0] - cons.use_y = not locks[1] - cons.use_z = not locks[2] - cons.target = performer_obj - cons.subtarget = perf_bone - addLocalRot = True + #if not (locks[0] or locks[1] or locks[2]): + cons = bone.constraints.new('COPY_LOCATION') + cons.name = "retargetTemp" + cons.use_x = not locks[0] + cons.use_y = not locks[1] + cons.use_z = not locks[2] + cons.target = performer_obj + cons.subtarget = perf_bone + cons.target_space = 'LOCAL' + cons.owner_space = 'LOCAL' + addLocalRot = True cons2 = bone.constraints.new('COPY_ROTATION') @@ -479,12 +485,17 @@ def preAdvancedRetargeting(performer_obj, enduser_obj): cons2.use_z = not locks[2] cons2.target = performer_obj cons2.subtarget = perf_bone + cons2.target_space = 'WORLD' + cons2.owner_space = 'WORLD' + + if perf_bone==perf_root: + addLocalRot = True - if addLocalRot: - for constraint in bone.constraints: - if constraint.type == 'COPY_ROTATION': - constraint.target_space = 'LOCAL' - constraint.owner_space = 'LOCAL_WITH_PARENT' + #~ if addLocalRot: + #~ for constraint in bone.constraints: + #~ if constraint.type == 'COPY_ROTATION': + #~ constraint.target_space = 'LOCAL' + #~ constraint.owner_space = 'LOCAL' def prepareForBake(enduser_obj): @@ -539,7 +550,7 @@ def totalRetarget(performer_obj, enduser_obj, scene, s_frame, e_frame): if not advanced: IKRetarget(performer_obj, enduser_obj, s_frame, e_frame, scene) bpy.ops.object.select_name(name=stride_bone.name, extend=False) - restoreObjMat(performer_obj, enduser_obj, perf_obj_mat, enduser_obj_mat, stride_bone) + restoreObjMat(performer_obj, enduser_obj, perf_obj_mat, enduser_obj_mat, stride_bone, scene, s_frame) bpy.ops.object.mode_set(mode='OBJECT') if not advanced: bpy.ops.object.select_name(name=inter_obj.name, extend=False) @@ -572,5 +583,16 @@ def profileWrapper(): e_frame = int(e_frame) totalRetarget(performer_obj, enduser_obj, scene, s_frame, e_frame) + +def isRigAdvanced(enduser_obj): + bones = enduser_obj.pose.bones + for bone in bones: + for constraint in bone.constraints: + if constraint.type != "IK": + return True + if enduser_obj.data.animation_data: + if enduser_obj.data.animation_data.drivers: + return True + if __name__ == "__main__": cProfile.run("profileWrapper()") diff --git a/release/scripts/startup/ui_mocap.py b/release/scripts/startup/ui_mocap.py index 19a96750e49..82f76d66d1b 100644 --- a/release/scripts/startup/ui_mocap.py +++ b/release/scripts/startup/ui_mocap.py @@ -404,7 +404,11 @@ class OBJECT_OT_RetargetButton(bpy.types.Operator): s_frame, e_frame = performer_obj.animation_data.action.frame_range s_frame = int(s_frame) e_frame = int(e_frame) - retarget.totalRetarget(performer_obj, enduser_obj, scene, s_frame, e_frame) + if retarget.isRigAdvanced(enduser_obj) and not enduser_obj.data.advancedRetarget: + print("Recommended to use Advanced Retargeting method") + enduser_obj.data.advancedRetarget = True + else: + retarget.totalRetarget(performer_obj, enduser_obj, scene, s_frame, e_frame) return {"FINISHED"} @classmethod -- cgit v1.2.3 From c5ef9b62c1f7f407c42bb48fe3362fa6cf3cf101 Mon Sep 17 00:00:00 2001 From: Mitchell Stokes Date: Fri, 12 Aug 2011 20:53:29 +0000 Subject: BGE Animations: Adding an option to let users choose whether or not to lock animation updates to the framerate. If this option is enabled, animations are only updated at the same speed as the animation framerate. This can give a significant speed up in performance, but at the cost of smoothness in animations. I'm defaulting this behavior to off for now, which is the behavior seen in trunk. --- release/scripts/startup/bl_ui/properties_game.py | 1 + 1 file changed, 1 insertion(+) (limited to 'release/scripts') diff --git a/release/scripts/startup/bl_ui/properties_game.py b/release/scripts/startup/bl_ui/properties_game.py index 58b2cacfbcc..dc397635452 100644 --- a/release/scripts/startup/bl_ui/properties_game.py +++ b/release/scripts/startup/bl_ui/properties_game.py @@ -342,6 +342,7 @@ class RENDER_PT_game_performance(RenderButtonsPanel, bpy.types.Panel): row = layout.row() row.prop(gs, "use_frame_rate") row.prop(gs, "use_display_lists") + row.prop(gs, "restrict_animation_updates") class RENDER_PT_game_display(RenderButtonsPanel, bpy.types.Panel): -- cgit v1.2.3 From aaeb498c26bf1241ab6f0b50976afdecd7636288 Mon Sep 17 00:00:00 2001 From: Benjy Cook Date: Sat, 13 Aug 2011 11:09:42 +0000 Subject: Added a small operator to ease mapping. When used, the hierarchy mapping field is filled with the currently selected (pose) bone. --- release/scripts/startup/ui_mocap.py | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) (limited to 'release/scripts') diff --git a/release/scripts/startup/ui_mocap.py b/release/scripts/startup/ui_mocap.py index 82f76d66d1b..0820b6183f4 100644 --- a/release/scripts/startup/ui_mocap.py +++ b/release/scripts/startup/ui_mocap.py @@ -283,6 +283,7 @@ class MocapPanel(bpy.types.Panel): row.prop(data=bone, property='foot', text='', icon='POSE_DATA') row.label(bone.name) row.prop_search(bone, "map", enduser_arm, "bones") + row.operator("mocap.selectmap", text='', icon='CURSOR').perf_bone = bone.name label_mod = "FK" if bone.map: pose_bone = perf_pose_bones[bone.map] @@ -493,6 +494,37 @@ class OBJECT_OT_LoadMappingButton(bpy.types.Operator): return activeIsArmature and isinstance(performer_obj[0].data, bpy.types.Armature) else: return False + + +class OBJECT_OT_SelectMapBoneButton(bpy.types.Operator): + '''Select a bone for faster mapping''' + bl_idname = "mocap.selectmap" + bl_label = "Select a bone for faster mapping" + perf_bone = bpy.props.StringProperty() + + 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] + selectedBone = "" + for bone in enduser_obj.data.bones: + boneVis = bone.layers + for i in range(32): + if boneVis[i] and enduser_obj.data.layers[i]: + if bone.select: + selectedBone = bone.name + break + performer_obj.data.bones[self.perf_bone].map = selectedBone + 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): -- cgit v1.2.3 From 2fef8f13f0189a8a748e6729081a4e5cb769314b Mon Sep 17 00:00:00 2001 From: Benjy Cook Date: Sat, 13 Aug 2011 18:46:34 +0000 Subject: Added argument to retargeting - step size. Allows retargeting every other 'step' frame, useful for previewing or faster retargeting. --- release/scripts/modules/retarget.py | 28 ++++++++++++++++------------ release/scripts/startup/ui_mocap.py | 5 +++++ 2 files changed, 21 insertions(+), 12 deletions(-) (limited to 'release/scripts') diff --git a/release/scripts/modules/retarget.py b/release/scripts/modules/retarget.py index d4deb159b22..de02913f5a7 100644 --- a/release/scripts/modules/retarget.py +++ b/release/scripts/modules/retarget.py @@ -69,7 +69,7 @@ def loadMapping(perf_arm, end_arm): # easily while concentrating on the hierarchy changes -def createIntermediate(performer_obj, enduser_obj, root, s_frame, e_frame, scene): +def createIntermediate(performer_obj, enduser_obj, root, s_frame, e_frame, scene, step): #creates and keyframes an empty with its location #the original position of the tail bone #useful for storing the important data in the original motion @@ -80,7 +80,10 @@ def createIntermediate(performer_obj, enduser_obj, root, s_frame, e_frame, scene perf_world_rotation = perf_bone.matrix * performer_obj.matrix_world inter_world_base_rotation = inter_bone.bone.matrix_local * inter_obj.matrix_world inter_world_base_inv = inter_world_base_rotation.inverted() - return (inter_world_base_inv.to_3x3() * perf_world_rotation.to_3x3()).to_4x4() + bake_matrix = (inter_world_base_inv.to_3x3() * perf_world_rotation.to_3x3()) + base_euler = inter_bone.rotation_euler + eul = bake_matrix.to_euler(base_euler.order,base_euler) + return eul.to_matrix().to_4x4() #uses 1to1 and interpolation/averaging to match many to 1 retarget def manyPerfToSingleInterRetarget(inter_bone, performer_bones_s): @@ -142,7 +145,7 @@ def createIntermediate(performer_obj, enduser_obj, root, s_frame, e_frame, scene else: inter_bone.bone.use_inherit_rotation = True - for t in range(s_frame, e_frame): + for t in range(s_frame, e_frame, step): if (t - s_frame) % 10 == 0: print("First pass: retargeting frame {0}/{1}".format(t, e_frame - s_frame)) scene.frame_set(t) @@ -161,7 +164,7 @@ def createIntermediate(performer_obj, enduser_obj, root, s_frame, e_frame, scene # Scale: ? Should work but needs testing. -def retargetEnduser(inter_obj, enduser_obj, root, s_frame, e_frame, scene): +def retargetEnduser(inter_obj, enduser_obj, root, s_frame, e_frame, scene, step): inter_bones = inter_obj.pose.bones end_bones = enduser_obj.pose.bones @@ -198,7 +201,7 @@ def retargetEnduser(inter_obj, enduser_obj, root, s_frame, e_frame, scene): for bone in end_bone.children: bakeTransform(bone) - for t in range(s_frame, e_frame): + for t in range(s_frame, e_frame, step): if (t - s_frame) % 10 == 0: print("Second pass: retargeting frame {0}/{1}".format(t, e_frame - s_frame)) scene.frame_set(t) @@ -277,7 +280,7 @@ def copyTranslation(performer_obj, enduser_obj, perfFeet, root, s_frame, e_frame if "stride_bone" in bpy.data.objects: stride_action = bpy.data.actions.new("Stride Bone " + action_name) stride_action.use_fake_user = True - #~ stride_bone = enduser_obj.parent + stride_bone = enduser_obj.parent stride_bone.animation_data.action = stride_action else: bpy.ops.object.add() @@ -305,7 +308,7 @@ def copyTranslation(performer_obj, enduser_obj, perfFeet, root, s_frame, e_frame return stride_bone -def IKRetarget(performer_obj, enduser_obj, s_frame, e_frame, scene): +def IKRetarget(performer_obj, enduser_obj, s_frame, e_frame, scene, step): bpy.ops.object.select_name(name=enduser_obj.name, extend=False) end_bones = enduser_obj.pose.bones for pose_bone in end_bones: @@ -332,7 +335,7 @@ def IKRetarget(performer_obj, enduser_obj, s_frame, e_frame, scene): target = ik_constraint.target # bake the correct locations for the ik target bones - for t in range(s_frame, e_frame): + for t in range(s_frame, e_frame, step): scene.frame_set(t) if target_is_bone: final_loc = pose_bone.tail - target.bone.matrix_local.to_translation() @@ -522,6 +525,7 @@ def totalRetarget(performer_obj, enduser_obj, scene, s_frame, e_frame): perf_arm = performer_obj.data end_arm = enduser_obj.data advanced = end_arm.advancedRetarget + step = end_arm.frameStep try: enduser_obj.animation_data.action = bpy.data.actions.new("temp") @@ -536,19 +540,19 @@ def totalRetarget(performer_obj, enduser_obj, scene, s_frame, e_frame): if not advanced: turnOffIK(enduser_obj) print("Creating intermediate armature (for first pass)") - inter_obj = createIntermediate(performer_obj, enduser_obj, root, s_frame, e_frame, scene) + inter_obj = createIntermediate(performer_obj, enduser_obj, root, s_frame, e_frame, scene, step) print("First pass: retargeting from intermediate to end user") - retargetEnduser(inter_obj, enduser_obj, root, s_frame, e_frame, scene) + retargetEnduser(inter_obj, enduser_obj, root, s_frame, e_frame, scene, step) else: prepareForBake(enduser_obj) print("Retargeting pose (Advanced Retarget)") - nla.bake(s_frame, e_frame, action=enduser_obj.animation_data.action, only_selected=True, do_pose=True, do_object=False) + nla.bake(s_frame, e_frame, action=enduser_obj.animation_data.action, only_selected=True, do_pose=True, do_object=False, step=step) name = performer_obj.animation_data.action.name enduser_obj.animation_data.action.name = "Base " + name print("Second pass: retargeting root translation and clean up") stride_bone = copyTranslation(performer_obj, enduser_obj, feetBones, root, s_frame, e_frame, scene, enduser_obj_mat) if not advanced: - IKRetarget(performer_obj, enduser_obj, s_frame, e_frame, scene) + IKRetarget(performer_obj, enduser_obj, s_frame, e_frame, scene, step) bpy.ops.object.select_name(name=stride_bone.name, extend=False) restoreObjMat(performer_obj, enduser_obj, perf_obj_mat, enduser_obj_mat, stride_bone, scene, s_frame) bpy.ops.object.mode_set(mode='OBJECT') diff --git a/release/scripts/startup/ui_mocap.py b/release/scripts/startup/ui_mocap.py index 0820b6183f4..9be26a3b2ff 100644 --- a/release/scripts/startup/ui_mocap.py +++ b/release/scripts/startup/ui_mocap.py @@ -161,6 +161,10 @@ bpy.types.Armature.stitch_settings = bpy.props.PointerProperty(type=AnimationSti bpy.types.Armature.active_mocap = bpy.props.StringProperty(update=retarget.NLASystemInitialize) bpy.types.Armature.mocapNLATracks = bpy.props.CollectionProperty(type=MocapNLATracks) bpy.types.Armature.advancedRetarget = bpy.props.BoolProperty(default=False, update=advancedRetargetToggle) +bpy.types.Armature.frameStep = smooth_out = bpy.props.IntProperty(name="Frame Skip", + default=1, + description="Amount of frames to skip - for previewing retargets quickly. 1 is fully sampled", + min=1) #Update function for IK functionality. Is called when IK prop checkboxes are toggled. @@ -301,6 +305,7 @@ class MocapPanel(bpy.types.Panel): mapRow.operator("mocap.loadmapping", text='Load mapping') self.layout.prop(data=performer_obj.animation_data.action, property='name', text='Action Name') self.layout.prop(enduser_arm, "advancedRetarget", text='Advanced Retarget') + self.layout.prop(enduser_arm, "frameStep") self.layout.operator("mocap.retarget", text='RETARGET!') -- cgit v1.2.3 From 3237f39243ab431fbdf736e3b8648a0c19400564 Mon Sep 17 00:00:00 2001 From: Benjy Cook Date: Mon, 15 Aug 2011 10:17:04 +0000 Subject: Small fix to autoloop due to changes in utility function by animation stitching --- release/scripts/modules/mocap_tools.py | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) (limited to 'release/scripts') diff --git a/release/scripts/modules/mocap_tools.py b/release/scripts/modules/mocap_tools.py index f4b6a93f531..e5d4dcb6554 100644 --- a/release/scripts/modules/mocap_tools.py +++ b/release/scripts/modules/mocap_tools.py @@ -169,20 +169,24 @@ def crossCorrelationMatch(curvesA, curvesB, margin): def autoloop_anim(): context = bpy.context obj = context.active_object - fcurves = [x for x in obj.animation_data.action.fcurves if x.select] + + def locCurve(x): + x.data_path == "location" + + fcurves = [x for x in obj.animation_data.action.fcurves if not locCurve(x)] margin = 10 flm, s, data = crossCorrelationMatch(fcurves, fcurves, margin) - loop = data[s:s + flm + margin] + loop = data[s:s + flm] #find *all* loops, s:s+flm, s+flm:s+2flm, etc... #and interpolate between all # to find "the perfect loop". #Maybe before finding s? interp(i,i+flm,i+2flm).... - for i in range(1, margin + 1): - w1 = sqrt(float(i) / margin) - loop[-i] = (loop[-i] * w1) + (loop[0] * (1 - w1)) + #~ for i in range(1, margin + 1): + #~ w1 = sqrt(float(i) / margin) + #~ loop[-i] = (loop[-i] * w1) + (loop[0] * (1 - w1)) for curve in fcurves: pts = curve.keyframe_points @@ -192,9 +196,9 @@ def autoloop_anim(): for c, curve in enumerate(fcurves): pts = curve.keyframe_points for i in range(len(loop)): - pts.insert(i + 1, loop[i][c]) + pts.insert(i + 2, loop[i][c]) - context.scene.frame_end = flm + 1 + context.scene.frame_end = flm def simplifyCurves(curveGroup, error, reparaError, maxIterations, group_mode): -- cgit v1.2.3 From c8ae881b619ec2d2d05b3c55283ca1f4c69828ca Mon Sep 17 00:00:00 2001 From: Benjy Cook Date: Mon, 15 Aug 2011 10:18:02 +0000 Subject: Added option to each retargeted bone to fix twist issues caused by variable bone rolls and unknown axis differences. Also made retarget operator a single undo --- release/scripts/modules/retarget.py | 35 +++++++++++++++++++++++++++-------- release/scripts/startup/ui_mocap.py | 5 +++++ 2 files changed, 32 insertions(+), 8 deletions(-) (limited to 'release/scripts') diff --git a/release/scripts/modules/retarget.py b/release/scripts/modules/retarget.py index de02913f5a7..2c4dcbe6bda 100644 --- a/release/scripts/modules/retarget.py +++ b/release/scripts/modules/retarget.py @@ -20,7 +20,7 @@ import bpy from mathutils import * -from math import radians, acos +from math import radians, acos, pi from bl_operators import nla import cProfile @@ -77,13 +77,21 @@ def createIntermediate(performer_obj, enduser_obj, root, s_frame, e_frame, scene #Simple 1to1 retarget of a bone def singleBoneRetarget(inter_bone, perf_bone): - perf_world_rotation = perf_bone.matrix * performer_obj.matrix_world - inter_world_base_rotation = inter_bone.bone.matrix_local * inter_obj.matrix_world + perf_world_rotation = perf_bone.matrix + inter_world_base_rotation = inter_bone.bone.matrix_local inter_world_base_inv = inter_world_base_rotation.inverted() bake_matrix = (inter_world_base_inv.to_3x3() * perf_world_rotation.to_3x3()) - base_euler = inter_bone.rotation_euler - eul = bake_matrix.to_euler(base_euler.order,base_euler) - return eul.to_matrix().to_4x4() + #~ orgEul = inter_bone.bone.matrix_local.to_euler("XYZ") + #~ eul = bake_matrix.to_euler("XYZ", orgEul) + #~ diff = -bake_matrix.to_euler().y + inter_bone.bone.matrix.to_euler().y + #~ eul.rotate_axis("Y", diff) + #~ eul.make_compatible(orgEul) + #~ bake_matrix = eul.to_matrix() + #~ #diff = abs(diff) + #bake_matrix = bake_matrix* Matrix.Rotation(pi/2, 3, "Y") + #~ scene = bpy.context.scene + #~ print(scene.frame_current, inter_bone.name, bake_matrix.to_euler().y) + return bake_matrix.to_4x4() #uses 1to1 and interpolation/averaging to match many to 1 retarget def manyPerfToSingleInterRetarget(inter_bone, performer_bones_s): @@ -109,7 +117,15 @@ def createIntermediate(performer_obj, enduser_obj, root, s_frame, e_frame, scene else: perf_bone = performer_bones[perf_bone_name[0].name] inter_bone.matrix_basis = singleBoneRetarget(inter_bone, perf_bone) - inter_bone.keyframe_insert("rotation_quaternion") + if inter_bone.bone.twistFix: + inter_bone.matrix_basis *= Matrix.Rotation(radians(180), 4, "Y") + rot_mode = inter_bone.rotation_mode + if rot_mode == "QUATERNION": + inter_bone.keyframe_insert("rotation_quaternion") + elif rot_mode == "AXIS_ANGLE": + inter_bone.keyframe_insert("rotation_axis_angle") + else: + inter_bone.keyframe_insert("rotation_euler") #creates the intermediate armature object inter_obj = enduser_obj.copy() @@ -119,6 +135,7 @@ def createIntermediate(performer_obj, enduser_obj, root, s_frame, e_frame, scene bpy.context.scene.objects.active = inter_obj bpy.ops.object.mode_set(mode='EDIT') #add some temporary connecting bones in case end user bones are not connected to their parents + rollDict = {} print("creating temp bones") for bone in inter_obj.data.edit_bones: if not bone.use_connect and bone.parent: @@ -130,9 +147,11 @@ def createIntermediate(performer_obj, enduser_obj, root, s_frame, e_frame, scene bone.parent = newBone bone.use_connect = True newBone.use_connect = True + rollDict[bone.name] = bone.roll + bone.roll = 0 #resets roll print("retargeting to intermediate") - bpy.ops.armature.calculate_roll(type='Z') + #bpy.ops.armature.calculate_roll(type='Z') bpy.ops.object.mode_set(mode="OBJECT") inter_obj.data.name = "inter_arm" inter_arm = inter_obj.data diff --git a/release/scripts/startup/ui_mocap.py b/release/scripts/startup/ui_mocap.py index 9be26a3b2ff..23354f9d722 100644 --- a/release/scripts/startup/ui_mocap.py +++ b/release/scripts/startup/ui_mocap.py @@ -223,6 +223,9 @@ bpy.types.Bone.reverseMap = bpy.props.CollectionProperty(type=MocapMapping) bpy.types.Bone.foot = bpy.props.BoolProperty(name="Foot", description="Marks this bone as a 'foot', which determines retargeted animation's translation", default=False) +bpy.types.Bone.twistFix = bpy.props.BoolProperty(name="Twist Fix", + description="Fix Twist on this bone", + default=False) bpy.types.PoseBone.IKRetarget = bpy.props.BoolProperty(name="IK", description="Toggles IK Retargeting method for given bone", update=toggleIKBone, default=False) @@ -295,6 +298,7 @@ class MocapPanel(bpy.types.Panel): label_mod = "ik chain" if hasIKConstraint(pose_bone): label_mod = "ik end" + row.prop(data=bone, property='twistFix', text='', icon='RNA') row.prop(pose_bone, 'IKRetarget') row.label(label_mod) else: @@ -396,6 +400,7 @@ class OBJECT_OT_RetargetButton(bpy.types.Operator): '''Retarget animation from selected armature to active armature ''' bl_idname = "mocap.retarget" bl_label = "Retargets active action from Performer to Enduser" + bl_options = {'REGISTER', 'UNDO'} def execute(self, context): scene = context.scene -- cgit v1.2.3 From cbbbf31315bf57aed5ccfec02f29495b2e3767ec Mon Sep 17 00:00:00 2001 From: Joshua Leung Date: Mon, 15 Aug 2011 13:24:53 +0000 Subject: Restoring "Clear User Transforms" operator This can now be found as Pose -> Clear Transforms -> Reset Unkeyed, or via the operator search (known by its old name there) --- release/scripts/startup/bl_ui/space_view3d.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'release/scripts') diff --git a/release/scripts/startup/bl_ui/space_view3d.py b/release/scripts/startup/bl_ui/space_view3d.py index c1973bc8555..c38bc0abc96 100644 --- a/release/scripts/startup/bl_ui/space_view3d.py +++ b/release/scripts/startup/bl_ui/space_view3d.py @@ -1262,11 +1262,15 @@ class VIEW3D_MT_pose_transform(bpy.types.Menu): layout.operator("pose.transforms_clear", text="All") + layout.separator() + layout.operator("pose.loc_clear", text="Location") layout.operator("pose.rot_clear", text="Rotation") layout.operator("pose.scale_clear", text="Scale") - layout.label(text="Origin") + layout.separator() + + layout.operator("pose.user_transforms_clear", text="Reset unkeyed") class VIEW3D_MT_pose_slide(bpy.types.Menu): -- cgit v1.2.3 From 87efb89901e36fbf72ed6f7ec0e6b6392946594f Mon Sep 17 00:00:00 2001 From: Joerg Mueller Date: Tue, 16 Aug 2011 14:43:04 +0000 Subject: Py fix for trunk to pepper merge. --- release/scripts/startup/bl_ui/properties_data_armature.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'release/scripts') diff --git a/release/scripts/startup/bl_ui/properties_data_armature.py b/release/scripts/startup/bl_ui/properties_data_armature.py index cddf9fef0f2..9a76ed81530 100644 --- a/release/scripts/startup/bl_ui/properties_data_armature.py +++ b/release/scripts/startup/bl_ui/properties_data_armature.py @@ -18,7 +18,7 @@ # import bpy -from bpy.types import Panel +from bpy.types import Panel, Menu from rna_prop_ui import PropertyPanel -- cgit v1.2.3 From 0b7911cf0a133b60921dea07567da2d1baf2c523 Mon Sep 17 00:00:00 2001 From: Benjy Cook Date: Tue, 16 Aug 2011 19:12:36 +0000 Subject: Small change that improves usability to advanced retargeting --- release/scripts/modules/retarget.py | 43 +++++++++++++------------------------ 1 file changed, 15 insertions(+), 28 deletions(-) (limited to 'release/scripts') diff --git a/release/scripts/modules/retarget.py b/release/scripts/modules/retarget.py index 2c4dcbe6bda..662dfc218ab 100644 --- a/release/scripts/modules/retarget.py +++ b/release/scripts/modules/retarget.py @@ -483,41 +483,28 @@ def preAdvancedRetargeting(performer_obj, enduser_obj): perf_root = performer_obj.pose.bones[0].name for bone in map_bones: perf_bone = bone.bone.reverseMap[0].name - addLocalRot = False; + + cons = bone.constraints.new('COPY_ROTATION') + cons.name = "retargetTemp" + locks = bone.lock_rotation + cons.use_x = not locks[0] + cons.use_y = not locks[1] + cons.use_z = not locks[2] + cons.target = performer_obj + cons.subtarget = perf_bone + cons.target_space = 'WORLD' + cons.owner_space = 'WORLD' + if (not bone.bone.use_connect) and (perf_bone!=perf_root): - locks = bone.lock_location - #if not (locks[0] or locks[1] or locks[2]): cons = bone.constraints.new('COPY_LOCATION') cons.name = "retargetTemp" - cons.use_x = not locks[0] - cons.use_y = not locks[1] - cons.use_z = not locks[2] cons.target = performer_obj cons.subtarget = perf_bone + cons.use_x = True + cons.use_y = True + cons.use_z = True cons.target_space = 'LOCAL' cons.owner_space = 'LOCAL' - addLocalRot = True - - - cons2 = bone.constraints.new('COPY_ROTATION') - cons2.name = "retargetTemp" - locks = bone.lock_rotation - cons2.use_x = not locks[0] - cons2.use_y = not locks[1] - cons2.use_z = not locks[2] - cons2.target = performer_obj - cons2.subtarget = perf_bone - cons2.target_space = 'WORLD' - cons2.owner_space = 'WORLD' - - if perf_bone==perf_root: - addLocalRot = True - - #~ if addLocalRot: - #~ for constraint in bone.constraints: - #~ if constraint.type == 'COPY_ROTATION': - #~ constraint.target_space = 'LOCAL' - #~ constraint.owner_space = 'LOCAL' def prepareForBake(enduser_obj): -- cgit v1.2.3 From 78b147fbc20cc5cbd30057474686a5fb91124fab Mon Sep 17 00:00:00 2001 From: Benjy Cook Date: Wed, 17 Aug 2011 10:13:24 +0000 Subject: Commenting and pep8 compliance --- release/scripts/modules/mocap_tools.py | 152 ++++++++++++++++++++++----------- release/scripts/modules/retarget.py | 87 +++++-------------- release/scripts/startup/ui_mocap.py | 86 ++++++++++--------- 3 files changed, 166 insertions(+), 159 deletions(-) (limited to 'release/scripts') diff --git a/release/scripts/modules/mocap_tools.py b/release/scripts/modules/mocap_tools.py index e5d4dcb6554..6c22f718296 100644 --- a/release/scripts/modules/mocap_tools.py +++ b/release/scripts/modules/mocap_tools.py @@ -24,7 +24,9 @@ import time from mathutils import Vector, Matrix -#Vector utility functions +# A Python implementation of n sized Vectors. +# Mathutils has a max size of 4, and we need at least 5 for Simplify Curves and even more for Cross Correlation. +# Vector utility functions class NdVector: vec = [] @@ -90,6 +92,7 @@ class NdVector: y = property(y) +#Sampled Data Point class for Simplify Curves class dataPoint: index = 0 # x,y1,y2,y3 coordinate of original point @@ -105,11 +108,19 @@ class dataPoint: self.u = u +#Cross Correlation Function +#http://en.wikipedia.org/wiki/Cross_correlation +#IN: curvesA, curvesB - bpy_collection/list of fcurves to analyze. Auto-Correlation is when they are the same. +# margin - When searching for the best "start" frame, how large a neighborhood of frames should we inspect (similar to epsilon in Calculus) +#OUT: startFrame, length of new anim, and curvesA def crossCorrelationMatch(curvesA, curvesB, margin): dataA = [] dataB = [] - end = len(curvesA[0].keyframe_points) + start, end = curvesA[0].range() + start = int(start) + end = int(end) + #transfer all fcurves data on each frame to a single NdVector. for i in range(1, end): vec = [] for fcurve in curvesA: @@ -120,9 +131,11 @@ def crossCorrelationMatch(curvesA, curvesB, margin): vec.append(fcurve.evaluate(i)) dataB.append(NdVector(vec)) + #Comparator for Cross Correlation. "Classic" implementation uses dot product, as do we. def comp(a, b): return a * b + #Create Rxy, which holds the Cross Correlation data. N = len(dataA) Rxy = [0.0] * N for i in range(N): @@ -131,7 +144,9 @@ def crossCorrelationMatch(curvesA, curvesB, margin): for j in range(i): Rxy[i] += comp(dataA[j], dataB[j - i + N]) Rxy[i] /= float(N) - def bestLocalMaximum(Rxy): + + #Find the Local maximums in the Cross Correlation data via numerical derivative. + def LocalMaximums(Rxy): Rxyd = [Rxy[i] - Rxy[i - 1] for i in range(1, len(Rxy))] maxs = [] for i in range(1, len(Rxyd) - 1): @@ -142,9 +157,12 @@ def crossCorrelationMatch(curvesA, curvesB, margin): maxs.append((i, max(Rxy[i], Rxy[i - 1]))) return [x[0] for x in maxs] #~ return max(maxs, key=lambda x: x[1])[0] - - flms = bestLocalMaximum(Rxy[0:int(len(Rxy))]) + + #flms - the possible offsets of the first part of the animation. In Auto-Corr, this is the length of the loop. + flms = LocalMaximums(Rxy[0:int(len(Rxy))]) ss = [] + + #for every local maximum, find the best one - i.e. also has the best start frame. for flm in flms: diff = [] @@ -159,20 +177,28 @@ def crossCorrelationMatch(curvesA, curvesB, margin): if errorSlice < bestSlice[1]: bestSlice = (i, errorSlice, flm) return bestSlice - + s = lowerErrorSlice(diff, margin) ss.append(s) - ss.sort(key = lambda x: x[1]) + #Find the best result and return it. + ss.sort(key=lambda x: x[1]) return ss[0][2], ss[0][0], dataA + +#Uses auto correlation (cross correlation of the same set of curves) and trims the active_object's fcurves +#Except for location curves (which in mocap tend to be not cyclic, e.g. a walk cycle forward) +#Transfers the fcurve data to a list of NdVector (length of list is number of fcurves), and calls the cross correlation function. +#Then trims the fcurve accordingly. +#IN: Nothing, set the object you want as active and call. Assumes object has animation_data.action! +#OUT: Trims the object's fcurves (except location curves). def autoloop_anim(): context = bpy.context obj = context.active_object - + def locCurve(x): x.data_path == "location" - + fcurves = [x for x in obj.animation_data.action.fcurves if not locCurve(x)] margin = 10 @@ -180,13 +206,10 @@ def autoloop_anim(): flm, s, data = crossCorrelationMatch(fcurves, fcurves, margin) loop = data[s:s + flm] - #find *all* loops, s:s+flm, s+flm:s+2flm, etc... - #and interpolate between all - # to find "the perfect loop". - #Maybe before finding s? interp(i,i+flm,i+2flm).... - #~ for i in range(1, margin + 1): - #~ w1 = sqrt(float(i) / margin) - #~ loop[-i] = (loop[-i] * w1) + (loop[0] * (1 - w1)) + #performs blending with a root falloff on the seam's neighborhood to ensure good tiling. + for i in range(1, margin + 1): + w1 = sqrt(float(i) / margin) + loop[-i] = (loop[-i] * w1) + (loop[0] * (1 - w1)) for curve in fcurves: pts = curve.keyframe_points @@ -201,8 +224,16 @@ def autoloop_anim(): context.scene.frame_end = flm +#simplifyCurves: performes the bulk of the samples to bezier conversion. +#IN: curveGroup - which can be a collection of singleFcurves, or grouped (via nested lists) . +# error - threshold of permittable error (max distance) of the new beziers to the original data +# reparaError - threshold of error where we should try to fix the parameterization rather than split the existing curve. > error, usually by a small constant factor for best performance. +# maxIterations - maximum number of iterations of reparameterizations we should attempt. (Newton-Rahpson is not guarenteed to converge, so this is needed). +# group_mode - boolean, indicating wether we should place bezier keyframes on the same x (frame), or optimize each individual curve. +#OUT: None. Deletes the existing curves and creates the new beziers. def simplifyCurves(curveGroup, error, reparaError, maxIterations, group_mode): + #Calculates the unit tangent of point v def unitTangent(v, data_pts): tang = NdVector((0, 0, 0, 0, 0)) if v != 0: @@ -214,7 +245,8 @@ def simplifyCurves(curveGroup, error, reparaError, maxIterations, group_mode): tang.normalize() return tang - #assign parametric u value for each point in original data + #assign parametric u value for each point in original data, via relative arc length + #http://en.wikipedia.org/wiki/Arc_length def chordLength(data_pts, s, e): totalLength = 0 for pt in data_pts[s:e + 1]: @@ -230,7 +262,7 @@ def simplifyCurves(curveGroup, error, reparaError, maxIterations, group_mode): print(s, e) pt.u = (pt.temp / totalLength) - # get binomial coefficient, this function/table is only called with args + # get binomial coefficient lookup table, this function/table is only called with args # (3,0),(3,1),(3,2),(3,3),(2,0),(2,1),(2,2)! binomDict = {(3, 0): 1, (3, 1): 3, @@ -239,8 +271,8 @@ def simplifyCurves(curveGroup, error, reparaError, maxIterations, group_mode): (2, 0): 1, (2, 1): 2, (2, 2): 1} - #value at pt t of a single bernstein Polynomial + #value at pt t of a single bernstein Polynomial def bernsteinPoly(n, i, t): binomCoeff = binomDict[(n, i)] return binomCoeff * pow(t, i) * pow(1 - t, n - i) @@ -380,6 +412,7 @@ def simplifyCurves(curveGroup, error, reparaError, maxIterations, group_mode): fud = 1 pt.u = pt.u - (fu / fud) + #Create data_pts, a list of dataPoint type, each is assigned index i, and an NdVector def createDataPts(curveGroup, group_mode): data_pts = [] if group_mode: @@ -403,6 +436,7 @@ def simplifyCurves(curveGroup, error, reparaError, maxIterations, group_mode): data_pts.append(dataPoint(i, NdVector((x, y1, y2, y3, y4)))) return data_pts + #Recursively fit cubic beziers to the data_pts between s and e def fitCubic(data_pts, s, e): # if there are less than 3 points, fit a single basic bezier if e - s < 3: @@ -437,6 +471,7 @@ def simplifyCurves(curveGroup, error, reparaError, maxIterations, group_mode): beziers.append(bez) return + # deletes the sampled points and creates beziers. def createNewCurves(curveGroup, beziers, group_mode): #remove all existing data points if group_mode: @@ -483,15 +518,14 @@ def simplifyCurves(curveGroup, error, reparaError, maxIterations, group_mode): #remove old Fcurves and insert the new ones createNewCurves(curveGroup, beziers, group_mode) -#Main function of simplification -#sel_opt: either "sel" or "all" for which curves to effect -#error: maximum error allowed, in fraction (20% = 0.0020), -#i.e. divide by 10000 from percentage wanted. -#group_mode: boolean, to analyze each curve seperately or in groups, -#where group is all curves that effect the same property -#(e.g. a bone's x,y,z rotation) - +#Main function of simplification, which called by Operator +#IN: +# sel_opt- either "sel" (selected) or "all" for which curves to effect +# error- maximum error allowed, in fraction (20% = 0.0020, which is the default), +# i.e. divide by 10000 from percentage wanted. +# group_mode- boolean, to analyze each curve seperately or in groups, +# where a group is all curves that effect the same property/RNA path def fcurves_simplify(context, obj, sel_opt="all", error=0.002, group_mode=True): # main vars fcurves = obj.animation_data.action.fcurves @@ -533,11 +567,12 @@ def fcurves_simplify(context, obj, sel_opt="all", error=0.002, group_mode=True): return + # Implementation of non-linear median filter, with variable kernel size -# Double pass - one marks spikes, the other smooths one +# Double pass - one marks spikes, the other smooths them # Expects sampled keyframes on everyframe - - +# IN: None. Performs the operations on the active_object's fcurves. Expects animation_data.action to exist! +# OUT: None. Fixes the fcurves "in-place". def denoise_median(): context = bpy.context obj = context.active_object @@ -568,6 +603,9 @@ def denoise_median(): return +# Recieves armature, and rotations all bones by 90 degrees along the X axis +# This fixes the common axis issue BVH files have when importing. +# IN: Armature (bpy.types.Armature) def rotate_fix_armature(arm_data): global_matrix = Matrix.Rotation(radians(90), 4, "X") bpy.ops.object.mode_set(mode='EDIT', toggle=False) @@ -588,6 +626,8 @@ def rotate_fix_armature(arm_data): bpy.ops.object.mode_set(mode='OBJECT', toggle=False) +#Roughly scales the performer armature to match the enduser armature +#IN: perfromer_obj, enduser_obj, Blender objects whose .data is an armature. def scale_fix_armature(performer_obj, enduser_obj): perf_bones = performer_obj.data.bones end_bones = enduser_obj.data.bones @@ -611,6 +651,8 @@ def scale_fix_armature(performer_obj, enduser_obj): performer_obj.scale *= factor +#Guess Mapping +#Given a performer and enduser armature, attempts to guess the hiearchy mapping def guessMapping(performer_obj, enduser_obj): perf_bones = performer_obj.data.bones end_bones = enduser_obj.data.bones @@ -642,11 +684,16 @@ def guessMapping(performer_obj, enduser_obj): def guessSingleMapping(perf_bone): possible_bones = [end_bones[0]] + while possible_bones: for end_bone in possible_bones: match = nameMatch(perf_bone.name, end_bone.name) if match == 2 and not perf_bone.map: perf_bone.map = end_bone.name + #~ elif match == 1 and not perf_bone.map: + #~ oppo = perf_bones[oppositeBone(perf_bone)].map + # if oppo: + # perf_bone = oppo newPossibleBones = [] for end_bone in possible_bones: newPossibleBones += list(end_bone.children) @@ -658,6 +705,9 @@ def guessMapping(performer_obj, enduser_obj): guessSingleMapping(root) +# Creates limit rotation constraints on the enduser armature based on range of motion (max min of fcurves) of the performer. +# IN: context (bpy.context, etc.), and 2 blender objects which are armatures +# OUT: creates the limit constraints. def limit_dof(context, performer_obj, enduser_obj): limitDict = {} perf_bones = [bone for bone in performer_obj.pose.bones if bone.bone.map] @@ -705,18 +755,10 @@ def limit_dof(context, performer_obj, enduser_obj): newCons.use_limit_x = True newCons.use_limit_y = True newCons.use_limit_z = True - #~ else: - #~ bone.ik_min_x, bone.ik_min_y, bone.ik_min_z, bone.ik_max_x, bone.ik_max_y, bone.ik_max_z = limitDict[bone.name] - #~ bone.use_ik_limit_x = True - #~ bone.use_ik_limit_y = True - #~ bone.use_ik_limit_z= True - #~ bone.ik_stiffness_x = 1/((limitDict[bone.name][3] - limitDict[bone.name][0])/(2*pi))) - #~ bone.ik_stiffness_y = 1/((limitDict[bone.name][4] - limitDict[bone.name][1])/(2*pi))) - #~ bone.ik_stiffness_z = 1/((limitDict[bone.name][5] - limitDict[bone.name][2])/(2*pi))) - context.scene.frame_set(c_frame) +# Removes the constraints that were added by limit_dof on the enduser_obj def limit_dof_toggle_off(context, enduser_obj): for bone in enduser_obj.pose.bones: existingConstraint = [constraint for constraint in bone.constraints if constraint.name == "DOF Limitation"] @@ -724,6 +766,8 @@ def limit_dof_toggle_off(context, enduser_obj): bone.constraints.remove(existingConstraint[0]) +# Reparameterizes a blender path via keyframing it's eval_time to match a stride_object's forward velocity. +# IN: Context, stride object (blender object with location keyframes), path object. def path_editing(context, stride_obj, path): y_fcurve = [fcurve for fcurve in stride_obj.animation_data.action.fcurves if fcurve.data_path == "location"][1] s, e = context.scene.frame_start, context.scene.frame_end # y_fcurve.range() @@ -771,11 +815,14 @@ def path_editing(context, stride_obj, path): print("finished path editing") +#Animation Stitching +#Stitches two retargeted animations together via NLA settings. +#IN: enduser_obj, a blender armature that has had two retargets applied. def anim_stitch(context, enduser_obj): stitch_settings = enduser_obj.data.stitch_settings action_1 = stitch_settings.first_action action_2 = stitch_settings.second_action - if stitch_settings.stick_bone!="": + if stitch_settings.stick_bone != "": selected_bone = enduser_obj.pose.bones[stitch_settings.stick_bone] else: selected_bone = enduser_obj.pose.bones[0] @@ -791,8 +838,8 @@ def anim_stitch(context, enduser_obj): mocapStrip = mocapTrack.strips.new(TrackNamesB.base_track, stitch_settings.blend_frame, mocapAction) mocapStrip.extrapolation = "HOLD_FORWARD" mocapStrip.blend_in = stitch_settings.blend_amount - mocapStrip.action_frame_start+=stitch_settings.second_offset - mocapStrip.action_frame_end+=stitch_settings.second_offset + mocapStrip.action_frame_start += stitch_settings.second_offset + mocapStrip.action_frame_end += stitch_settings.second_offset constraintTrack = anim_data.nla_tracks.new() constraintTrack.name = TrackNamesB.auto_fix_track constraintAction = bpy.data.actions[TrackNamesB.auto_fix_track] @@ -821,8 +868,8 @@ def anim_stitch(context, enduser_obj): actionBTrack = stride_anim_data.nla_tracks.new() actionBTrack.name = TrackNamesB.stride_action actionBStrip = actionBTrack.strips.new(TrackNamesB.stride_action, stitch_settings.blend_frame, bpy.data.actions[TrackNamesB.stride_action]) - actionBStrip.action_frame_start+=stitch_settings.second_offset - actionBStrip.action_frame_end+=stitch_settings.second_offset + actionBStrip.action_frame_start += stitch_settings.second_offset + actionBStrip.action_frame_end += stitch_settings.second_offset actionBStrip.blend_in = stitch_settings.blend_amount actionBStrip.extrapolation = "NOTHING" #we need to change the stride_bone's action to add the offset @@ -831,15 +878,16 @@ def anim_stitch(context, enduser_obj): scene.frame_set(stitch_settings.blend_frame) actual_pos = (selected_bone.matrix.to_translation() * enduser_obj.matrix_world) offset = actual_pos - desired_pos - - for i,fcurve in enumerate([fcurve for fcurve in bpy.data.actions[TrackNamesB.stride_action].fcurves if fcurve.data_path=="location"]): - print(offset[i],i,fcurve.array_index) + + for i, fcurve in enumerate([fcurve for fcurve in bpy.data.actions[TrackNamesB.stride_action].fcurves if fcurve.data_path == "location"]): + print(offset[i], i, fcurve.array_index) for pt in fcurve.keyframe_points: - pt.co.y-=offset[i] - pt.handle_left.y-=offset[i] - pt.handle_right.y-=offset[i] + pt.co.y -= offset[i] + pt.handle_left.y -= offset[i] + pt.handle_right.y -= offset[i] +#Guesses setting for animation stitching via Cross Correlation def guess_anim_stitch(context, enduser_obj): stitch_settings = enduser_obj.data.stitch_settings action_1 = stitch_settings.first_action @@ -851,6 +899,6 @@ def guess_anim_stitch(context, enduser_obj): curvesA = mocapA.fcurves curvesB = mocapB.fcurves flm, s, data = crossCorrelationMatch(curvesA, curvesB, 10) - print(flm,s) + print("Guessed the following for start and offset: ", s, flm) enduser_obj.data.stitch_settings.blend_frame = flm - enduser_obj.data.stitch_settings.second_offset = s \ No newline at end of file + enduser_obj.data.stitch_settings.second_offset = s diff --git a/release/scripts/modules/retarget.py b/release/scripts/modules/retarget.py index 662dfc218ab..67e8c7da55d 100644 --- a/release/scripts/modules/retarget.py +++ b/release/scripts/modules/retarget.py @@ -22,7 +22,6 @@ import bpy from mathutils import * from math import radians, acos, pi from bl_operators import nla -import cProfile def hasIKConstraint(pose_bone): @@ -53,8 +52,8 @@ def createDictionary(perf_arm, end_arm): feetBones = [bone.name for bone in perf_arm.bones if bone.foot] return feetBones, root -def loadMapping(perf_arm, end_arm): +def loadMapping(perf_arm, end_arm): for end_bone in end_arm.bones: #find its match and add perf_bone to the match's mapping if end_bone.reverseMap: @@ -80,17 +79,7 @@ def createIntermediate(performer_obj, enduser_obj, root, s_frame, e_frame, scene perf_world_rotation = perf_bone.matrix inter_world_base_rotation = inter_bone.bone.matrix_local inter_world_base_inv = inter_world_base_rotation.inverted() - bake_matrix = (inter_world_base_inv.to_3x3() * perf_world_rotation.to_3x3()) - #~ orgEul = inter_bone.bone.matrix_local.to_euler("XYZ") - #~ eul = bake_matrix.to_euler("XYZ", orgEul) - #~ diff = -bake_matrix.to_euler().y + inter_bone.bone.matrix.to_euler().y - #~ eul.rotate_axis("Y", diff) - #~ eul.make_compatible(orgEul) - #~ bake_matrix = eul.to_matrix() - #~ #diff = abs(diff) - #bake_matrix = bake_matrix* Matrix.Rotation(pi/2, 3, "Y") - #~ scene = bpy.context.scene - #~ print(scene.frame_current, inter_bone.name, bake_matrix.to_euler().y) + bake_matrix = (inter_world_base_inv.to_3x3() * perf_world_rotation.to_3x3()) return bake_matrix.to_4x4() #uses 1to1 and interpolation/averaging to match many to 1 retarget @@ -117,6 +106,7 @@ def createIntermediate(performer_obj, enduser_obj, root, s_frame, e_frame, scene else: perf_bone = performer_bones[perf_bone_name[0].name] inter_bone.matrix_basis = singleBoneRetarget(inter_bone, perf_bone) + #Some bones have incorrect roll on the source armature, and need to be marked for fixing if inter_bone.bone.twistFix: inter_bone.matrix_basis *= Matrix.Rotation(radians(180), 4, "Y") rot_mode = inter_bone.rotation_mode @@ -151,7 +141,6 @@ def createIntermediate(performer_obj, enduser_obj, root, s_frame, e_frame, scene bone.roll = 0 #resets roll print("retargeting to intermediate") - #bpy.ops.armature.calculate_roll(type='Z') bpy.ops.object.mode_set(mode="OBJECT") inter_obj.data.name = "inter_arm" inter_arm = inter_obj.data @@ -187,6 +176,7 @@ def retargetEnduser(inter_obj, enduser_obj, root, s_frame, e_frame, scene, step) inter_bones = inter_obj.pose.bones end_bones = enduser_obj.pose.bones + #Basic "visual baking" function, for transfering rotations from intermediate to end user def bakeTransform(end_bone): src_bone = inter_bones[end_bone.name] trg_bone = end_bone @@ -265,29 +255,21 @@ def copyTranslation(performer_obj, enduser_obj, perfFeet, root, s_frame, e_frame # now we take our locDict and analyze it. # we need to derive all chains - + def locDeriv(key, t): graph = locDict[key] return graph[t + 1] - graph[t] - #~ locDeriv = {} - #~ for key in locDictKeys: - #~ locDeriv[key] = [] - - #~ for key in locDict.keys(): - #~ graph = locDict[key] - #~ locDeriv[key] = [graph[t + 1] - graph[t] for t in range(len(graph) - 1)] - # now find the plant frames, where perfFeet don't move much linearAvg = [] for key in perfFeet: for i in range(len(locDict[key]) - 1): - v = locDeriv(key,i) + v = locDeriv(key, i) if (v.length < 0.1): - hipV = locDeriv(perfRoot,i) - endV = locDeriv(perf_bones[key].bone.map,i) + hipV = locDeriv(perfRoot, i) + endV = locDeriv(perf_bones[key].bone.map, i) #this is a plant frame. #lets see what the original hip delta is, and the corresponding #end bone's delta @@ -312,7 +294,7 @@ def copyTranslation(performer_obj, enduser_obj, perfFeet, root, s_frame, e_frame #determine the average change in scale needed avg = sum(linearAvg) / len(linearAvg) scene.frame_set(s_frame) - initialPos = (tailLoc(perf_bones[perfRoot]) / avg) #+ stride_bone.location + initialPos = (tailLoc(perf_bones[perfRoot]) / avg) for t in range(s_frame, e_frame): scene.frame_set(t) #calculate the new position, by dividing by the found ratio between performer and enduser @@ -320,7 +302,6 @@ def copyTranslation(performer_obj, enduser_obj, perfFeet, root, s_frame, e_frame stride_bone.location = enduser_obj_mat * (newTranslation - initialPos) stride_bone.keyframe_insert("location") else: - stride_bone.keyframe_insert("location") stride_bone.animation_data.action.name = ("Stride Bone " + action_name) @@ -342,7 +323,7 @@ def IKRetarget(performer_obj, enduser_obj, s_frame, e_frame, scene, step): bpy.ops.object.mode_set(mode='OBJECT') if not ik_constraint.target: ik_constraint.target = enduser_obj - ik_constraint.subtarget = pose_bone.name+"IK" + ik_constraint.subtarget = pose_bone.name + "IK" target = orgLocTrg # There is a target now @@ -370,14 +351,6 @@ def IKRetarget(performer_obj, enduser_obj, s_frame, e_frame, scene, step): def turnOffIK(enduser_obj): end_bones = enduser_obj.pose.bones for pose_bone in end_bones: - if pose_bone.is_in_ik_chain: - pass - # TODO: - # set stiffness according to place on chain - # and values from analysis that is stored in the bone - #pose_bone.ik_stiffness_x = 0.5 - #pose_bone.ik_stiffness_y = 0.5 - #pose_bone.ik_stiffness_z = 0.5 ik_constraint = hasIKConstraint(pose_bone) if ik_constraint: ik_constraint.mute = True @@ -412,18 +385,14 @@ def originalLocationTarget(end_bone, enduser_obj): if not end_bone.name + "IK" in enduser_obj.data.bones: newBone = enduser_obj.data.edit_bones.new(end_bone.name + "IK") newBone.head = end_bone.tail - newBone.tail = end_bone.tail + Vector((0,0.1,0)) - #~ empty = bpy.context.active_object - #~ empty.name = end_bone.name + "Org" - #~ empty.empty_draw_size = 0.1 - #~ empty.parent = enduser_obj + newBone.tail = end_bone.tail + Vector((0, 0.1, 0)) else: newBone = enduser_obj.pose.bones[end_bone.name + "IK"] return newBone #create the specified NLA setup for base animation, constraints and tweak layer. -def NLASystemInitialize(enduser_arm, context):#enduser_obj, name): +def NLASystemInitialize(enduser_arm, context): enduser_obj = context.active_object NLATracks = enduser_arm.mocapNLATracks[enduser_obj.data.active_mocap] name = NLATracks.name @@ -461,9 +430,8 @@ def NLASystemInitialize(enduser_arm, context):#enduser_obj, name): userAction.use_fake_user = True userStrip = userTrack.strips.new("Manual fixes " + name, s_frame, userAction) userStrip.extrapolation = "HOLD" - #userStrip.blend_type = "MULITPLY" - doesn't work due to work, will be activated soon + userStrip.blend_type = "ADD" anim_data.nla_tracks.active = constraintTrack - #anim_data.action = constraintAction anim_data.action_extrapolation = "NOTHING" #set the stride_bone's action if "stride_bone" in bpy.data.objects: @@ -494,8 +462,8 @@ def preAdvancedRetargeting(performer_obj, enduser_obj): cons.subtarget = perf_bone cons.target_space = 'WORLD' cons.owner_space = 'WORLD' - - if (not bone.bone.use_connect) and (perf_bone!=perf_root): + + if (not bone.bone.use_connect) and (perf_bone != perf_root): cons = bone.constraints.new('COPY_LOCATION') cons.name = "retargetTemp" cons.target = performer_obj @@ -517,6 +485,7 @@ def prepareForBake(enduser_obj): if "retargetTemp" in cons.name: bone.bone.select = True + def cleanTempConstraints(enduser_obj): bones = enduser_obj.pose.bones map_bones = [bone for bone in bones if bone.bone.reverseMap] @@ -525,6 +494,7 @@ def cleanTempConstraints(enduser_obj): if "retargetTemp" in cons.name: bone.constraints.remove(cons) + #Main function that runs the retargeting sequence. #If advanced == True, we assume constraint's were already created def totalRetarget(performer_obj, enduser_obj, scene, s_frame, e_frame): @@ -532,13 +502,13 @@ def totalRetarget(performer_obj, enduser_obj, scene, s_frame, e_frame): end_arm = enduser_obj.data advanced = end_arm.advancedRetarget step = end_arm.frameStep - + try: enduser_obj.animation_data.action = bpy.data.actions.new("temp") enduser_obj.animation_data.action.use_fake_user = True except: print("no need to create new action") - + print("creating Dictionary") feetBones, root = createDictionary(perf_arm, end_arm) print("cleaning stuff up") @@ -576,22 +546,6 @@ def totalRetarget(performer_obj, enduser_obj, scene, s_frame, e_frame): NLATracks = end_arm.mocapNLATracks[name] end_arm.active_mocap = name print("retargeting done!") - -def profileWrapper(): - context = bpy.context - scene = context.scene - s_frame = scene.frame_start - e_frame = scene.frame_end - 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] - s_frame, e_frame = performer_obj.animation_data.action.frame_range - s_frame = int(s_frame) - e_frame = int(e_frame) - totalRetarget(performer_obj, enduser_obj, scene, s_frame, e_frame) def isRigAdvanced(enduser_obj): @@ -603,6 +557,3 @@ def isRigAdvanced(enduser_obj): if enduser_obj.data.animation_data: if enduser_obj.data.animation_data.drivers: return True - -if __name__ == "__main__": - cProfile.run("profileWrapper()") diff --git a/release/scripts/startup/ui_mocap.py b/release/scripts/startup/ui_mocap.py index 23354f9d722..3cb33776b0b 100644 --- a/release/scripts/startup/ui_mocap.py +++ b/release/scripts/startup/ui_mocap.py @@ -112,6 +112,7 @@ bpy.utils.register_class(MocapConstraint) bpy.types.Armature.mocap_constraints = bpy.props.CollectionProperty(type=MocapConstraint) +# Animation Stitch Settings, used for animation stitching of 2 retargeted animations. class AnimationStitchSettings(bpy.types.PropertyGroup): first_action = bpy.props.StringProperty(name="Action 1", description="First action in stitch") @@ -132,6 +133,7 @@ class AnimationStitchSettings(bpy.types.PropertyGroup): bpy.utils.register_class(AnimationStitchSettings) +# MocapNLA Tracks. Stores which tracks/actions are associated with each retargeted animation. class MocapNLATracks(bpy.types.PropertyGroup): name = bpy.props.StringProperty() base_track = bpy.props.StringProperty() @@ -141,7 +143,8 @@ class MocapNLATracks(bpy.types.PropertyGroup): bpy.utils.register_class(MocapNLATracks) - + +#Update function for Advanced Retarget boolean variable. def advancedRetargetToggle(self, context): enduser_obj = context.active_object performer_obj = [obj for obj in context.selected_objects if obj != enduser_obj] @@ -156,20 +159,23 @@ def advancedRetargetToggle(self, context): retarget.cleanTempConstraints(enduser_obj) - +#Animation Stitch Settings Property bpy.types.Armature.stitch_settings = bpy.props.PointerProperty(type=AnimationStitchSettings) -bpy.types.Armature.active_mocap = bpy.props.StringProperty(update=retarget.NLASystemInitialize) +#Current/Active retargeted animation on the armature +bpy.types.Armature.active_mocap = bpy.props.StringProperty(update=retarget.NLASystemInitialize) +#Collection of retargeted animations and their NLA Tracks on the armature bpy.types.Armature.mocapNLATracks = bpy.props.CollectionProperty(type=MocapNLATracks) +#Advanced retargeting boolean property bpy.types.Armature.advancedRetarget = bpy.props.BoolProperty(default=False, update=advancedRetargetToggle) +#frame step - frequency of frames to retarget. Skipping is useful for previewing, faster work etc. bpy.types.Armature.frameStep = smooth_out = bpy.props.IntProperty(name="Frame Skip", default=1, description="Amount of frames to skip - for previewing retargets quickly. 1 is fully sampled", min=1) -#Update function for IK functionality. Is called when IK prop checkboxes are toggled. - def toggleIKBone(self, context): + #Update function for IK functionality. Is called when IK prop checkboxes are toggled. if self.IKRetarget: if not self.is_in_ik_chain: print(self.name + " IK toggled ON!") @@ -211,21 +217,29 @@ def toggleIKBone(self, context): for bone in cnstrn_bone.parent_recursive: if not bone.is_in_ik_chain: bone.IKRetarget = False - + +#MocapMap class for storing mapping on enduser performer, +# where a bone may be linked to more than one on the performer class MocapMapping(bpy.types.PropertyGroup): name = bpy.props.StringProperty() bpy.utils.register_class(MocapMapping) +#string property for storing performer->enduser mapping bpy.types.Bone.map = bpy.props.StringProperty() +#Collection Property for storing enduser->performer mapping bpy.types.Bone.reverseMap = bpy.props.CollectionProperty(type=MocapMapping) +#Boolean property for storing foot bone toggle bpy.types.Bone.foot = bpy.props.BoolProperty(name="Foot", description="Marks this bone as a 'foot', which determines retargeted animation's translation", default=False) +#Boolean property for storing if this bone is twisted along the y axis, +# which can happen due to various sources of performers bpy.types.Bone.twistFix = bpy.props.BoolProperty(name="Twist Fix", description="Fix Twist on this bone", default=False) +#Boolean property for toggling ik retargeting for this bone bpy.types.PoseBone.IKRetarget = bpy.props.BoolProperty(name="IK", description="Toggles IK Retargeting method for given bone", update=toggleIKBone, default=False) @@ -384,7 +398,7 @@ class ExtraToolsPanel(bpy.types.Panel): if activeIsArmature: enduser_arm = context.active_object.data layout.label("Retargeted Animations:") - layout.prop_search(enduser_arm, "active_mocap",enduser_arm, "mocapNLATracks") + layout.prop_search(enduser_arm, "active_mocap", enduser_arm, "mocapNLATracks") settings = enduser_arm.stitch_settings layout.prop_search(settings, "first_action", enduser_arm, "mocapNLATracks") layout.prop_search(settings, "second_action", enduser_arm, "mocapNLATracks") @@ -397,6 +411,8 @@ class ExtraToolsPanel(bpy.types.Panel): class OBJECT_OT_RetargetButton(bpy.types.Operator): + #Retargeting operator. Assumes selected and active armatures, where the performer (the selected one) + # has an action for retargeting '''Retarget animation from selected armature to active armature ''' bl_idname = "mocap.retarget" bl_label = "Retargets active action from Performer to Enduser" @@ -428,41 +444,13 @@ class OBJECT_OT_RetargetButton(bpy.types.Operator): 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) + return activeIsArmature and isinstance(performer_obj[0].data, bpy.types.Armature) and performer_obj[0].animation_data else: return False - - - #~ class OBJECT_OT_AdvancedRetargetButton(bpy.types.Operator): - #~ '''Prepare for advanced retargeting ''' - #~ bl_idname = "mocap.preretarget" - #~ bl_label = "Prepares retarget of active action from Performer to Enduser" - - #~ def execute(self, context): - #~ scene = context.scene - #~ s_frame = scene.frame_start - #~ e_frame = scene.frame_end - #~ 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] - #~ retarget.preAdvancedRetargeting(performer_obj, enduser_obj) - #~ 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): + #Operator for saving mapping to enduser armature '''Save mapping to active armature (for future retargets) ''' bl_idname = "mocap.savemapping" bl_label = "Saves user generated mapping from Performer to Enduser" @@ -486,6 +474,7 @@ class OBJECT_OT_SaveMappingButton(bpy.types.Operator): class OBJECT_OT_LoadMappingButton(bpy.types.Operator): '''Load saved mapping from active armature''' + #Operator for loading mapping to enduser armature bl_idname = "mocap.loadmapping" bl_label = "Loads user generated mapping from Performer to Enduser" @@ -504,9 +493,10 @@ class OBJECT_OT_LoadMappingButton(bpy.types.Operator): return activeIsArmature and isinstance(performer_obj[0].data, bpy.types.Armature) else: return False - + class OBJECT_OT_SelectMapBoneButton(bpy.types.Operator): + #Operator for setting selected bone in enduser armature to the performer mapping '''Select a bone for faster mapping''' bl_idname = "mocap.selectmap" bl_label = "Select a bone for faster mapping" @@ -538,6 +528,7 @@ class OBJECT_OT_SelectMapBoneButton(bpy.types.Operator): class OBJECT_OT_ConvertSamplesButton(bpy.types.Operator): + #Operator to convert samples to beziers on the selected object '''Convert active armature's sampled keyframed to beziers''' bl_idname = "mocap.samples" bl_label = "Converts samples / simplifies keyframes to beziers" @@ -552,6 +543,7 @@ class OBJECT_OT_ConvertSamplesButton(bpy.types.Operator): class OBJECT_OT_LooperButton(bpy.types.Operator): + #Operator to trim fcurves which contain a few loops to a single one on the selected object '''Trim active armature's animation to a single cycle, given a cyclic animation (such as a walk cycle)''' bl_idname = "mocap.looper" bl_label = "loops animation / sampled mocap data" @@ -566,6 +558,7 @@ class OBJECT_OT_LooperButton(bpy.types.Operator): class OBJECT_OT_DenoiseButton(bpy.types.Operator): + #Operator to denoise impluse noise on the active object's fcurves '''Denoise active armature's animation. Good for dealing with 'bad' frames inherent in mocap animation''' bl_idname = "mocap.denoise" bl_label = "Denoises sampled mocap data " @@ -584,6 +577,7 @@ class OBJECT_OT_DenoiseButton(bpy.types.Operator): class OBJECT_OT_LimitDOFButton(bpy.types.Operator): + #Operator to analyze performer armature and apply rotation constraints on the enduser armature '''Create limit constraints on the active armature from the selected armature's animation's range of motion''' bl_idname = "mocap.limitdof" bl_label = "Analyzes animations Max/Min DOF and adds hard/soft constraints" @@ -605,6 +599,7 @@ class OBJECT_OT_LimitDOFButton(bpy.types.Operator): class OBJECT_OT_RemoveLimitDOFButton(bpy.types.Operator): + #Removes constraints created by above operator '''Removes previously created limit constraints on the active armature''' bl_idname = "mocap.removelimitdof" bl_label = "Removes previously created limit constraints on the active armature" @@ -622,6 +617,7 @@ class OBJECT_OT_RemoveLimitDOFButton(bpy.types.Operator): class OBJECT_OT_RotateFixArmature(bpy.types.Operator): + #Operator to fix common imported Mocap data issue of wrong axis system on active object '''Realign the active armature's axis system to match Blender (Commonly needed after bvh import)''' bl_idname = "mocap.rotate_fix" bl_label = "Rotates selected armature 90 degrees (fix for bvh import)" @@ -637,6 +633,7 @@ class OBJECT_OT_RotateFixArmature(bpy.types.Operator): class OBJECT_OT_ScaleFixArmature(bpy.types.Operator): + #Operator to scale down the selected armature to match the active one '''Rescale selected armature to match the active animation, for convienence''' bl_idname = "mocap.scale_fix" bl_label = "Scales performer armature to match target armature" @@ -659,6 +656,7 @@ class OBJECT_OT_ScaleFixArmature(bpy.types.Operator): class MOCAP_OT_AddMocapFix(bpy.types.Operator): + #Operator to add a post-retarget fix '''Add a post-retarget fix - useful for fixing certain artifacts following the retarget''' bl_idname = "mocap.addmocapfix" bl_label = "Add Mocap Fix to target armature" @@ -683,6 +681,7 @@ class MOCAP_OT_AddMocapFix(bpy.types.Operator): class OBJECT_OT_RemoveMocapConstraint(bpy.types.Operator): + #Operator to remove a post-retarget fix '''Remove this post-retarget fix''' bl_idname = "mocap.removeconstraint" bl_label = "Removes fixes from target armature" @@ -707,6 +706,7 @@ class OBJECT_OT_RemoveMocapConstraint(bpy.types.Operator): class OBJECT_OT_BakeMocapConstraints(bpy.types.Operator): + #Operator to bake all post-retarget fixes '''Bake all post-retarget fixes to the Retarget Fixes NLA Track''' bl_idname = "mocap.bakeconstraints" bl_label = "Bake all fixes to target armature" @@ -722,6 +722,7 @@ class OBJECT_OT_BakeMocapConstraints(bpy.types.Operator): class OBJECT_OT_UnbakeMocapConstraints(bpy.types.Operator): + #Operator to unbake all post-retarget fixes '''Unbake all post-retarget fixes - removes the baked data from the Retarget Fixes NLA Track''' bl_idname = "mocap.unbakeconstraints" bl_label = "Unbake all fixes to target armature" @@ -737,6 +738,8 @@ class OBJECT_OT_UnbakeMocapConstraints(bpy.types.Operator): class OBJECT_OT_UpdateMocapConstraints(bpy.types.Operator): + #Operator to update all post-retarget fixes, similar to update dependencies on drivers + #Needed because python properties lack certain callbacks and some fixes take a while to recalculate. '''Updates all post-retarget fixes - needed after changes to armature object or pose''' bl_idname = "mocap.updateconstraints" bl_label = "Updates all fixes to target armature - neccesary to take under consideration changes to armature object or pose" @@ -752,6 +755,7 @@ class OBJECT_OT_UpdateMocapConstraints(bpy.types.Operator): class OBJECT_OT_GuessHierachyMapping(bpy.types.Operator): + #Operator which calls heurisitic function to guess mapping between 2 armatures '''Attemps to auto figure out hierarchy mapping''' bl_idname = "mocap.guessmapping" bl_label = "Attemps to auto figure out hierarchy mapping" @@ -774,6 +778,7 @@ class OBJECT_OT_GuessHierachyMapping(bpy.types.Operator): class OBJECT_OT_PathEditing(bpy.types.Operator): + #Operator which calls path editing function, making active object follow the selected curve. '''Sets active object (stride object) to follow the selected curve''' bl_idname = "mocap.pathediting" bl_label = "Sets active object (stride object) to follow the selected curve" @@ -793,6 +798,7 @@ class OBJECT_OT_PathEditing(bpy.types.Operator): class OBJECT_OT_AnimationStitchingButton(bpy.types.Operator): + #Operator which calls stitching function, combining 2 animations onto the NLA. '''Stitches two defined animations into a single one via alignment of NLA Tracks''' bl_idname = "mocap.animstitch" bl_label = "Stitches two defined animations into a single one via alignment of NLA Tracks" @@ -810,9 +816,10 @@ class OBJECT_OT_AnimationStitchingButton(bpy.types.Operator): stitch_settings = context.active_object.data.stitch_settings return (stitch_settings.first_action and stitch_settings.second_action) return False - + class OBJECT_OT_GuessAnimationStitchingButton(bpy.types.Operator): + #Operator which calls stitching function heuristic, setting good values for above operator. '''Guesses the stitch frame and second offset for animation stitch''' bl_idname = "mocap.animstitchguess" bl_label = "Guesses the stitch frame and second offset for animation stitch" @@ -831,6 +838,7 @@ class OBJECT_OT_GuessAnimationStitchingButton(bpy.types.Operator): return (stitch_settings.first_action and stitch_settings.second_action) return False + def register(): bpy.utils.register_module(__name__) -- cgit v1.2.3 From 06ae5e48258dacc5598b23286d46891be32a08e5 Mon Sep 17 00:00:00 2001 From: Joshua Leung Date: Mon, 22 Aug 2011 02:14:39 +0000 Subject: Reshuffling DopeSheet filter icons so that they appear more obviously related to each other --- release/scripts/startup/bl_ui/space_dopesheet.py | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) (limited to 'release/scripts') diff --git a/release/scripts/startup/bl_ui/space_dopesheet.py b/release/scripts/startup/bl_ui/space_dopesheet.py index cab58a3aadb..74ae427c5cf 100644 --- a/release/scripts/startup/bl_ui/space_dopesheet.py +++ b/release/scripts/startup/bl_ui/space_dopesheet.py @@ -35,7 +35,7 @@ def dopesheet_filter(layout, context, genericFiltersOnly=False): row.prop(dopesheet, "show_hidden", text="") if is_nla: - row.prop(dopesheet, "show_missing_nla", text="") + row.prop(dopesheet, "show_missing_nla", text="") if not genericFiltersOnly: if bpy.data.groups: @@ -50,21 +50,16 @@ def dopesheet_filter(layout, context, genericFiltersOnly=False): if dopesheet.show_only_matching_fcurves: row.prop(dopesheet, "filter_fcurve_name", text="") - row = layout.row() - row.prop(dopesheet, "show_datablock_filters", text="Filters", icon='DISCLOSURE_TRI_RIGHT') + row = layout.row(align=True) + row.prop(dopesheet, "show_datablock_filters", text="Filters") if (not genericFiltersOnly) and (dopesheet.show_datablock_filters): - # TODO: put a box around these? - subrow = row.row() - - row = subrow.row(align=True) - row.prop(dopesheet, "show_transforms", text="") - - row = subrow.row(align=True) row.prop(dopesheet, "show_scenes", text="") row.prop(dopesheet, "show_worlds", text="") row.prop(dopesheet, "show_nodes", text="") + row.prop(dopesheet, "show_transforms", text="") + if bpy.data.meshes: row.prop(dopesheet, "show_meshes", text="") if bpy.data.shape_keys: -- cgit v1.2.3 From a594196dc0cf0482aab7d16b4cfee1a7d3b8707d Mon Sep 17 00:00:00 2001 From: Joshua Leung Date: Mon, 22 Aug 2011 02:30:43 +0000 Subject: Bugfix: "Filters" toggle was being shown in Action Editor too, where it was irrelevant --- release/scripts/startup/bl_ui/space_dopesheet.py | 69 ++++++++++++------------ 1 file changed, 35 insertions(+), 34 deletions(-) (limited to 'release/scripts') diff --git a/release/scripts/startup/bl_ui/space_dopesheet.py b/release/scripts/startup/bl_ui/space_dopesheet.py index 74ae427c5cf..a2a51f9587f 100644 --- a/release/scripts/startup/bl_ui/space_dopesheet.py +++ b/release/scripts/startup/bl_ui/space_dopesheet.py @@ -50,40 +50,41 @@ def dopesheet_filter(layout, context, genericFiltersOnly=False): if dopesheet.show_only_matching_fcurves: row.prop(dopesheet, "filter_fcurve_name", text="") - row = layout.row(align=True) - row.prop(dopesheet, "show_datablock_filters", text="Filters") - - if (not genericFiltersOnly) and (dopesheet.show_datablock_filters): - row.prop(dopesheet, "show_scenes", text="") - row.prop(dopesheet, "show_worlds", text="") - row.prop(dopesheet, "show_nodes", text="") - - row.prop(dopesheet, "show_transforms", text="") - - if bpy.data.meshes: - row.prop(dopesheet, "show_meshes", text="") - if bpy.data.shape_keys: - row.prop(dopesheet, "show_shapekeys", text="") - if bpy.data.materials: - row.prop(dopesheet, "show_materials", text="") - if bpy.data.lamps: - row.prop(dopesheet, "show_lamps", text="") - if bpy.data.textures: - row.prop(dopesheet, "show_textures", text="") - if bpy.data.cameras: - row.prop(dopesheet, "show_cameras", text="") - if bpy.data.curves: - row.prop(dopesheet, "show_curves", text="") - if bpy.data.metaballs: - row.prop(dopesheet, "show_metaballs", text="") - if bpy.data.lattices: - row.prop(dopesheet, "show_lattices", text="") - if bpy.data.armatures: - row.prop(dopesheet, "show_armatures", text="") - if bpy.data.particles: - row.prop(dopesheet, "show_particles", text="") - if bpy.data.speakers: - row.prop(dopesheet, "show_speakers", text="") + if not genericFiltersOnly: + row = layout.row(align=True) + row.prop(dopesheet, "show_datablock_filters", text="Filters") + + if dopesheet.show_datablock_filters: + row.prop(dopesheet, "show_scenes", text="") + row.prop(dopesheet, "show_worlds", text="") + row.prop(dopesheet, "show_nodes", text="") + + row.prop(dopesheet, "show_transforms", text="") + + if bpy.data.meshes: + row.prop(dopesheet, "show_meshes", text="") + if bpy.data.shape_keys: + row.prop(dopesheet, "show_shapekeys", text="") + if bpy.data.materials: + row.prop(dopesheet, "show_materials", text="") + if bpy.data.lamps: + row.prop(dopesheet, "show_lamps", text="") + if bpy.data.textures: + row.prop(dopesheet, "show_textures", text="") + if bpy.data.cameras: + row.prop(dopesheet, "show_cameras", text="") + if bpy.data.curves: + row.prop(dopesheet, "show_curves", text="") + if bpy.data.metaballs: + row.prop(dopesheet, "show_metaballs", text="") + if bpy.data.lattices: + row.prop(dopesheet, "show_lattices", text="") + if bpy.data.armatures: + row.prop(dopesheet, "show_armatures", text="") + if bpy.data.particles: + row.prop(dopesheet, "show_particles", text="") + if bpy.data.speakers: + row.prop(dopesheet, "show_speakers", text="") ####################################### -- cgit v1.2.3 From a71c215f228173070d41faef1321db25b40d723e Mon Sep 17 00:00:00 2001 From: Joerg Mueller Date: Mon, 22 Aug 2011 18:59:56 +0000 Subject: 3D Audio GSoC: Final GSoC commit. * Bugfix: Negative frames crashed * Bugfix: JOS sample buffer size prediction error (wasted memory) * Optimisation: for JOS upsampling (around 12 % difference measured here) * Optimisation: Better filter for JOS resampling * Bugfix: Error in relative 3D audio code. * Removed Attenuation * Bugfix: Multiple scenes in BGE lead to errors, BGE audio now all relative, to support multiple scenes. --- release/scripts/startup/bl_ui/space_sequencer.py | 1 - 1 file changed, 1 deletion(-) (limited to 'release/scripts') diff --git a/release/scripts/startup/bl_ui/space_sequencer.py b/release/scripts/startup/bl_ui/space_sequencer.py index f796ce8da5f..cebe78ff627 100644 --- a/release/scripts/startup/bl_ui/space_sequencer.py +++ b/release/scripts/startup/bl_ui/space_sequencer.py @@ -653,7 +653,6 @@ class SEQUENCER_PT_sound(SequencerButtonsPanel, Panel): layout.prop(strip, "waveform") layout.prop(strip, "volume") - layout.prop(strip, "attenuation") layout.prop(strip, "pitch") layout.prop(strip, "pan") -- cgit v1.2.3 From b4b046995b21d59e315eb71ed08fc1ae066c891b Mon Sep 17 00:00:00 2001 From: Joerg Mueller Date: Sun, 28 Aug 2011 14:21:44 +0000 Subject: * Removing mocap GSoC (is an addon already). * Fixing ffmpeg-0.8 errors. * Fixing Ketsji paths. * Removing DoSound from BGE. * Fixing audio scene update to use only current scene objects. --- release/scripts/modules/mocap_constraints.py | 434 ------------- release/scripts/modules/mocap_tools.py | 904 --------------------------- release/scripts/modules/retarget.py | 559 ----------------- release/scripts/startup/ui_mocap.py | 850 ------------------------- 4 files changed, 2747 deletions(-) delete mode 100644 release/scripts/modules/mocap_constraints.py delete mode 100644 release/scripts/modules/mocap_tools.py delete mode 100644 release/scripts/modules/retarget.py delete mode 100644 release/scripts/startup/ui_mocap.py (limited to 'release/scripts') diff --git a/release/scripts/modules/mocap_constraints.py b/release/scripts/modules/mocap_constraints.py deleted file mode 100644 index 540e8fa06db..00000000000 --- a/release/scripts/modules/mocap_constraints.py +++ /dev/null @@ -1,434 +0,0 @@ -# ##### 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 ##### - -# - -import bpy -from mathutils import * -from bl_operators import nla -from retarget import hasIKConstraint - -### Utility Functions - - -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 - - -def consObjToBone(cons_obj): - #Utility function - returns related bone from ik object - if cons_obj.name[-3:] == "Org": - return cons_obj.name[:-3] - else: - return cons_obj.name - -### And and Remove Constraints (called from operators) - - -def addNewConstraint(m_constraint, cons_obj): - #Decide the correct Blender constraint according to the Mocap constraint type - 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 = "LIMIT_LOCATION" - #create and store the new constraint within m_constraint - real_constraint = cons_obj.constraints.new(c_type) - real_constraint.name = "Auto fixes " + str(len(cons_obj.constraints)) - m_constraint.real_constraint_bone = consObjToBone(cons_obj) - m_constraint.real_constraint = real_constraint.name - #set the rest of the constraint properties - setConstraint(m_constraint, bpy.context) - - -def removeConstraint(m_constraint, cons_obj): - #remove the influence fcurve and Blender constraint - oldConstraint = cons_obj.constraints[m_constraint.real_constraint] - removeFcurves(cons_obj, bpy.context.active_object, oldConstraint, m_constraint) - cons_obj.constraints.remove(oldConstraint) - -### Update functions. There are 3: UpdateType/Bone -### update framing (deals with changes in the desired frame range) -### And setConstraint which deals with the rest - - -def updateConstraintBoneType(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 - if m_constraint.constrained_bone: - bone = bones[m_constraint.constrained_bone] - cons_obj = getConsObj(bone) - addNewConstraint(m_constraint, cons_obj) - - -def setConstraintFraming(m_constraint, context): - obj = 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] - #remove the old keyframes - removeFcurves(cons_obj, obj, real_constraint, m_constraint) - #set the new ones according to the m_constraint properties - s, e = m_constraint.s_frame, m_constraint.e_frame - s_in, s_out = m_constraint.smooth_in, m_constraint.smooth_out - 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 - s_in) - real_constraint.keyframe_insert(data_path="influence", frame=e + s_out) - - -def removeFcurves(cons_obj, obj, real_constraint, m_constraint): - #Determine if the constrained object is a bone or an empty - if isinstance(cons_obj, bpy.types.PoseBone): - fcurves = obj.animation_data.action.fcurves - else: - fcurves = cons_obj.animation_data.action.fcurves - #Find the RNA data path of the constraint's influence - RNA_paths = [] - RNA_paths.append(real_constraint.path_from_id("influence")) - if m_constraint.type == "floor" or m_constraint.type == "point": - RNA_paths += [real_constraint.path_from_id("max_x"), real_constraint.path_from_id("min_x")] - RNA_paths += [real_constraint.path_from_id("max_y"), real_constraint.path_from_id("min_y")] - RNA_paths += [real_constraint.path_from_id("max_z"), real_constraint.path_from_id("min_z")] - #Retrieve the correct fcurve via the RNA data path and remove it - fcurves_del = [fcurve for fcurve in fcurves if fcurve.data_path in RNA_paths] - #clear the fcurve and set the frames. - if fcurves_del: - for fcurve in fcurves_del: - fcurves.remove(fcurve) - #remove armature fcurves (if user keyframed m_constraint properties) - if obj.data.animation_data and m_constraint.type == "point": - if obj.data.animation_data.action: - path = m_constraint.path_from_id("targetPoint") - m_fcurves = [fcurve for fcurve in obj.data.animation_data.action.fcurves if fcurve.data_path == path] - for curve in m_fcurves: - obj.data.animation_data.action.fcurves.remove(curve) - -#Utility function for copying property fcurves over - - -def copyFCurve(newCurve, oldCurve): - for point in oldCurve.keyframe_points: - newCurve.keyframe_points.insert(frame=point.co.x, value=point.co.y) - -#Creates new fcurves for the constraint properties (for floor and point) - - -def createConstraintFCurves(cons_obj, obj, real_constraint): - if isinstance(cons_obj, bpy.types.PoseBone): - c_fcurves = obj.animation_data.action.fcurves - else: - c_fcurves = cons_obj.animation_data.action.fcurves - c_x_path = [real_constraint.path_from_id("max_x"), real_constraint.path_from_id("min_x")] - c_y_path = [real_constraint.path_from_id("max_y"), real_constraint.path_from_id("min_y")] - c_z_path = [real_constraint.path_from_id("max_z"), real_constraint.path_from_id("min_z")] - c_constraints_path = c_x_path + c_y_path + c_z_path - existing_curves = [fcurve for fcurve in c_fcurves if fcurve.data_path in c_constraints_path] - if existing_curves: - for curve in existing_curves: - c_fcurves.remove(curve) - xCurves, yCurves, zCurves = [], [], [] - for path in c_constraints_path: - newCurve = c_fcurves.new(path) - if path in c_x_path: - xCurves.append(newCurve) - elif path in c_y_path: - yCurves.append(newCurve) - else: - zCurves.append(newCurve) - return xCurves, yCurves, zCurves - - -# 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: - return - obj = 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] - NLATracks = obj.data.mocapNLATracks[obj.data.active_mocap] - obj.animation_data.action = bpy.data.actions[NLATracks.auto_fix_track] - - #frame changing section - setConstraintFraming(m_constraint, context) - s, e = m_constraint.s_frame, m_constraint.e_frame - s_in, s_out = m_constraint.smooth_in, m_constraint.smooth_out - s -= s_in - e += s_out - #Set the blender constraint parameters - if m_constraint.type == "point": - constraint_settings = False # are fix settings keyframed? - if not m_constraint.targetSpace == "constrained_boneB": - real_constraint.owner_space = m_constraint.targetSpace - else: - real_constraint.owner_space = "LOCAL" - if obj.data.animation_data: - if obj.data.animation_data.action: - path = m_constraint.path_from_id("targetPoint") - m_fcurves = [fcurve for fcurve in obj.data.animation_data.action.fcurves if fcurve.data_path == path] - if m_fcurves: - constraint_settings = True - xCurves, yCurves, zCurves = createConstraintFCurves(cons_obj, obj, real_constraint) - for curve in xCurves: - copyFCurve(curve, m_fcurves[0]) - for curve in yCurves: - copyFCurve(curve, m_fcurves[1]) - for curve in zCurves: - copyFCurve(curve, m_fcurves[2]) - if m_constraint.targetSpace == "constrained_boneB" and m_constraint.constrained_boneB: - c_frame = context.scene.frame_current - bakedPos = {} - src_bone = bones[m_constraint.constrained_boneB] - if not constraint_settings: - xCurves, yCurves, zCurves = createConstraintFCurves(cons_obj, obj, real_constraint) - print("please wait a moment, calculating fix") - for t in range(s, e): - context.scene.frame_set(t) - src_bone_pos = src_bone.matrix.to_translation() - bakedPos[t] = src_bone_pos + m_constraint.targetPoint # final position for constrained bone in object space - context.scene.frame_set(c_frame) - for frame in bakedPos.keys(): - pos = bakedPos[frame] - for xCurve in xCurves: - xCurve.keyframe_points.insert(frame=frame, value=pos.x) - for yCurve in yCurves: - yCurve.keyframe_points.insert(frame=frame, value=pos.y) - for zCurve in zCurves: - zCurve.keyframe_points.insert(frame=frame, value=pos.z) - - if not constraint_settings: - 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 - - if m_constraint.type == "freeze": - real_constraint.owner_space = m_constraint.targetSpace - bpy.context.scene.frame_set(m_constraint.s_frame) - if isinstance(cons_obj, bpy.types.PoseBone): - x, y, z = cons_obj.bone.center + (cons_obj.bone.vector / 2) + obj.matrix_world.to_translation() - else: - x, y, z = cons_obj.matrix_world.to_translation() - - 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 - - if m_constraint.type == "distance" and m_constraint.constrained_boneB: - real_constraint.owner_space = "WORLD" - real_constraint.target = getConsObj(bones[m_constraint.constrained_boneB]) - real_constraint.limit_mode = "LIMITDIST_ONSURFACE" - real_constraint.distance = m_constraint.targetDist - - if m_constraint.type == "floor" and m_constraint.targetMesh: - real_constraint.mute = True - real_constraint.owner_space = "WORLD" - #calculate the positions thoughout the range - s, e = m_constraint.s_frame, m_constraint.e_frame - s_in, s_out = m_constraint.smooth_in, m_constraint.smooth_out - s -= s_in - e += s_out - bakedPos = {} - floor = bpy.data.objects[m_constraint.targetMesh] - c_frame = context.scene.frame_current - print("please wait a moment, calculating fix") - for t in range(s, e): - context.scene.frame_set(t) - axis = Vector((0, 0, 100)) * obj.matrix_world.to_3x3() - offset = Vector((0, 0, m_constraint.targetDist)) * obj.matrix_world.to_3x3() - ray_origin = cons_obj.matrix_world.to_translation() - offset # world position of constrained bone - ray_target = ray_origin + axis - #convert ray points to floor's object space - ray_origin *= floor.matrix_world.inverted() - ray_target *= floor.matrix_world.inverted() - hit, nor, ind = floor.ray_cast(ray_origin, ray_target) - if hit != Vector((0, 0, 0)): - bakedPos[t] = (hit * floor.matrix_world) - bakedPos[t] += Vector((0, 0, m_constraint.targetDist)) - else: - bakedPos[t] = cons_obj.matrix_world.to_translation() - context.scene.frame_set(c_frame) - #create keyframes for real constraint - xCurves, yCurves, zCurves = createConstraintFCurves(cons_obj, obj, real_constraint) - for frame in bakedPos.keys(): - pos = bakedPos[frame] - for xCurve in xCurves: - xCurve.keyframe_points.insert(frame=frame, value=pos.x) - for yCurve in yCurves: - yCurve.keyframe_points.insert(frame=frame, value=pos.y) - for zCurve in zCurves: - zCurve.keyframe_points.insert(frame=frame, value=pos.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 - - # active/baked check - real_constraint.mute = (not m_constraint.active) - - -def locBake(s_frame, e_frame, bones): - scene = bpy.context.scene - bakeDict = {} - for bone in bones: - bakeDict[bone.name] = {} - for t in range(s_frame, e_frame): - scene.frame_set(t) - for bone in bones: - bakeDict[bone.name][t] = bone.matrix.copy() - for t in range(s_frame, e_frame): - for bone in bones: - print(bone.bone.matrix_local.to_translation()) - bone.matrix = bakeDict[bone.name][t] - bone.keyframe_insert("location", frame=t) - - -# Baking function which bakes all bones effected by the constraint -def bakeAllConstraints(obj, s_frame, e_frame, bones): - for bone in bones: - bone.bone.select = False - selectedBones = [] # Marks bones that need a full bake - simpleBake = [] # Marks bones that need only a location bake - for end_bone in bones: - if end_bone.name in [m_constraint.real_constraint_bone for m_constraint in obj.data.mocap_constraints]: - #For all bones that have a constraint: - ik = hasIKConstraint(end_bone) - cons_obj = getConsObj(end_bone) - if ik: - #If it's an auto generated IK: - if ik.chain_count == 0: - selectedBones += bones # Chain len 0, bake everything - else: - selectedBones += [end_bone] + end_bone.parent_recursive[:ik.chain_count - 1] # Bake the chain - else: - #It's either an FK bone which we should just bake - #OR a user created IK target bone - simpleBake += [end_bone] - for bone in selectedBones: - bone.bone.select = True - NLATracks = obj.data.mocapNLATracks[obj.data.active_mocap] - obj.animation_data.action = bpy.data.actions[NLATracks.auto_fix_track] - constraintTrack = obj.animation_data.nla_tracks[NLATracks.auto_fix_track] - constraintStrip = constraintTrack.strips[0] - constraintStrip.action_frame_start = s_frame - constraintStrip.action_frame_end = e_frame - constraintStrip.frame_start = s_frame - constraintStrip.frame_end = e_frame - if selectedBones: - #Use bake function from NLA Bake Action operator - nla.bake(s_frame, e_frame, action=constraintStrip.action, only_selected=True, do_pose=True, do_object=False) - if simpleBake: - #Do a "simple" bake, location only, world space only. - locBake(s_frame, e_frame, simpleBake) - - -#Calls the baking function and decativates releveant constraints -def bakeConstraints(context): - obj = context.active_object - bones = obj.pose.bones - s_frame, e_frame = context.scene.frame_start, context.scene.frame_end - #Bake relevant bones - bakeAllConstraints(obj, s_frame, e_frame, bones) - for m_constraint in obj.data.mocap_constraints: - end_bone = bones[m_constraint.real_constraint_bone] - cons_obj = getConsObj(end_bone) - # It's a control empty: turn the ik off - if not isinstance(cons_obj, bpy.types.PoseBone): - ik_con = hasIKConstraint(end_bone) - if ik_con: - ik_con.mute = True - # Deactivate related Blender Constraint - m_constraint.active = False - - -#Deletes the baked fcurves and reactivates relevant constraints -def unbakeConstraints(context): - # to unbake constraints we delete the whole strip - obj = context.active_object - bones = obj.pose.bones - scene = bpy.context.scene - NLATracks = obj.data.mocapNLATracks[obj.data.active_mocap] - obj.animation_data.action = bpy.data.actions[NLATracks.auto_fix_track] - constraintTrack = obj.animation_data.nla_tracks[NLATracks.auto_fix_track] - constraintStrip = constraintTrack.strips[0] - action = constraintStrip.action - # delete the fcurves on the strip - for fcurve in action.fcurves: - action.fcurves.remove(fcurve) - # reactivate relevant constraints - for m_constraint in obj.data.mocap_constraints: - end_bone = bones[m_constraint.real_constraint_bone] - cons_obj = getConsObj(end_bone) - # It's a control empty: turn the ik back on - if not isinstance(cons_obj, bpy.types.PoseBone): - ik_con = hasIKConstraint(end_bone) - if ik_con: - ik_con.mute = False - m_constraint.active = True - - -def updateConstraints(obj, context): - fixes = obj.data.mocap_constraints - for fix in fixes: - fix.active = False - fix.active = True diff --git a/release/scripts/modules/mocap_tools.py b/release/scripts/modules/mocap_tools.py deleted file mode 100644 index 6c22f718296..00000000000 --- a/release/scripts/modules/mocap_tools.py +++ /dev/null @@ -1,904 +0,0 @@ -# ##### 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 ##### - -# - -from math import hypot, sqrt, isfinite, radians, pi -import bpy -import time -from mathutils import Vector, Matrix - - -# A Python implementation of n sized Vectors. -# Mathutils has a max size of 4, and we need at least 5 for Simplify Curves and even more for Cross Correlation. -# Vector utility functions -class NdVector: - vec = [] - - def __init__(self, vec): - self.vec = vec[:] - - def __len__(self): - return len(self.vec) - - def __mul__(self, otherMember): - if (isinstance(otherMember, int) or - isinstance(otherMember, float)): - return NdVector([otherMember * x for x in self.vec]) - else: - a = self.vec - b = otherMember.vec - n = len(self) - return sum([a[i] * b[i] for i in range(n)]) - - def __sub__(self, otherVec): - a = self.vec - b = otherVec.vec - n = len(self) - return NdVector([a[i] - b[i] for i in range(n)]) - - def __add__(self, otherVec): - a = self.vec - b = otherVec.vec - n = len(self) - return NdVector([a[i] + b[i] for i in range(n)]) - - def __div__(self, scalar): - return NdVector([x / scalar for x in self.vec]) - - def vecLength(self): - return sqrt(self * self) - - def vecLengthSq(self): - return (self * self) - - def normalize(self): - len = self.length - self.vec = [x / len for x in self.vec] - - def copy(self): - return NdVector(self.vec) - - def __getitem__(self, i): - return self.vec[i] - - def x(self): - return self.vec[0] - - def y(self): - return self.vec[1] - - def resize_2d(self): - return Vector((self.x, self.y)) - - length = property(vecLength) - lengthSq = property(vecLengthSq) - x = property(x) - y = property(y) - - -#Sampled Data Point class for Simplify Curves -class dataPoint: - index = 0 - # x,y1,y2,y3 coordinate of original point - co = NdVector((0, 0, 0, 0, 0)) - #position according to parametric view of original data, [0,1] range - u = 0 - #use this for anything - temp = 0 - - def __init__(self, index, co, u=0): - self.index = index - self.co = co - self.u = u - - -#Cross Correlation Function -#http://en.wikipedia.org/wiki/Cross_correlation -#IN: curvesA, curvesB - bpy_collection/list of fcurves to analyze. Auto-Correlation is when they are the same. -# margin - When searching for the best "start" frame, how large a neighborhood of frames should we inspect (similar to epsilon in Calculus) -#OUT: startFrame, length of new anim, and curvesA -def crossCorrelationMatch(curvesA, curvesB, margin): - dataA = [] - dataB = [] - start, end = curvesA[0].range() - start = int(start) - end = int(end) - - #transfer all fcurves data on each frame to a single NdVector. - for i in range(1, end): - vec = [] - for fcurve in curvesA: - vec.append(fcurve.evaluate(i)) - dataA.append(NdVector(vec)) - vec = [] - for fcurve in curvesB: - vec.append(fcurve.evaluate(i)) - dataB.append(NdVector(vec)) - - #Comparator for Cross Correlation. "Classic" implementation uses dot product, as do we. - def comp(a, b): - return a * b - - #Create Rxy, which holds the Cross Correlation data. - N = len(dataA) - Rxy = [0.0] * N - for i in range(N): - for j in range(i, min(i + N, N)): - Rxy[i] += comp(dataA[j], dataB[j - i]) - for j in range(i): - Rxy[i] += comp(dataA[j], dataB[j - i + N]) - Rxy[i] /= float(N) - - #Find the Local maximums in the Cross Correlation data via numerical derivative. - def LocalMaximums(Rxy): - Rxyd = [Rxy[i] - Rxy[i - 1] for i in range(1, len(Rxy))] - maxs = [] - for i in range(1, len(Rxyd) - 1): - a = Rxyd[i - 1] - b = Rxyd[i] - #sign change (zerocrossing) at point i, denoting max point (only) - if (a >= 0 and b < 0) or (a < 0 and b >= 0): - maxs.append((i, max(Rxy[i], Rxy[i - 1]))) - return [x[0] for x in maxs] - #~ return max(maxs, key=lambda x: x[1])[0] - - #flms - the possible offsets of the first part of the animation. In Auto-Corr, this is the length of the loop. - flms = LocalMaximums(Rxy[0:int(len(Rxy))]) - ss = [] - - #for every local maximum, find the best one - i.e. also has the best start frame. - for flm in flms: - diff = [] - - for i in range(len(dataA) - flm): - diff.append((dataA[i] - dataB[i + flm]).lengthSq) - - def lowerErrorSlice(diff, e): - #index, error at index - bestSlice = (0, 100000) - for i in range(e, len(diff) - e): - errorSlice = sum(diff[i - e:i + e + 1]) - if errorSlice < bestSlice[1]: - bestSlice = (i, errorSlice, flm) - return bestSlice - - s = lowerErrorSlice(diff, margin) - ss.append(s) - - #Find the best result and return it. - ss.sort(key=lambda x: x[1]) - return ss[0][2], ss[0][0], dataA - - -#Uses auto correlation (cross correlation of the same set of curves) and trims the active_object's fcurves -#Except for location curves (which in mocap tend to be not cyclic, e.g. a walk cycle forward) -#Transfers the fcurve data to a list of NdVector (length of list is number of fcurves), and calls the cross correlation function. -#Then trims the fcurve accordingly. -#IN: Nothing, set the object you want as active and call. Assumes object has animation_data.action! -#OUT: Trims the object's fcurves (except location curves). -def autoloop_anim(): - context = bpy.context - obj = context.active_object - - def locCurve(x): - x.data_path == "location" - - fcurves = [x for x in obj.animation_data.action.fcurves if not locCurve(x)] - - margin = 10 - - flm, s, data = crossCorrelationMatch(fcurves, fcurves, margin) - loop = data[s:s + flm] - - #performs blending with a root falloff on the seam's neighborhood to ensure good tiling. - for i in range(1, margin + 1): - w1 = sqrt(float(i) / margin) - loop[-i] = (loop[-i] * w1) + (loop[0] * (1 - w1)) - - for curve in fcurves: - pts = curve.keyframe_points - for i in range(len(pts) - 1, -1, -1): - pts.remove(pts[i]) - - for c, curve in enumerate(fcurves): - pts = curve.keyframe_points - for i in range(len(loop)): - pts.insert(i + 2, loop[i][c]) - - context.scene.frame_end = flm - - -#simplifyCurves: performes the bulk of the samples to bezier conversion. -#IN: curveGroup - which can be a collection of singleFcurves, or grouped (via nested lists) . -# error - threshold of permittable error (max distance) of the new beziers to the original data -# reparaError - threshold of error where we should try to fix the parameterization rather than split the existing curve. > error, usually by a small constant factor for best performance. -# maxIterations - maximum number of iterations of reparameterizations we should attempt. (Newton-Rahpson is not guarenteed to converge, so this is needed). -# group_mode - boolean, indicating wether we should place bezier keyframes on the same x (frame), or optimize each individual curve. -#OUT: None. Deletes the existing curves and creates the new beziers. -def simplifyCurves(curveGroup, error, reparaError, maxIterations, group_mode): - - #Calculates the unit tangent of point v - def unitTangent(v, data_pts): - tang = NdVector((0, 0, 0, 0, 0)) - if v != 0: - #If it's not the first point, we can calculate a leftside tangent - tang += data_pts[v].co - data_pts[v - 1].co - if v != len(data_pts) - 1: - #If it's not the last point, we can calculate a rightside tangent - tang += data_pts[v + 1].co - data_pts[v].co - tang.normalize() - return tang - - #assign parametric u value for each point in original data, via relative arc length - #http://en.wikipedia.org/wiki/Arc_length - def chordLength(data_pts, s, e): - totalLength = 0 - for pt in data_pts[s:e + 1]: - i = pt.index - if i == s: - chordLength = 0 - else: - chordLength = (data_pts[i].co - data_pts[i - 1].co).length - totalLength += chordLength - pt.temp = totalLength - for pt in data_pts[s:e + 1]: - if totalLength == 0: - print(s, e) - pt.u = (pt.temp / totalLength) - - # get binomial coefficient lookup table, this function/table is only called with args - # (3,0),(3,1),(3,2),(3,3),(2,0),(2,1),(2,2)! - binomDict = {(3, 0): 1, - (3, 1): 3, - (3, 2): 3, - (3, 3): 1, - (2, 0): 1, - (2, 1): 2, - (2, 2): 1} - - #value at pt t of a single bernstein Polynomial - def bernsteinPoly(n, i, t): - binomCoeff = binomDict[(n, i)] - return binomCoeff * pow(t, i) * pow(1 - t, n - i) - - # fit a single cubic to data points in range [s(tart),e(nd)]. - def fitSingleCubic(data_pts, s, e): - - # A - matrix used for calculating C matrices for fitting - def A(i, j, s, e, t1, t2): - if j == 1: - t = t1 - if j == 2: - t = t2 - u = data_pts[i].u - return t * bernsteinPoly(3, j, u) - - # X component, used for calculating X matrices for fitting - def xComponent(i, s, e): - di = data_pts[i].co - u = data_pts[i].u - v0 = data_pts[s].co - v3 = data_pts[e].co - a = v0 * bernsteinPoly(3, 0, u) - b = v0 * bernsteinPoly(3, 1, u) - c = v3 * bernsteinPoly(3, 2, u) - d = v3 * bernsteinPoly(3, 3, u) - return (di - (a + b + c + d)) - - t1 = unitTangent(s, data_pts) - t2 = unitTangent(e, data_pts) - c11 = sum([A(i, 1, s, e, t1, t2) * A(i, 1, s, e, t1, t2) for i in range(s, e + 1)]) - c12 = sum([A(i, 1, s, e, t1, t2) * A(i, 2, s, e, t1, t2) for i in range(s, e + 1)]) - c21 = c12 - c22 = sum([A(i, 2, s, e, t1, t2) * A(i, 2, s, e, t1, t2) for i in range(s, e + 1)]) - - x1 = sum([xComponent(i, s, e) * A(i, 1, s, e, t1, t2) for i in range(s, e + 1)]) - x2 = sum([xComponent(i, s, e) * A(i, 2, s, e, t1, t2) for i in range(s, e + 1)]) - - # calculate Determinate of the 3 matrices - det_cc = c11 * c22 - c21 * c12 - det_cx = c11 * x2 - c12 * x1 - det_xc = x1 * c22 - x2 * c12 - - # if matrix is not homogenous, fudge the data a bit - if det_cc == 0: - det_cc = 0.01 - - # alpha's are the correct offset for bezier handles - alpha0 = det_xc / det_cc # offset from right (first) point - alpha1 = det_cx / det_cc # offset from left (last) point - - sRightHandle = data_pts[s].co.copy() - sTangent = t1 * abs(alpha0) - sRightHandle += sTangent # position of first pt's handle - eLeftHandle = data_pts[e].co.copy() - eTangent = t2 * abs(alpha1) - eLeftHandle += eTangent # position of last pt's handle. - - # return a 4 member tuple representing the bezier - return (data_pts[s].co, - sRightHandle, - eLeftHandle, - data_pts[e].co) - - # convert 2 given data points into a cubic bezier. - # handles are offset along the tangent at - # a 3rd of the length between the points. - def fitSingleCubic2Pts(data_pts, s, e): - alpha0 = alpha1 = (data_pts[s].co - data_pts[e].co).length / 3 - - sRightHandle = data_pts[s].co.copy() - sTangent = unitTangent(s, data_pts) * abs(alpha0) - sRightHandle += sTangent # position of first pt's handle - eLeftHandle = data_pts[e].co.copy() - eTangent = unitTangent(e, data_pts) * abs(alpha1) - eLeftHandle += eTangent # position of last pt's handle. - - #return a 4 member tuple representing the bezier - return (data_pts[s].co, - sRightHandle, - eLeftHandle, - data_pts[e].co) - - #evaluate bezier, represented by a 4 member tuple (pts) at point t. - def bezierEval(pts, t): - sumVec = NdVector((0, 0, 0, 0, 0)) - for i in range(4): - sumVec += pts[i] * bernsteinPoly(3, i, t) - return sumVec - - #calculate the highest error between bezier and original data - #returns the distance and the index of the point where max error occurs. - def maxErrorAmount(data_pts, bez, s, e): - maxError = 0 - maxErrorPt = s - if e - s < 3: - return 0, None - for pt in data_pts[s:e + 1]: - bezVal = bezierEval(bez, pt.u) - normalize_error = pt.co.length - if normalize_error == 0: - normalize_error = 1 - tmpError = (pt.co - bezVal).length / normalize_error - if tmpError >= maxError: - maxError = tmpError - maxErrorPt = pt.index - return maxError, maxErrorPt - - #calculated bezier derivative at point t. - #That is, tangent of point t. - def getBezDerivative(bez, t): - n = len(bez) - 1 - sumVec = NdVector((0, 0, 0, 0, 0)) - for i in range(n - 1): - sumVec += (bez[i + 1] - bez[i]) * bernsteinPoly(n - 1, i, t) - return sumVec - - #use Newton-Raphson to find a better paramterization of datapoints, - #one that minimizes the distance (or error) - # between bezier and original data. - def newtonRaphson(data_pts, s, e, bez): - for pt in data_pts[s:e + 1]: - if pt.index == s: - pt.u = 0 - elif pt.index == e: - pt.u = 1 - else: - u = pt.u - qu = bezierEval(bez, pt.u) - qud = getBezDerivative(bez, u) - #we wish to minimize f(u), - #the squared distance between curve and data - fu = (qu - pt.co).length ** 2 - fud = (2 * (qu.x - pt.co.x) * (qud.x)) - (2 * (qu.y - pt.co.y) * (qud.y)) - if fud == 0: - fu = 0 - fud = 1 - pt.u = pt.u - (fu / fud) - - #Create data_pts, a list of dataPoint type, each is assigned index i, and an NdVector - def createDataPts(curveGroup, group_mode): - data_pts = [] - if group_mode: - print([x.data_path for x in curveGroup]) - for i in range(len(curveGroup[0].keyframe_points)): - x = curveGroup[0].keyframe_points[i].co.x - y1 = curveGroup[0].keyframe_points[i].co.y - y2 = curveGroup[1].keyframe_points[i].co.y - y3 = curveGroup[2].keyframe_points[i].co.y - y4 = 0 - if len(curveGroup) == 4: - y4 = curveGroup[3].keyframe_points[i].co.y - data_pts.append(dataPoint(i, NdVector((x, y1, y2, y3, y4)))) - else: - for i in range(len(curveGroup.keyframe_points)): - x = curveGroup.keyframe_points[i].co.x - y1 = curveGroup.keyframe_points[i].co.y - y2 = 0 - y3 = 0 - y4 = 0 - data_pts.append(dataPoint(i, NdVector((x, y1, y2, y3, y4)))) - return data_pts - - #Recursively fit cubic beziers to the data_pts between s and e - def fitCubic(data_pts, s, e): - # if there are less than 3 points, fit a single basic bezier - if e - s < 3: - bez = fitSingleCubic2Pts(data_pts, s, e) - else: - #if there are more, parameterize the points - # and fit a single cubic bezier - chordLength(data_pts, s, e) - bez = fitSingleCubic(data_pts, s, e) - - #calculate max error and point where it occurs - maxError, maxErrorPt = maxErrorAmount(data_pts, bez, s, e) - #if error is small enough, reparameterization might be enough - if maxError < reparaError and maxError > error: - for i in range(maxIterations): - newtonRaphson(data_pts, s, e, bez) - if e - s < 3: - bez = fitSingleCubic2Pts(data_pts, s, e) - else: - bez = fitSingleCubic(data_pts, s, e) - - #recalculate max error and point where it occurs - maxError, maxErrorPt = maxErrorAmount(data_pts, bez, s, e) - - #repara wasn't enough, we need 2 beziers for this range. - #Split the bezier at point of maximum error - if maxError > error: - fitCubic(data_pts, s, maxErrorPt) - fitCubic(data_pts, maxErrorPt, e) - else: - #error is small enough, return the beziers. - beziers.append(bez) - return - - # deletes the sampled points and creates beziers. - def createNewCurves(curveGroup, beziers, group_mode): - #remove all existing data points - if group_mode: - for fcurve in curveGroup: - for i in range(len(fcurve.keyframe_points) - 1, 0, -1): - fcurve.keyframe_points.remove(fcurve.keyframe_points[i]) - else: - fcurve = curveGroup - for i in range(len(fcurve.keyframe_points) - 1, 0, -1): - fcurve.keyframe_points.remove(fcurve.keyframe_points[i]) - - #insert the calculated beziers to blender data.\ - if group_mode: - for fullbez in beziers: - for i, fcurve in enumerate(curveGroup): - bez = [Vector((vec[0], vec[i + 1])) for vec in fullbez] - newKey = fcurve.keyframe_points.insert(frame=bez[0].x, value=bez[0].y) - newKey.handle_right = (bez[1].x, bez[1].y) - - newKey = fcurve.keyframe_points.insert(frame=bez[3].x, value=bez[3].y) - newKey.handle_left = (bez[2].x, bez[2].y) - else: - for bez in beziers: - for vec in bez: - vec.resize_2d() - newKey = fcurve.keyframe_points.insert(frame=bez[0].x, value=bez[0].y) - newKey.handle_right = (bez[1].x, bez[1].y) - - newKey = fcurve.keyframe_points.insert(frame=bez[3].x, value=bez[3].y) - newKey.handle_left = (bez[2].x, bez[2].y) - - # indices are detached from data point's frame (x) value and - # stored in the dataPoint object, represent a range - - data_pts = createDataPts(curveGroup, group_mode) - - s = 0 # start - e = len(data_pts) - 1 # end - - beziers = [] - - #begin the recursive fitting algorithm. - fitCubic(data_pts, s, e) - #remove old Fcurves and insert the new ones - createNewCurves(curveGroup, beziers, group_mode) - - -#Main function of simplification, which called by Operator -#IN: -# sel_opt- either "sel" (selected) or "all" for which curves to effect -# error- maximum error allowed, in fraction (20% = 0.0020, which is the default), -# i.e. divide by 10000 from percentage wanted. -# group_mode- boolean, to analyze each curve seperately or in groups, -# where a group is all curves that effect the same property/RNA path -def fcurves_simplify(context, obj, sel_opt="all", error=0.002, group_mode=True): - # main vars - fcurves = obj.animation_data.action.fcurves - - if sel_opt == "sel": - sel_fcurves = [fcurve for fcurve in fcurves if fcurve.select] - else: - sel_fcurves = fcurves[:] - - #Error threshold for Newton Raphson reparamatizing - reparaError = error * 32 - maxIterations = 16 - - if group_mode: - fcurveDict = {} - #this loop sorts all the fcurves into groups of 3 or 4, - #based on their RNA Data path, which corresponds to - #which property they effect - for curve in sel_fcurves: - if curve.data_path in fcurveDict: # if this bone has been added, append the curve to its list - fcurveDict[curve.data_path].append(curve) - else: - fcurveDict[curve.data_path] = [curve] # new bone, add a new dict value with this first curve - fcurveGroups = fcurveDict.values() - else: - fcurveGroups = sel_fcurves - - if error > 0.00000: - #simplify every selected curve. - totalt = 0 - for i, fcurveGroup in enumerate(fcurveGroups): - print("Processing curve " + str(i + 1) + "/" + str(len(fcurveGroups))) - t = time.clock() - simplifyCurves(fcurveGroup, error, reparaError, maxIterations, group_mode) - t = time.clock() - t - print(str(t)[:5] + " seconds to process last curve") - totalt += t - print(str(totalt)[:5] + " seconds, total time elapsed") - - return - - -# Implementation of non-linear median filter, with variable kernel size -# Double pass - one marks spikes, the other smooths them -# Expects sampled keyframes on everyframe -# IN: None. Performs the operations on the active_object's fcurves. Expects animation_data.action to exist! -# OUT: None. Fixes the fcurves "in-place". -def denoise_median(): - context = bpy.context - obj = context.active_object - fcurves = obj.animation_data.action.fcurves - medKernel = 1 # actually *2+1... since it this is offset - flagKernel = 4 - highThres = (flagKernel * 2) - 1 - lowThres = 0 - for fcurve in fcurves: - orgPts = fcurve.keyframe_points[:] - flaggedFrames = [] - # mark frames that are spikes by sorting a large kernel - for i in range(flagKernel, len(fcurve.keyframe_points) - flagKernel): - center = orgPts[i] - neighborhood = orgPts[i - flagKernel: i + flagKernel] - neighborhood.sort(key=lambda pt: pt.co[1]) - weight = neighborhood.index(center) - if weight >= highThres or weight <= lowThres: - flaggedFrames.append((i, center)) - # clean marked frames with a simple median filter - # averages all frames in the kernel equally, except center which has no weight - for i, pt in flaggedFrames: - newValue = 0 - sumWeights = 0 - neighborhood = [neighpt.co[1] for neighpt in orgPts[i - medKernel: i + medKernel + 1] if neighpt != pt] - newValue = sum(neighborhood) / len(neighborhood) - pt.co[1] = newValue - return - - -# Recieves armature, and rotations all bones by 90 degrees along the X axis -# This fixes the common axis issue BVH files have when importing. -# IN: Armature (bpy.types.Armature) -def rotate_fix_armature(arm_data): - global_matrix = Matrix.Rotation(radians(90), 4, "X") - bpy.ops.object.mode_set(mode='EDIT', toggle=False) - #disconnect all bones for ease of global rotation - connectedBones = [] - for bone in arm_data.edit_bones: - if bone.use_connect: - connectedBones.append(bone.name) - bone.use_connect = False - - #rotate all the bones around their center - for bone in arm_data.edit_bones: - bone.transform(global_matrix) - - #reconnect the bones - for bone in connectedBones: - arm_data.edit_bones[bone].use_connect = True - bpy.ops.object.mode_set(mode='OBJECT', toggle=False) - - -#Roughly scales the performer armature to match the enduser armature -#IN: perfromer_obj, enduser_obj, Blender objects whose .data is an armature. -def scale_fix_armature(performer_obj, enduser_obj): - perf_bones = performer_obj.data.bones - end_bones = enduser_obj.data.bones - - def calculateBoundingRadius(bones): - center = Vector() - for bone in bones: - center += bone.head_local - center /= len(bones) - radius = 0 - for bone in bones: - dist = (bone.head_local - center).length - if dist > radius: - radius = dist - return radius - - perf_rad = calculateBoundingRadius(performer_obj.data.bones) - end_rad = calculateBoundingRadius(enduser_obj.data.bones) - #end_avg = enduser_obj.dimensions - factor = end_rad / perf_rad * 1.2 - performer_obj.scale *= factor - - -#Guess Mapping -#Given a performer and enduser armature, attempts to guess the hiearchy mapping -def guessMapping(performer_obj, enduser_obj): - perf_bones = performer_obj.data.bones - end_bones = enduser_obj.data.bones - - root = perf_bones[0] - - def findBoneSide(bone): - if "Left" in bone: - return "Left", bone.replace("Left", "").lower().replace(".", "") - if "Right" in bone: - return "Right", bone.replace("Right", "").lower().replace(".", "") - if "L" in bone: - return "Left", bone.replace("Left", "").lower().replace(".", "") - if "R" in bone: - return "Right", bone.replace("Right", "").lower().replace(".", "") - return "", bone - - def nameMatch(bone_a, bone_b): - # nameMatch - recieves two strings, returns 2 if they are relatively the same, 1 if they are the same but R and L and 0 if no match at all - side_a, noside_a = findBoneSide(bone_a) - side_b, noside_b = findBoneSide(bone_b) - if side_a == side_b: - if noside_a in noside_b or noside_b in noside_a: - return 2 - else: - if noside_a in noside_b or noside_b in noside_a: - return 1 - return 0 - - def guessSingleMapping(perf_bone): - possible_bones = [end_bones[0]] - - while possible_bones: - for end_bone in possible_bones: - match = nameMatch(perf_bone.name, end_bone.name) - if match == 2 and not perf_bone.map: - perf_bone.map = end_bone.name - #~ elif match == 1 and not perf_bone.map: - #~ oppo = perf_bones[oppositeBone(perf_bone)].map - # if oppo: - # perf_bone = oppo - newPossibleBones = [] - for end_bone in possible_bones: - newPossibleBones += list(end_bone.children) - possible_bones = newPossibleBones - - for child in perf_bone.children: - guessSingleMapping(child) - - guessSingleMapping(root) - - -# Creates limit rotation constraints on the enduser armature based on range of motion (max min of fcurves) of the performer. -# IN: context (bpy.context, etc.), and 2 blender objects which are armatures -# OUT: creates the limit constraints. -def limit_dof(context, performer_obj, enduser_obj): - limitDict = {} - perf_bones = [bone for bone in performer_obj.pose.bones if bone.bone.map] - c_frame = context.scene.frame_current - for bone in perf_bones: - limitDict[bone.bone.map] = [1000, 1000, 1000, -1000, -1000, -1000] - for t in range(context.scene.frame_start, context.scene.frame_end): - context.scene.frame_set(t) - for bone in perf_bones: - end_bone = enduser_obj.pose.bones[bone.bone.map] - bake_matrix = bone.matrix - rest_matrix = end_bone.bone.matrix_local - - if end_bone.parent and end_bone.bone.use_inherit_rotation: - srcParent = bone.parent - parent_mat = srcParent.matrix - parent_rest = end_bone.parent.bone.matrix_local - parent_rest_inv = parent_rest.inverted() - parent_mat_inv = parent_mat.inverted() - bake_matrix = parent_mat_inv * bake_matrix - rest_matrix = parent_rest_inv * rest_matrix - - rest_matrix_inv = rest_matrix.inverted() - bake_matrix = rest_matrix_inv * bake_matrix - - mat = bake_matrix - euler = mat.to_euler() - limitDict[bone.bone.map][0] = min(limitDict[bone.bone.map][0], euler.x) - limitDict[bone.bone.map][1] = min(limitDict[bone.bone.map][1], euler.y) - limitDict[bone.bone.map][2] = min(limitDict[bone.bone.map][2], euler.z) - limitDict[bone.bone.map][3] = max(limitDict[bone.bone.map][3], euler.x) - limitDict[bone.bone.map][4] = max(limitDict[bone.bone.map][4], euler.y) - limitDict[bone.bone.map][5] = max(limitDict[bone.bone.map][5], euler.z) - for bone in enduser_obj.pose.bones: - existingConstraint = [constraint for constraint in bone.constraints if constraint.name == "DOF Limitation"] - if existingConstraint: - bone.constraints.remove(existingConstraint[0]) - end_bones = [bone for bone in enduser_obj.pose.bones if bone.name in limitDict.keys()] - for bone in end_bones: - #~ if not bone.is_in_ik_chain: - newCons = bone.constraints.new("LIMIT_ROTATION") - newCons.name = "DOF Limitation" - newCons.owner_space = "LOCAL" - newCons.min_x, newCons.min_y, newCons.min_z, newCons.max_x, newCons.max_y, newCons.max_z = limitDict[bone.name] - newCons.use_limit_x = True - newCons.use_limit_y = True - newCons.use_limit_z = True - context.scene.frame_set(c_frame) - - -# Removes the constraints that were added by limit_dof on the enduser_obj -def limit_dof_toggle_off(context, enduser_obj): - for bone in enduser_obj.pose.bones: - existingConstraint = [constraint for constraint in bone.constraints if constraint.name == "DOF Limitation"] - if existingConstraint: - bone.constraints.remove(existingConstraint[0]) - - -# Reparameterizes a blender path via keyframing it's eval_time to match a stride_object's forward velocity. -# IN: Context, stride object (blender object with location keyframes), path object. -def path_editing(context, stride_obj, path): - y_fcurve = [fcurve for fcurve in stride_obj.animation_data.action.fcurves if fcurve.data_path == "location"][1] - s, e = context.scene.frame_start, context.scene.frame_end # y_fcurve.range() - s = int(s) - e = int(e) - y_s = y_fcurve.evaluate(s) - y_e = y_fcurve.evaluate(e) - direction = (y_e - y_s) / abs(y_e - y_s) - existing_cons = [constraint for constraint in stride_obj.constraints if constraint.type == "FOLLOW_PATH"] - for cons in existing_cons: - stride_obj.constraints.remove(cons) - path_cons = stride_obj.constraints.new("FOLLOW_PATH") - if direction < 0: - path_cons.forward_axis = "TRACK_NEGATIVE_Y" - else: - path_cons.forward_axis = "FORWARD_Y" - path_cons.target = path - path_cons.use_curve_follow = True - path.data.path_duration = e - s - try: - path.data.animation_data.action.fcurves - except AttributeError: - path.data.keyframe_insert("eval_time", frame=0) - eval_time_fcurve = [fcurve for fcurve in path.data.animation_data.action.fcurves if fcurve.data_path == "eval_time"] - eval_time_fcurve = eval_time_fcurve[0] - totalLength = 0 - parameterization = {} - print("evaluating curve") - for t in range(s, e - 1): - if s == t: - chordLength = 0 - else: - chordLength = (y_fcurve.evaluate(t) - y_fcurve.evaluate(t + 1)) - totalLength += chordLength - parameterization[t] = totalLength - for t in range(s + 1, e - 1): - if totalLength == 0: - print("no forward motion") - parameterization[t] /= totalLength - parameterization[t] *= e - s - parameterization[e] = e - s - for t in parameterization.keys(): - eval_time_fcurve.keyframe_points.insert(frame=t, value=parameterization[t]) - y_fcurve.mute = True - print("finished path editing") - - -#Animation Stitching -#Stitches two retargeted animations together via NLA settings. -#IN: enduser_obj, a blender armature that has had two retargets applied. -def anim_stitch(context, enduser_obj): - stitch_settings = enduser_obj.data.stitch_settings - action_1 = stitch_settings.first_action - action_2 = stitch_settings.second_action - if stitch_settings.stick_bone != "": - selected_bone = enduser_obj.pose.bones[stitch_settings.stick_bone] - else: - selected_bone = enduser_obj.pose.bones[0] - scene = context.scene - TrackNamesA = enduser_obj.data.mocapNLATracks[action_1] - TrackNamesB = enduser_obj.data.mocapNLATracks[action_2] - enduser_obj.data.active_mocap = action_1 - anim_data = enduser_obj.animation_data - # add tracks for action 2 - mocapAction = bpy.data.actions[TrackNamesB.base_track] - mocapTrack = anim_data.nla_tracks.new() - mocapTrack.name = TrackNamesB.base_track - mocapStrip = mocapTrack.strips.new(TrackNamesB.base_track, stitch_settings.blend_frame, mocapAction) - mocapStrip.extrapolation = "HOLD_FORWARD" - mocapStrip.blend_in = stitch_settings.blend_amount - mocapStrip.action_frame_start += stitch_settings.second_offset - mocapStrip.action_frame_end += stitch_settings.second_offset - constraintTrack = anim_data.nla_tracks.new() - constraintTrack.name = TrackNamesB.auto_fix_track - constraintAction = bpy.data.actions[TrackNamesB.auto_fix_track] - constraintStrip = constraintTrack.strips.new(TrackNamesB.auto_fix_track, stitch_settings.blend_frame, constraintAction) - constraintStrip.extrapolation = "HOLD_FORWARD" - constraintStrip.blend_in = stitch_settings.blend_amount - userTrack = anim_data.nla_tracks.new() - userTrack.name = TrackNamesB.manual_fix_track - userAction = bpy.data.actions[TrackNamesB.manual_fix_track] - userStrip = userTrack.strips.new(TrackNamesB.manual_fix_track, stitch_settings.blend_frame, userAction) - userStrip.extrapolation = "HOLD_FORWARD" - userStrip.blend_in = stitch_settings.blend_amount - #stride bone - if enduser_obj.parent: - if enduser_obj.parent.name == "stride_bone": - stride_bone = enduser_obj.parent - stride_anim_data = stride_bone.animation_data - stride_anim_data.use_nla = True - stride_anim_data.action = None - for track in stride_anim_data.nla_tracks: - stride_anim_data.nla_tracks.remove(track) - actionATrack = stride_anim_data.nla_tracks.new() - actionATrack.name = TrackNamesA.stride_action - actionAStrip = actionATrack.strips.new(TrackNamesA.stride_action, 0, bpy.data.actions[TrackNamesA.stride_action]) - actionAStrip.extrapolation = "NOTHING" - actionBTrack = stride_anim_data.nla_tracks.new() - actionBTrack.name = TrackNamesB.stride_action - actionBStrip = actionBTrack.strips.new(TrackNamesB.stride_action, stitch_settings.blend_frame, bpy.data.actions[TrackNamesB.stride_action]) - actionBStrip.action_frame_start += stitch_settings.second_offset - actionBStrip.action_frame_end += stitch_settings.second_offset - actionBStrip.blend_in = stitch_settings.blend_amount - actionBStrip.extrapolation = "NOTHING" - #we need to change the stride_bone's action to add the offset - scene.frame_set(stitch_settings.blend_frame - 1) - desired_pos = (selected_bone.matrix.to_translation() * enduser_obj.matrix_world) - scene.frame_set(stitch_settings.blend_frame) - actual_pos = (selected_bone.matrix.to_translation() * enduser_obj.matrix_world) - offset = actual_pos - desired_pos - - for i, fcurve in enumerate([fcurve for fcurve in bpy.data.actions[TrackNamesB.stride_action].fcurves if fcurve.data_path == "location"]): - print(offset[i], i, fcurve.array_index) - for pt in fcurve.keyframe_points: - pt.co.y -= offset[i] - pt.handle_left.y -= offset[i] - pt.handle_right.y -= offset[i] - - -#Guesses setting for animation stitching via Cross Correlation -def guess_anim_stitch(context, enduser_obj): - stitch_settings = enduser_obj.data.stitch_settings - action_1 = stitch_settings.first_action - action_2 = stitch_settings.second_action - TrackNamesA = enduser_obj.data.mocapNLATracks[action_1] - TrackNamesB = enduser_obj.data.mocapNLATracks[action_2] - mocapA = bpy.data.actions[TrackNamesA.base_track] - mocapB = bpy.data.actions[TrackNamesB.base_track] - curvesA = mocapA.fcurves - curvesB = mocapB.fcurves - flm, s, data = crossCorrelationMatch(curvesA, curvesB, 10) - print("Guessed the following for start and offset: ", s, flm) - enduser_obj.data.stitch_settings.blend_frame = flm - enduser_obj.data.stitch_settings.second_offset = s diff --git a/release/scripts/modules/retarget.py b/release/scripts/modules/retarget.py deleted file mode 100644 index 67e8c7da55d..00000000000 --- a/release/scripts/modules/retarget.py +++ /dev/null @@ -1,559 +0,0 @@ -# ##### 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 ##### - -# - -import bpy -from mathutils import * -from math import radians, acos, pi -from bl_operators import nla - - -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 - - -def createDictionary(perf_arm, end_arm): - # clear any old data - for end_bone in end_arm.bones: - for mapping in end_bone.reverseMap: - 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 - end_bone.foot = perf_bone.foot - - #root is the root of the enduser - root = end_arm.bones[0].name - feetBones = [bone.name for bone in perf_arm.bones if bone.foot] - return feetBones, root - - -def loadMapping(perf_arm, end_arm): - for end_bone in end_arm.bones: - #find its match and add perf_bone to the match's mapping - if end_bone.reverseMap: - for perf_bone in end_bone.reverseMap: - perf_arm.bones[perf_bone.name].map = end_bone.name - -#creation of intermediate armature -# the intermediate armature has the hiearchy of the end user, -# does not have rotation inheritence -# and bone roll is identical to the performer -# its purpose is to copy over the rotations -# easily while concentrating on the hierarchy changes - - -def createIntermediate(performer_obj, enduser_obj, root, s_frame, e_frame, scene, step): - #creates and keyframes an empty with its location - #the original position of the tail bone - #useful for storing the important data in the original motion - #i.e. using this empty to IK the chain to that pos / DEBUG - - #Simple 1to1 retarget of a bone - def singleBoneRetarget(inter_bone, perf_bone): - perf_world_rotation = perf_bone.matrix - inter_world_base_rotation = inter_bone.bone.matrix_local - inter_world_base_inv = inter_world_base_rotation.inverted() - bake_matrix = (inter_world_base_inv.to_3x3() * perf_world_rotation.to_3x3()) - return bake_matrix.to_4x4() - - #uses 1to1 and interpolation/averaging to match many to 1 retarget - def manyPerfToSingleInterRetarget(inter_bone, performer_bones_s): - retarget_matrices = [singleBoneRetarget(inter_bone, perf_bone) for perf_bone in performer_bones_s] - lerp_matrix = Matrix() - for i in range(len(retarget_matrices) - 1): - first_mat = retarget_matrices[i] - next_mat = retarget_matrices[i + 1] - lerp_matrix = first_mat.lerp(next_mat, 0.5) - return lerp_matrix - - #determines the type of hierachy change needed and calls the - #right function - def retargetPerfToInter(inter_bone): - if inter_bone.bone.reverseMap: - perf_bone_name = inter_bone.bone.reverseMap - # 1 to many not supported yet - # then its either a many to 1 or 1 to 1 - if len(perf_bone_name) > 1: - performer_bones_s = [performer_bones[map.name] for map in perf_bone_name] - #we need to map several performance bone to a single - inter_bone.matrix_basis = manyPerfToSingleInterRetarget(inter_bone, performer_bones_s) - else: - perf_bone = performer_bones[perf_bone_name[0].name] - inter_bone.matrix_basis = singleBoneRetarget(inter_bone, perf_bone) - #Some bones have incorrect roll on the source armature, and need to be marked for fixing - if inter_bone.bone.twistFix: - inter_bone.matrix_basis *= Matrix.Rotation(radians(180), 4, "Y") - rot_mode = inter_bone.rotation_mode - if rot_mode == "QUATERNION": - inter_bone.keyframe_insert("rotation_quaternion") - elif rot_mode == "AXIS_ANGLE": - inter_bone.keyframe_insert("rotation_axis_angle") - else: - inter_bone.keyframe_insert("rotation_euler") - - #creates the intermediate armature object - inter_obj = enduser_obj.copy() - inter_obj.data = inter_obj.data.copy() # duplicate data - bpy.context.scene.objects.link(inter_obj) - inter_obj.name = "intermediate" - bpy.context.scene.objects.active = inter_obj - bpy.ops.object.mode_set(mode='EDIT') - #add some temporary connecting bones in case end user bones are not connected to their parents - rollDict = {} - print("creating temp bones") - for bone in inter_obj.data.edit_bones: - if not bone.use_connect and bone.parent: - if inter_obj.data.bones[bone.parent.name].reverseMap or inter_obj.data.bones[bone.name].reverseMap: - newBone = inter_obj.data.edit_bones.new("Temp") - newBone.head = bone.parent.tail - newBone.tail = bone.head - newBone.parent = bone.parent - bone.parent = newBone - bone.use_connect = True - newBone.use_connect = True - rollDict[bone.name] = bone.roll - bone.roll = 0 - #resets roll - print("retargeting to intermediate") - bpy.ops.object.mode_set(mode="OBJECT") - inter_obj.data.name = "inter_arm" - inter_arm = inter_obj.data - performer_bones = performer_obj.pose.bones - inter_bones = inter_obj.pose.bones - #clears inheritance - for inter_bone in inter_bones: - if inter_bone.bone.reverseMap: - inter_bone.bone.use_inherit_rotation = False - else: - inter_bone.bone.use_inherit_rotation = True - - for t in range(s_frame, e_frame, step): - if (t - s_frame) % 10 == 0: - print("First pass: retargeting frame {0}/{1}".format(t, e_frame - s_frame)) - scene.frame_set(t) - for bone in inter_bones: - retargetPerfToInter(bone) - - return inter_obj - -# this procedure copies the rotations over from the intermediate -# armature to the end user one. -# As the hierarchies are 1 to 1, this is a simple matter of -# copying the rotation, while keeping in mind bone roll, parenting, etc. -# TODO: Control Bones: If a certain bone is constrained in a way -# that its rotation is determined by another (a control bone) -# We should determine the right pos of the control bone. -# Scale: ? Should work but needs testing. - - -def retargetEnduser(inter_obj, enduser_obj, root, s_frame, e_frame, scene, step): - inter_bones = inter_obj.pose.bones - end_bones = enduser_obj.pose.bones - - #Basic "visual baking" function, for transfering rotations from intermediate to end user - def bakeTransform(end_bone): - src_bone = inter_bones[end_bone.name] - trg_bone = end_bone - bake_matrix = src_bone.matrix - rest_matrix = trg_bone.bone.matrix_local - - if trg_bone.parent and trg_bone.bone.use_inherit_rotation: - srcParent = src_bone.parent - if "Temp" in srcParent.name: - srcParent = srcParent.parent - parent_mat = srcParent.matrix - parent_rest = trg_bone.parent.bone.matrix_local - parent_rest_inv = parent_rest.inverted() - parent_mat_inv = parent_mat.inverted() - bake_matrix = parent_mat_inv * bake_matrix - rest_matrix = parent_rest_inv * rest_matrix - - rest_matrix_inv = rest_matrix.inverted() - bake_matrix = rest_matrix_inv * bake_matrix - end_bone.matrix_basis = bake_matrix - rot_mode = end_bone.rotation_mode - if rot_mode == "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") - if not end_bone.bone.use_connect: - end_bone.keyframe_insert("location") - - for bone in end_bone.children: - bakeTransform(bone) - - for t in range(s_frame, e_frame, step): - if (t - s_frame) % 10 == 0: - print("Second pass: retargeting frame {0}/{1}".format(t, e_frame - s_frame)) - scene.frame_set(t) - end_bone = end_bones[root] - end_bone.location = Vector((0, 0, 0)) - end_bone.keyframe_insert("location") - bakeTransform(end_bone) - -#recieves the performer feet bones as a variable -# by "feet" I mean those bones that have plants -# (they don't move, despite root moving) somewhere in the animation. - - -def copyTranslation(performer_obj, enduser_obj, perfFeet, root, s_frame, e_frame, scene, enduser_obj_mat): - - perf_bones = performer_obj.pose.bones - end_bones = enduser_obj.pose.bones - - perfRoot = perf_bones[0].name - endFeet = [perf_bones[perfBone].bone.map for perfBone in perfFeet] - locDictKeys = perfFeet + endFeet + [perfRoot] - - def tailLoc(bone): - return bone.center + (bone.vector / 2) - - #Step 1 - we create a dict that contains these keys: - #(Performer) Hips, Feet - #(End user) Feet - # where the values are their world position on each frame in range (s,e) - - locDict = {} - for key in locDictKeys: - locDict[key] = [] - - for t in range(scene.frame_start, scene.frame_end): - scene.frame_set(t) - for bone in perfFeet: - locDict[bone].append(tailLoc(perf_bones[bone])) - locDict[perfRoot].append(tailLoc(perf_bones[perfRoot])) - for bone in endFeet: - locDict[bone].append(tailLoc(end_bones[bone])) - - # now we take our locDict and analyze it. - # we need to derive all chains - - def locDeriv(key, t): - graph = locDict[key] - return graph[t + 1] - graph[t] - - # now find the plant frames, where perfFeet don't move much - - linearAvg = [] - - for key in perfFeet: - for i in range(len(locDict[key]) - 1): - v = locDeriv(key, i) - if (v.length < 0.1): - hipV = locDeriv(perfRoot, i) - endV = locDeriv(perf_bones[key].bone.map, i) - #this is a plant frame. - #lets see what the original hip delta is, and the corresponding - #end bone's delta - if endV.length != 0: - linearAvg.append(hipV.length / endV.length) - - action_name = performer_obj.animation_data.action.name - #is there a stride_bone? - if "stride_bone" in bpy.data.objects: - stride_action = bpy.data.actions.new("Stride Bone " + action_name) - stride_action.use_fake_user = True - stride_bone = enduser_obj.parent - stride_bone.animation_data.action = stride_action - else: - bpy.ops.object.add() - stride_bone = bpy.context.active_object - stride_bone.name = "stride_bone" - print(stride_bone) - stride_bone.location = enduser_obj_mat.to_translation() - print(linearAvg) - if linearAvg: - #determine the average change in scale needed - avg = sum(linearAvg) / len(linearAvg) - scene.frame_set(s_frame) - initialPos = (tailLoc(perf_bones[perfRoot]) / avg) - for t in range(s_frame, e_frame): - scene.frame_set(t) - #calculate the new position, by dividing by the found ratio between performer and enduser - newTranslation = (tailLoc(perf_bones[perfRoot]) / avg) - stride_bone.location = enduser_obj_mat * (newTranslation - initialPos) - stride_bone.keyframe_insert("location") - else: - stride_bone.keyframe_insert("location") - stride_bone.animation_data.action.name = ("Stride Bone " + action_name) - - return stride_bone - - -def IKRetarget(performer_obj, enduser_obj, s_frame, e_frame, scene, step): - bpy.ops.object.select_name(name=enduser_obj.name, extend=False) - end_bones = enduser_obj.pose.bones - for pose_bone in end_bones: - ik_constraint = hasIKConstraint(pose_bone) - if ik_constraint: - target_is_bone = False - # set constraint target to corresponding empty if targetless, - # if not, keyframe current target to corresponding empty - perf_bone = pose_bone.bone.reverseMap[-1].name - bpy.ops.object.mode_set(mode='EDIT') - orgLocTrg = originalLocationTarget(pose_bone, enduser_obj) - bpy.ops.object.mode_set(mode='OBJECT') - if not ik_constraint.target: - ik_constraint.target = enduser_obj - ik_constraint.subtarget = pose_bone.name + "IK" - target = orgLocTrg - - # There is a target now - if ik_constraint.subtarget: - target = ik_constraint.target.pose.bones[ik_constraint.subtarget] - target.bone.use_local_location = False - target_is_bone = True - else: - target = ik_constraint.target - - # bake the correct locations for the ik target bones - for t in range(s_frame, e_frame, step): - scene.frame_set(t) - if target_is_bone: - final_loc = pose_bone.tail - target.bone.matrix_local.to_translation() - else: - final_loc = pose_bone.tail - target.location = final_loc - target.keyframe_insert("location") - ik_constraint.mute = False - scene.frame_set(s_frame) - bpy.ops.object.mode_set(mode='OBJECT') - - -def turnOffIK(enduser_obj): - end_bones = enduser_obj.pose.bones - for pose_bone in end_bones: - ik_constraint = hasIKConstraint(pose_bone) - if ik_constraint: - ik_constraint.mute = True - - -#copy the object matrixes and clear them (to be reinserted later) -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() - performer_obj.matrix_world = zero_mat - enduser_obj.matrix_world = zero_mat - return perf_obj_mat, enduser_obj_mat - - -#restore the object matrixes after parenting the auto generated IK empties -def restoreObjMat(performer_obj, enduser_obj, perf_obj_mat, enduser_obj_mat, stride_bone, scene, s_frame): - 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 = stride_bone - performer_obj.matrix_world = perf_obj_mat - enduser_obj.parent = stride_bone - scene.frame_set(s_frame) - enduser_obj_mat = enduser_obj_mat.to_3x3().to_4x4() * Matrix.Translation(stride_bone.matrix_world.to_translation()) - enduser_obj.matrix_world = enduser_obj_mat - - -#create (or return if exists) the related IK empty to the bone -def originalLocationTarget(end_bone, enduser_obj): - if not end_bone.name + "IK" in enduser_obj.data.bones: - newBone = enduser_obj.data.edit_bones.new(end_bone.name + "IK") - newBone.head = end_bone.tail - newBone.tail = end_bone.tail + Vector((0, 0.1, 0)) - else: - newBone = enduser_obj.pose.bones[end_bone.name + "IK"] - return newBone - - -#create the specified NLA setup for base animation, constraints and tweak layer. -def NLASystemInitialize(enduser_arm, context): - enduser_obj = context.active_object - NLATracks = enduser_arm.mocapNLATracks[enduser_obj.data.active_mocap] - name = NLATracks.name - anim_data = enduser_obj.animation_data - s_frame = 0 - print(name) - if ("Base " + name) in bpy.data.actions: - mocapAction = bpy.data.actions[("Base " + name)] - else: - print("That retargeted anim has no base action") - anim_data.use_nla = True - for track in anim_data.nla_tracks: - anim_data.nla_tracks.remove(track) - mocapTrack = anim_data.nla_tracks.new() - mocapTrack.name = "Base " + name - NLATracks.base_track = mocapTrack.name - mocapStrip = mocapTrack.strips.new("Base " + name, s_frame, mocapAction) - constraintTrack = anim_data.nla_tracks.new() - constraintTrack.name = "Auto fixes " + name - NLATracks.auto_fix_track = constraintTrack.name - if ("Auto fixes " + name) in bpy.data.actions: - constraintAction = bpy.data.actions[("Auto fixes " + name)] - else: - constraintAction = bpy.data.actions.new("Auto fixes " + name) - constraintAction.use_fake_user = True - constraintStrip = constraintTrack.strips.new("Auto fixes " + name, s_frame, constraintAction) - constraintStrip.extrapolation = "NOTHING" - userTrack = anim_data.nla_tracks.new() - userTrack.name = "Manual fixes " + name - NLATracks.manual_fix_track = userTrack.name - if ("Manual fixes " + name) in bpy.data.actions: - userAction = bpy.data.actions[("Manual fixes " + name)] - else: - userAction = bpy.data.actions.new("Manual fixes " + name) - userAction.use_fake_user = True - userStrip = userTrack.strips.new("Manual fixes " + name, s_frame, userAction) - userStrip.extrapolation = "HOLD" - userStrip.blend_type = "ADD" - anim_data.nla_tracks.active = constraintTrack - anim_data.action_extrapolation = "NOTHING" - #set the stride_bone's action - if "stride_bone" in bpy.data.objects: - stride_bone = bpy.data.objects["stride_bone"] - if NLATracks.stride_action: - stride_bone.animation_data.action = bpy.data.actions[NLATracks.stride_action] - else: - NLATracks.stride_action = stride_bone.animation_data.action.name - stride_bone.animation_data.action.use_fake_user = True - anim_data.action = None - - -def preAdvancedRetargeting(performer_obj, enduser_obj): - createDictionary(performer_obj.data, enduser_obj.data) - bones = enduser_obj.pose.bones - map_bones = [bone for bone in bones if bone.bone.reverseMap] - perf_root = performer_obj.pose.bones[0].name - for bone in map_bones: - perf_bone = bone.bone.reverseMap[0].name - - cons = bone.constraints.new('COPY_ROTATION') - cons.name = "retargetTemp" - locks = bone.lock_rotation - cons.use_x = not locks[0] - cons.use_y = not locks[1] - cons.use_z = not locks[2] - cons.target = performer_obj - cons.subtarget = perf_bone - cons.target_space = 'WORLD' - cons.owner_space = 'WORLD' - - if (not bone.bone.use_connect) and (perf_bone != perf_root): - cons = bone.constraints.new('COPY_LOCATION') - cons.name = "retargetTemp" - cons.target = performer_obj - cons.subtarget = perf_bone - cons.use_x = True - cons.use_y = True - cons.use_z = True - cons.target_space = 'LOCAL' - cons.owner_space = 'LOCAL' - - -def prepareForBake(enduser_obj): - bones = enduser_obj.pose.bones - for bone in bones: - bone.bone.select = False - map_bones = [bone for bone in bones if bone.bone.reverseMap] - for bone in map_bones: - for cons in bone.constraints: - if "retargetTemp" in cons.name: - bone.bone.select = True - - -def cleanTempConstraints(enduser_obj): - bones = enduser_obj.pose.bones - map_bones = [bone for bone in bones if bone.bone.reverseMap] - for bone in map_bones: - for cons in bone.constraints: - if "retargetTemp" in cons.name: - bone.constraints.remove(cons) - - -#Main function that runs the retargeting sequence. -#If advanced == True, we assume constraint's were already created -def totalRetarget(performer_obj, enduser_obj, scene, s_frame, e_frame): - perf_arm = performer_obj.data - end_arm = enduser_obj.data - advanced = end_arm.advancedRetarget - step = end_arm.frameStep - - try: - enduser_obj.animation_data.action = bpy.data.actions.new("temp") - enduser_obj.animation_data.action.use_fake_user = True - except: - print("no need to create new action") - - print("creating Dictionary") - feetBones, root = createDictionary(perf_arm, end_arm) - print("cleaning stuff up") - perf_obj_mat, enduser_obj_mat = cleanAndStoreObjMat(performer_obj, enduser_obj) - if not advanced: - turnOffIK(enduser_obj) - print("Creating intermediate armature (for first pass)") - inter_obj = createIntermediate(performer_obj, enduser_obj, root, s_frame, e_frame, scene, step) - print("First pass: retargeting from intermediate to end user") - retargetEnduser(inter_obj, enduser_obj, root, s_frame, e_frame, scene, step) - else: - prepareForBake(enduser_obj) - print("Retargeting pose (Advanced Retarget)") - nla.bake(s_frame, e_frame, action=enduser_obj.animation_data.action, only_selected=True, do_pose=True, do_object=False, step=step) - name = performer_obj.animation_data.action.name - enduser_obj.animation_data.action.name = "Base " + name - print("Second pass: retargeting root translation and clean up") - stride_bone = copyTranslation(performer_obj, enduser_obj, feetBones, root, s_frame, e_frame, scene, enduser_obj_mat) - if not advanced: - IKRetarget(performer_obj, enduser_obj, s_frame, e_frame, scene, step) - bpy.ops.object.select_name(name=stride_bone.name, extend=False) - restoreObjMat(performer_obj, enduser_obj, perf_obj_mat, enduser_obj_mat, stride_bone, scene, s_frame) - bpy.ops.object.mode_set(mode='OBJECT') - if not advanced: - bpy.ops.object.select_name(name=inter_obj.name, extend=False) - bpy.ops.object.delete() - else: - cleanTempConstraints(enduser_obj) - bpy.ops.object.select_name(name=enduser_obj.name, extend=False) - - if not name in [tracks.name for tracks in end_arm.mocapNLATracks]: - NLATracks = end_arm.mocapNLATracks.add() - NLATracks.name = name - else: - NLATracks = end_arm.mocapNLATracks[name] - end_arm.active_mocap = name - print("retargeting done!") - - -def isRigAdvanced(enduser_obj): - bones = enduser_obj.pose.bones - for bone in bones: - for constraint in bone.constraints: - if constraint.type != "IK": - return True - if enduser_obj.data.animation_data: - if enduser_obj.data.animation_data.drivers: - return True diff --git a/release/scripts/startup/ui_mocap.py b/release/scripts/startup/ui_mocap.py deleted file mode 100644 index 3cb33776b0b..00000000000 --- a/release/scripts/startup/ui_mocap.py +++ /dev/null @@ -1,850 +0,0 @@ -# ##### 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 ##### - -# - -import bpy - -from bpy.props import * -from bpy import * -import mocap_constraints -import retarget -import mocap_tools - -### reloads modules (for testing purposes only) -from imp import reload -reload(mocap_constraints) -reload(retarget) -reload(mocap_tools) - -from mocap_constraints import * - -# MocapConstraint class -# Defines MocapConstraint datatype, used to add and configute mocap constraints -# Attached to Armature data - - -class MocapConstraint(bpy.types.PropertyGroup): - name = bpy.props.StringProperty(name="Name", - default="Mocap Fix", - description="Name of Mocap Fix", - update=setConstraint) - constrained_bone = bpy.props.StringProperty(name="Bone", - default="", - description="Constrained Bone", - update=updateConstraintBoneType) - constrained_boneB = bpy.props.StringProperty(name="Bone (2)", - default="", - description="Other Constrained Bone (optional, depends on type)", - update=setConstraint) - s_frame = bpy.props.IntProperty(name="S", - default=0, - description="Start frame of Fix", - update=setConstraint) - e_frame = bpy.props.IntProperty(name="E", - default=100, - description="End frame of Fix", - update=setConstraint) - smooth_in = bpy.props.IntProperty(name="In", - default=10, - description="Amount of frames to smooth in", - update=setConstraint, - min=0) - smooth_out = bpy.props.IntProperty(name="Out", - default=10, - description="Amount of frames to smooth out", - update=setConstraint, - min=0) - targetMesh = bpy.props.StringProperty(name="Mesh", - default="", - description="Target of Fix - Mesh (optional, depends on type)", - update=setConstraint) - active = bpy.props.BoolProperty(name="Active", - default=True, - description="Fix is active", - update=setConstraint) - show_expanded = bpy.props.BoolProperty(name="Show Expanded", - default=True, - description="Fix is fully shown") - targetPoint = bpy.props.FloatVectorProperty(name="Point", size=3, - subtype="XYZ", default=(0.0, 0.0, 0.0), - description="Target of Fix - Point", - update=setConstraint) - targetDist = bpy.props.FloatProperty(name="Offset", - default=0.0, - description="Distance and Floor Fixes - Desired offset", - update=setConstraint) - targetSpace = bpy.props.EnumProperty( - items=[("WORLD", "World Space", "Evaluate target in global space"), - ("LOCAL", "Object space", "Evaluate target in object space"), - ("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=setConstraint) - type = bpy.props.EnumProperty(name="Type of constraint", - 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 Fix", - update=updateConstraintBoneType) - real_constraint = bpy.props.StringProperty() - real_constraint_bone = bpy.props.StringProperty() - - -bpy.utils.register_class(MocapConstraint) - -bpy.types.Armature.mocap_constraints = bpy.props.CollectionProperty(type=MocapConstraint) - - -# Animation Stitch Settings, used for animation stitching of 2 retargeted animations. -class AnimationStitchSettings(bpy.types.PropertyGroup): - first_action = bpy.props.StringProperty(name="Action 1", - description="First action in stitch") - second_action = bpy.props.StringProperty(name="Action 2", - description="Second action in stitch") - blend_frame = bpy.props.IntProperty(name="Stitch frame", - description="Frame to locate stitch on") - blend_amount = bpy.props.IntProperty(name="Blend amount", - description="Size of blending transitiion, on both sides of the stitch", - default=10) - second_offset = bpy.props.IntProperty(name="Second offset", - description="Frame offset for 2nd animation, where it should start", - default=10) - stick_bone = bpy.props.StringProperty(name="Stick Bone", - description="Bone to freeze during transition", - default="") - -bpy.utils.register_class(AnimationStitchSettings) - - -# MocapNLA Tracks. Stores which tracks/actions are associated with each retargeted animation. -class MocapNLATracks(bpy.types.PropertyGroup): - name = bpy.props.StringProperty() - base_track = bpy.props.StringProperty() - auto_fix_track = bpy.props.StringProperty() - manual_fix_track = bpy.props.StringProperty() - stride_action = bpy.props.StringProperty() - -bpy.utils.register_class(MocapNLATracks) - - -#Update function for Advanced Retarget boolean variable. -def advancedRetargetToggle(self, context): - 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") - return - else: - performer_obj = performer_obj[0] - if self.advancedRetarget: - retarget.preAdvancedRetargeting(performer_obj, enduser_obj) - else: - retarget.cleanTempConstraints(enduser_obj) - - -#Animation Stitch Settings Property -bpy.types.Armature.stitch_settings = bpy.props.PointerProperty(type=AnimationStitchSettings) -#Current/Active retargeted animation on the armature -bpy.types.Armature.active_mocap = bpy.props.StringProperty(update=retarget.NLASystemInitialize) -#Collection of retargeted animations and their NLA Tracks on the armature -bpy.types.Armature.mocapNLATracks = bpy.props.CollectionProperty(type=MocapNLATracks) -#Advanced retargeting boolean property -bpy.types.Armature.advancedRetarget = bpy.props.BoolProperty(default=False, update=advancedRetargetToggle) -#frame step - frequency of frames to retarget. Skipping is useful for previewing, faster work etc. -bpy.types.Armature.frameStep = smooth_out = bpy.props.IntProperty(name="Frame Skip", - default=1, - description="Amount of frames to skip - for previewing retargets quickly. 1 is fully sampled", - min=1) - - -def toggleIKBone(self, context): - #Update function for IK functionality. Is called when IK prop checkboxes are toggled. - if self.IKRetarget: - if not self.is_in_ik_chain: - print(self.name + " IK toggled ON!") - ik = self.constraints.new('IK') - #ik the whole chain up to the root, excluding - chainLen = 0 - for parent_bone in self.parent_recursive: - chainLen += 1 - if hasIKConstraint(parent_bone): - break - deformer_children = [child for child in parent_bone.children if child.bone.use_deform] - #~ if len(deformer_children) > 1: - #~ break - ik.chain_count = chainLen - for bone in self.parent_recursive: - if bone.is_in_ik_chain: - bone.IKRetarget = True - else: - print(self.name + " IK toggled OFF!") - cnstrn_bones = [] - newChainLength = [] - if hasIKConstraint(self): - cnstrn_bones = [self] - elif self.is_in_ik_chain: - cnstrn_bones = [child for child in self.children_recursive if hasIKConstraint(child)] - for cnstrn_bone in cnstrn_bones: - newChainLength.append(cnstrn_bone.parent_recursive.index(self) + 1) - if cnstrn_bones: - # remove constraint, and update IK retarget for all parents of cnstrn_bone up to chain_len - for i, cnstrn_bone in enumerate(cnstrn_bones): - print(cnstrn_bone.name) - if newChainLength: - ik = hasIKConstraint(cnstrn_bone) - ik.chain_count = newChainLength[i] - else: - ik = hasIKConstraint(cnstrn_bone) - cnstrn_bone.constraints.remove(ik) - cnstrn_bone.IKRetarget = False - for bone in cnstrn_bone.parent_recursive: - if not bone.is_in_ik_chain: - bone.IKRetarget = False - - -#MocapMap class for storing mapping on enduser performer, -# where a bone may be linked to more than one on the performer -class MocapMapping(bpy.types.PropertyGroup): - name = bpy.props.StringProperty() - -bpy.utils.register_class(MocapMapping) - -#string property for storing performer->enduser mapping -bpy.types.Bone.map = bpy.props.StringProperty() -#Collection Property for storing enduser->performer mapping -bpy.types.Bone.reverseMap = bpy.props.CollectionProperty(type=MocapMapping) -#Boolean property for storing foot bone toggle -bpy.types.Bone.foot = bpy.props.BoolProperty(name="Foot", - description="Marks this bone as a 'foot', which determines retargeted animation's translation", - default=False) -#Boolean property for storing if this bone is twisted along the y axis, -# which can happen due to various sources of performers -bpy.types.Bone.twistFix = bpy.props.BoolProperty(name="Twist Fix", - description="Fix Twist on this bone", - default=False) -#Boolean property for toggling ik retargeting for this bone -bpy.types.PoseBone.IKRetarget = bpy.props.BoolProperty(name="IK", - description="Toggles IK Retargeting method for given bone", - update=toggleIKBone, default=False) - - -def updateIKRetarget(): - # ensures that Blender constraints and IK properties are in sync - # currently runs when module is loaded, should run when scene is loaded - # or user adds a constraint to armature. Will be corrected in the future, - # once python callbacks are implemented - for obj in bpy.data.objects: - if obj.pose: - bones = obj.pose.bones - for pose_bone in bones: - if pose_bone.is_in_ik_chain or hasIKConstraint(pose_bone): - pose_bone.IKRetarget = True - else: - pose_bone.IKRetarget = False - -updateIKRetarget() - - -class MocapPanel(bpy.types.Panel): - # Motion capture retargeting panel - bl_label = "Mocap tools" - bl_space_type = "PROPERTIES" - bl_region_type = "WINDOW" - bl_context = "object" - - def draw(self, context): - self.layout.label("Preprocessing") - row = self.layout.row(align=True) - row.alignment = 'EXPAND' - row.operator("mocap.samples", text='Samples to Beziers') - row.operator("mocap.denoise", text='Clean noise') - row.operator("mocap.rotate_fix", text='Fix BVH Axis Orientation') - row.operator("mocap.scale_fix", text='Auto scale Performer') - row2 = self.layout.row(align=True) - row2.operator("mocap.looper", text='Loop animation') - row2.operator("mocap.limitdof", text='Constrain Rig') - row2.operator("mocap.removelimitdof", text='Unconstrain Rig') - self.layout.label("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: - self.layout.label("Select performer rig and target rig (as active)") - else: - self.layout.operator("mocap.guessmapping", text="Guess Hiearchy Mapping") - row3 = self.layout.row(align=True) - column1 = row3.column(align=True) - column1.label("Performer Rig") - column2 = row3.column(align=True) - column2.label("Enduser Rig") - performer_obj = performer_obj[0] - if performer_obj.data and enduser_obj.data: - if performer_obj.data.name in bpy.data.armatures and enduser_obj.data.name in bpy.data.armatures: - perf = performer_obj.data - enduser_arm = enduser_obj.data - perf_pose_bones = enduser_obj.pose.bones - for bone in perf.bones: - row = self.layout.row() - row.prop(data=bone, property='foot', text='', icon='POSE_DATA') - row.label(bone.name) - row.prop_search(bone, "map", enduser_arm, "bones") - row.operator("mocap.selectmap", text='', icon='CURSOR').perf_bone = bone.name - label_mod = "FK" - if bone.map: - pose_bone = perf_pose_bones[bone.map] - if pose_bone.is_in_ik_chain: - label_mod = "ik chain" - if hasIKConstraint(pose_bone): - label_mod = "ik end" - row.prop(data=bone, property='twistFix', text='', icon='RNA') - row.prop(pose_bone, 'IKRetarget') - row.label(label_mod) - else: - row.label(" ") - row.label(" ") - mapRow = self.layout.row() - mapRow.operator("mocap.savemapping", text='Save mapping') - mapRow.operator("mocap.loadmapping", text='Load mapping') - self.layout.prop(data=performer_obj.animation_data.action, property='name', text='Action Name') - self.layout.prop(enduser_arm, "advancedRetarget", text='Advanced Retarget') - self.layout.prop(enduser_arm, "frameStep") - self.layout.operator("mocap.retarget", text='RETARGET!') - - -class MocapConstraintsPanel(bpy.types.Panel): - #Motion capture constraints panel - bl_label = "Mocap Fixes" - bl_space_type = "PROPERTIES" - bl_region_type = "WINDOW" - bl_context = "object" - - def draw(self, context): - layout = self.layout - if context.active_object: - if context.active_object.data: - if context.active_object.data.name in bpy.data.armatures: - enduser_obj = context.active_object - enduser_arm = enduser_obj.data - layout.operator_menu_enum("mocap.addmocapfix", "type") - layout.operator("mocap.updateconstraints", text='Update Fixes') - bakeRow = layout.row() - bakeRow.operator("mocap.bakeconstraints", text='Bake Fixes') - bakeRow.operator("mocap.unbakeconstraints", text='Unbake Fixes') - layout.separator() - for i, m_constraint in enumerate(enduser_arm.mocap_constraints): - box = layout.box() - headerRow = box.row() - headerRow.prop(m_constraint, 'show_expanded', text='', icon='TRIA_DOWN' if m_constraint.show_expanded else 'TRIA_RIGHT', emboss=False) - headerRow.prop(m_constraint, 'type', text='') - headerRow.prop(m_constraint, 'name', text='') - headerRow.prop(m_constraint, 'active', icon='MUTE_IPO_ON' if not m_constraint.active else'MUTE_IPO_OFF', text='', emboss=False) - headerRow.operator("mocap.removeconstraint", text="", icon='X', emboss=False).constraint = i - if m_constraint.show_expanded: - box.separator() - box.prop_search(m_constraint, 'constrained_bone', enduser_obj.pose, "bones", icon='BONE_DATA') - if m_constraint.type == "distance" or m_constraint.type == "point": - box.prop_search(m_constraint, 'constrained_boneB', enduser_obj.pose, "bones", icon='CONSTRAINT_BONE') - frameRow = box.row() - frameRow.label("Frame Range:") - frameRow.prop(m_constraint, 's_frame') - frameRow.prop(m_constraint, 'e_frame') - smoothRow = box.row() - smoothRow.label("Smoothing:") - smoothRow.prop(m_constraint, 'smooth_in') - smoothRow.prop(m_constraint, 'smooth_out') - targetRow = box.row() - targetLabelCol = targetRow.column() - targetLabelCol.label("Target settings:") - targetPropCol = targetRow.column() - if m_constraint.type == "floor": - targetPropCol.prop_search(m_constraint, 'targetMesh', bpy.data, "objects") - if m_constraint.type == "point" or m_constraint.type == "freeze": - box.prop(m_constraint, 'targetSpace') - if m_constraint.type == "point": - targetPropCol.prop(m_constraint, 'targetPoint') - if m_constraint.type == "distance" or m_constraint.type == "floor": - targetPropCol.prop(m_constraint, 'targetDist') - layout.separator() - - -class ExtraToolsPanel(bpy.types.Panel): - # Motion capture retargeting panel - bl_label = "Extra Mocap Tools" - bl_space_type = "PROPERTIES" - bl_region_type = "WINDOW" - bl_context = "object" - - def draw(self, context): - layout = self.layout - layout.operator('mocap.pathediting', text="Follow Path") - layout.label("Animation Stitching") - activeIsArmature = isinstance(context.active_object.data, bpy.types.Armature) - if activeIsArmature: - enduser_arm = context.active_object.data - layout.label("Retargeted Animations:") - layout.prop_search(enduser_arm, "active_mocap", enduser_arm, "mocapNLATracks") - settings = enduser_arm.stitch_settings - layout.prop_search(settings, "first_action", enduser_arm, "mocapNLATracks") - layout.prop_search(settings, "second_action", enduser_arm, "mocapNLATracks") - layout.prop(settings, "blend_frame") - layout.prop(settings, "blend_amount") - layout.prop(settings, "second_offset") - layout.prop_search(settings, "stick_bone", context.active_object.pose, "bones") - layout.operator('mocap.animstitchguess', text="Guess Settings") - layout.operator('mocap.animstitch', text="Stitch Animations") - - -class OBJECT_OT_RetargetButton(bpy.types.Operator): - #Retargeting operator. Assumes selected and active armatures, where the performer (the selected one) - # has an action for retargeting - '''Retarget animation from selected armature to active armature ''' - bl_idname = "mocap.retarget" - bl_label = "Retargets active action from Performer to Enduser" - bl_options = {'REGISTER', 'UNDO'} - - def execute(self, context): - scene = context.scene - s_frame = scene.frame_start - e_frame = scene.frame_end - 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] - s_frame, e_frame = performer_obj.animation_data.action.frame_range - s_frame = int(s_frame) - e_frame = int(e_frame) - if retarget.isRigAdvanced(enduser_obj) and not enduser_obj.data.advancedRetarget: - print("Recommended to use Advanced Retargeting method") - enduser_obj.data.advancedRetarget = True - else: - retarget.totalRetarget(performer_obj, enduser_obj, scene, s_frame, e_frame) - 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) and performer_obj[0].animation_data - else: - return False - - -class OBJECT_OT_SaveMappingButton(bpy.types.Operator): - #Operator for saving mapping to enduser armature - '''Save mapping to active armature (for future retargets) ''' - 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_LoadMappingButton(bpy.types.Operator): - '''Load saved mapping from active armature''' - #Operator for loading mapping to enduser armature - bl_idname = "mocap.loadmapping" - bl_label = "Loads 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.loadMapping(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_SelectMapBoneButton(bpy.types.Operator): - #Operator for setting selected bone in enduser armature to the performer mapping - '''Select a bone for faster mapping''' - bl_idname = "mocap.selectmap" - bl_label = "Select a bone for faster mapping" - perf_bone = bpy.props.StringProperty() - - 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] - selectedBone = "" - for bone in enduser_obj.data.bones: - boneVis = bone.layers - for i in range(32): - if boneVis[i] and enduser_obj.data.layers[i]: - if bone.select: - selectedBone = bone.name - break - performer_obj.data.bones[self.perf_bone].map = selectedBone - 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): - #Operator to convert samples to beziers on the selected object - '''Convert active armature's sampled keyframed to beziers''' - bl_idname = "mocap.samples" - bl_label = "Converts samples / simplifies keyframes to beziers" - - def execute(self, context): - mocap_tools.fcurves_simplify(context, context.active_object) - return {"FINISHED"} - - @classmethod - def poll(cls, context): - return context.active_object.animation_data - - -class OBJECT_OT_LooperButton(bpy.types.Operator): - #Operator to trim fcurves which contain a few loops to a single one on the selected object - '''Trim active armature's animation to a single cycle, given a cyclic animation (such as a walk cycle)''' - bl_idname = "mocap.looper" - bl_label = "loops animation / sampled mocap data" - - def execute(self, context): - mocap_tools.autoloop_anim() - return {"FINISHED"} - - @classmethod - def poll(cls, context): - return context.active_object.animation_data - - -class OBJECT_OT_DenoiseButton(bpy.types.Operator): - #Operator to denoise impluse noise on the active object's fcurves - '''Denoise active armature's animation. Good for dealing with 'bad' frames inherent in mocap animation''' - bl_idname = "mocap.denoise" - bl_label = "Denoises sampled mocap data " - - def execute(self, context): - mocap_tools.denoise_median() - 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): - #Operator to analyze performer armature and apply rotation constraints on the enduser armature - '''Create limit constraints on the active armature from the selected armature's animation's range of motion''' - bl_idname = "mocap.limitdof" - bl_label = "Analyzes animations Max/Min DOF and adds hard/soft constraints" - - def execute(self, context): - performer_obj = [obj for obj in context.selected_objects if obj != context.active_object][0] - mocap_tools.limit_dof(context, performer_obj, context.active_object) - 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_RemoveLimitDOFButton(bpy.types.Operator): - #Removes constraints created by above operator - '''Removes previously created limit constraints on the active armature''' - bl_idname = "mocap.removelimitdof" - bl_label = "Removes previously created limit constraints on the active armature" - - def execute(self, context): - mocap_tools.limit_dof_toggle_off(context, context.active_object) - return {"FINISHED"} - - @classmethod - def poll(cls, context): - activeIsArmature = False - if context.active_object: - activeIsArmature = isinstance(context.active_object.data, bpy.types.Armature) - return activeIsArmature - - -class OBJECT_OT_RotateFixArmature(bpy.types.Operator): - #Operator to fix common imported Mocap data issue of wrong axis system on active object - '''Realign the active armature's axis system to match Blender (Commonly needed after bvh import)''' - bl_idname = "mocap.rotate_fix" - bl_label = "Rotates selected armature 90 degrees (fix for bvh import)" - - def execute(self, context): - mocap_tools.rotate_fix_armature(context.active_object.data) - return {"FINISHED"} - - @classmethod - def poll(cls, context): - if context.active_object: - return isinstance(context.active_object.data, bpy.types.Armature) - - -class OBJECT_OT_ScaleFixArmature(bpy.types.Operator): - #Operator to scale down the selected armature to match the active one - '''Rescale selected armature to match the active animation, for convienence''' - bl_idname = "mocap.scale_fix" - bl_label = "Scales performer armature to match target armature" - - 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] - mocap_tools.scale_fix_armature(performer_obj, enduser_obj) - 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 MOCAP_OT_AddMocapFix(bpy.types.Operator): - #Operator to add a post-retarget fix - '''Add a post-retarget fix - useful for fixing certain artifacts following the retarget''' - bl_idname = "mocap.addmocapfix" - bl_label = "Add Mocap Fix to target armature" - type = bpy.props.EnumProperty(name="Type of Fix", - 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 fix") - - def execute(self, context): - enduser_obj = bpy.context.active_object - enduser_arm = enduser_obj.data - new_mcon = enduser_arm.mocap_constraints.add() - new_mcon.type = self.type - 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): - #Operator to remove a post-retarget fix - '''Remove this post-retarget fix''' - bl_idname = "mocap.removeconstraint" - bl_label = "Removes fixes from target armature" - constraint = bpy.props.IntProperty() - - def execute(self, context): - enduser_obj = bpy.context.active_object - enduser_arm = enduser_obj.data - 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"} - - @classmethod - def poll(cls, context): - if context.active_object: - return isinstance(context.active_object.data, bpy.types.Armature) - - -class OBJECT_OT_BakeMocapConstraints(bpy.types.Operator): - #Operator to bake all post-retarget fixes - '''Bake all post-retarget fixes to the Retarget Fixes NLA Track''' - bl_idname = "mocap.bakeconstraints" - bl_label = "Bake all fixes to target armature" - - def execute(self, context): - bakeConstraints(context) - return {"FINISHED"} - - @classmethod - def poll(cls, context): - if context.active_object: - return isinstance(context.active_object.data, bpy.types.Armature) - - -class OBJECT_OT_UnbakeMocapConstraints(bpy.types.Operator): - #Operator to unbake all post-retarget fixes - '''Unbake all post-retarget fixes - removes the baked data from the Retarget Fixes NLA Track''' - bl_idname = "mocap.unbakeconstraints" - bl_label = "Unbake all fixes to target armature" - - def execute(self, context): - unbakeConstraints(context) - return {"FINISHED"} - - @classmethod - def poll(cls, context): - if context.active_object: - return isinstance(context.active_object.data, bpy.types.Armature) - - -class OBJECT_OT_UpdateMocapConstraints(bpy.types.Operator): - #Operator to update all post-retarget fixes, similar to update dependencies on drivers - #Needed because python properties lack certain callbacks and some fixes take a while to recalculate. - '''Updates all post-retarget fixes - needed after changes to armature object or pose''' - bl_idname = "mocap.updateconstraints" - bl_label = "Updates all fixes to target armature - neccesary to take under consideration changes to armature object or pose" - - def execute(self, context): - updateConstraints(context.active_object, context) - return {"FINISHED"} - - @classmethod - def poll(cls, context): - if context.active_object: - return isinstance(context.active_object.data, bpy.types.Armature) - - -class OBJECT_OT_GuessHierachyMapping(bpy.types.Operator): - #Operator which calls heurisitic function to guess mapping between 2 armatures - '''Attemps to auto figure out hierarchy mapping''' - bl_idname = "mocap.guessmapping" - bl_label = "Attemps to auto figure out hierarchy mapping" - - 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] - mocap_tools.guessMapping(performer_obj, enduser_obj) - 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_PathEditing(bpy.types.Operator): - #Operator which calls path editing function, making active object follow the selected curve. - '''Sets active object (stride object) to follow the selected curve''' - bl_idname = "mocap.pathediting" - bl_label = "Sets active object (stride object) to follow the selected curve" - - def execute(self, context): - path = [obj for obj in context.selected_objects if obj != context.active_object][0] - mocap_tools.path_editing(context, context.active_object, path) - return {"FINISHED"} - - @classmethod - def poll(cls, context): - if context.active_object: - selected_objs = [obj for obj in context.selected_objects if obj != context.active_object and isinstance(obj.data, bpy.types.Curve)] - return selected_objs - else: - return False - - -class OBJECT_OT_AnimationStitchingButton(bpy.types.Operator): - #Operator which calls stitching function, combining 2 animations onto the NLA. - '''Stitches two defined animations into a single one via alignment of NLA Tracks''' - bl_idname = "mocap.animstitch" - bl_label = "Stitches two defined animations into a single one via alignment of NLA Tracks" - - def execute(self, context): - mocap_tools.anim_stitch(context, context.active_object) - return {"FINISHED"} - - @classmethod - def poll(cls, context): - activeIsArmature = False - if context.active_object: - activeIsArmature = isinstance(context.active_object.data, bpy.types.Armature) - if activeIsArmature: - stitch_settings = context.active_object.data.stitch_settings - return (stitch_settings.first_action and stitch_settings.second_action) - return False - - -class OBJECT_OT_GuessAnimationStitchingButton(bpy.types.Operator): - #Operator which calls stitching function heuristic, setting good values for above operator. - '''Guesses the stitch frame and second offset for animation stitch''' - bl_idname = "mocap.animstitchguess" - bl_label = "Guesses the stitch frame and second offset for animation stitch" - - def execute(self, context): - mocap_tools.guess_anim_stitch(context, context.active_object) - return {"FINISHED"} - - @classmethod - def poll(cls, context): - activeIsArmature = False - if context.active_object: - activeIsArmature = isinstance(context.active_object.data, bpy.types.Armature) - if activeIsArmature: - stitch_settings = context.active_object.data.stitch_settings - return (stitch_settings.first_action and stitch_settings.second_action) - return False - - -def register(): - bpy.utils.register_module(__name__) - - -def unregister(): - bpy.utils.unregister_module(__name__) - -if __name__ == "__main__": - register() -- cgit v1.2.3 From 5bac37f6d4d2e8d584ae0ec6bafd2808c47fbb25 Mon Sep 17 00:00:00 2001 From: Joerg Mueller Date: Mon, 29 Aug 2011 15:01:55 +0000 Subject: * Reverting Titlecard commit r37537 * Reverting update recent files commit r37155 * Turning reference counts into unsigned ints * Minor things --- release/scripts/startup/bl_ui/space_sequencer.py | 14 +++----------- release/scripts/startup/bl_ui/space_userpref.py | 1 - 2 files changed, 3 insertions(+), 12 deletions(-) (limited to 'release/scripts') diff --git a/release/scripts/startup/bl_ui/space_sequencer.py b/release/scripts/startup/bl_ui/space_sequencer.py index cebe78ff627..168a025c407 100644 --- a/release/scripts/startup/bl_ui/space_sequencer.py +++ b/release/scripts/startup/bl_ui/space_sequencer.py @@ -225,7 +225,6 @@ class SEQUENCER_MT_add_effect(Menu): layout.operator("sequencer.effect_strip_add", text="Speed Control").type = 'SPEED' layout.operator("sequencer.effect_strip_add", text="Multicam Selector").type = 'MULTICAM' layout.operator("sequencer.effect_strip_add", text="Adjustment Layer").type = 'ADJUSTMENT' - layout.operator("sequencer.effect_strip_add", text="Title Card").type = 'TITLE_CARD' class SEQUENCER_MT_strip(Menu): @@ -407,7 +406,7 @@ class SEQUENCER_PT_effect(SequencerButtonsPanel, Panel): 'CROSS', 'GAMMA_CROSS', 'MULTIPLY', 'OVER_DROP', 'PLUGIN', 'WIPE', 'GLOW', 'TRANSFORM', 'COLOR', 'SPEED', - 'MULTICAM', 'ADJUSTMENT', 'TITLE_CARD'} + 'MULTICAM', 'ADJUSTMENT'} def draw(self, context): layout = self.layout @@ -475,11 +474,6 @@ class SEQUENCER_PT_effect(SequencerButtonsPanel, Panel): row.label("Cut To") for i in range(1, strip.channel): row.operator("sequencer.cut_multicam", text=str(i)).camera = i - elif strip.type == "TITLE_CARD": - layout.prop(strip, "title") - layout.prop(strip, "subtitle") - layout.prop(strip, "color_foreground") - layout.prop(strip, "color_background") col = layout.column(align=True) if strip.type == 'SPEED': @@ -551,8 +545,7 @@ class SEQUENCER_PT_input(SequencerButtonsPanel, Panel): 'CROSS', 'GAMMA_CROSS', 'MULTIPLY', 'OVER_DROP', 'PLUGIN', 'WIPE', 'GLOW', 'TRANSFORM', 'COLOR', - 'MULTICAM', 'SPEED', 'ADJUSTMENT', - 'TITLE_CARD'} + 'MULTICAM', 'SPEED', 'ADJUSTMENT'} def draw(self, context): layout = self.layout @@ -713,8 +706,7 @@ class SEQUENCER_PT_filter(SequencerButtonsPanel, Panel): 'CROSS', 'GAMMA_CROSS', 'MULTIPLY', 'OVER_DROP', 'PLUGIN', 'WIPE', 'GLOW', 'TRANSFORM', 'COLOR', - 'MULTICAM', 'SPEED', 'ADJUSTMENT', - 'TITLE_CARD'} + 'MULTICAM', 'SPEED', 'ADJUSTMENT'} def draw(self, context): layout = self.layout diff --git a/release/scripts/startup/bl_ui/space_userpref.py b/release/scripts/startup/bl_ui/space_userpref.py index 79565030e56..13edc3471d2 100644 --- a/release/scripts/startup/bl_ui/space_userpref.py +++ b/release/scripts/startup/bl_ui/space_userpref.py @@ -746,7 +746,6 @@ class USERPREF_PT_file(Panel): col.prop(paths, "save_version") col.prop(paths, "recent_files") - col.prop(paths, "use_update_recent_files_on_load") col.prop(paths, "use_save_preview_images") col.label(text="Auto Save:") col.prop(paths, "use_auto_save_temporary_files") -- cgit v1.2.3 From d049a722fea3d150fbfad06ffdbbb5c150717134 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Tue, 30 Aug 2011 04:13:48 +0000 Subject: fix [#28413] bpy.ops.nla.bake can't bake from frame 0 --- release/scripts/startup/bl_operators/nla.py | 33 ++++++++++++++++++++--------- 1 file changed, 23 insertions(+), 10 deletions(-) (limited to 'release/scripts') diff --git a/release/scripts/startup/bl_operators/nla.py b/release/scripts/startup/bl_operators/nla.py index 714b889da26..cf595b1adf4 100644 --- a/release/scripts/startup/bl_operators/nla.py +++ b/release/scripts/startup/bl_operators/nla.py @@ -200,19 +200,32 @@ class BakeAction(Operator): bl_label = "Bake Action" bl_options = {'REGISTER', 'UNDO'} - frame_start = IntProperty(name="Start Frame", + frame_start = IntProperty( + name="Start Frame", description="Start frame for baking", - default=1, min=1, max=300000) - frame_end = IntProperty(name="End Frame", + min=0, max=300000, + default=1, + ) + frame_end = IntProperty( + name="End Frame", description="End frame for baking", - default=250, min=1, max=300000) - step = IntProperty(name="Frame Step", + min=1, max=300000, + default=250, + ) + step = IntProperty( + name="Frame Step", description="Frame Step", - default=1, min=1, max=120) - only_selected = BoolProperty(name="Only Selected", - default=True) - clear_consraints = BoolProperty(name="Clear Constraints", - default=False) + min=1, max=120, + default=1, + ) + only_selected = BoolProperty( + name="Only Selected", + default=True, + ) + clear_consraints = BoolProperty( + name="Clear Constraints", + default=False, + ) bake_types = EnumProperty( name="Bake Data", options={'ENUM_FLAG'}, -- cgit v1.2.3 From 6c9ee34dd8acba35808847d3669798c6d3459b9c Mon Sep 17 00:00:00 2001 From: Thomas Dinges Date: Tue, 30 Aug 2011 10:44:02 +0000 Subject: 2.5 UI Files: * Code cleanup after Pepper merge. --- release/scripts/startup/bl_ui/properties_data_armature.py | 3 +-- release/scripts/startup/bl_ui/properties_data_speaker.py | 8 ++------ release/scripts/startup/bl_ui/properties_object_constraint.py | 5 +---- release/scripts/startup/bl_ui/properties_render.py | 9 +++------ release/scripts/startup/bl_ui/properties_scene.py | 2 -- 5 files changed, 7 insertions(+), 20 deletions(-) (limited to 'release/scripts') diff --git a/release/scripts/startup/bl_ui/properties_data_armature.py b/release/scripts/startup/bl_ui/properties_data_armature.py index 9a76ed81530..b2c09469067 100644 --- a/release/scripts/startup/bl_ui/properties_data_armature.py +++ b/release/scripts/startup/bl_ui/properties_data_armature.py @@ -72,8 +72,7 @@ class DATA_PT_skeleton(ArmatureButtonsPanel, Panel): flow.prop(arm, "use_deform_preserve_volume", text="Quaternion") if context.scene.render.engine == "BLENDER_GAME": - col = layout.column() - col.prop(arm, "vert_deformer") + layout.row().prop(arm, "vert_deformer", expand=True, text="") class DATA_PT_display(ArmatureButtonsPanel, Panel): bl_label = "Display" diff --git a/release/scripts/startup/bl_ui/properties_data_speaker.py b/release/scripts/startup/bl_ui/properties_data_speaker.py index fe9f798af0c..657c0fe652a 100644 --- a/release/scripts/startup/bl_ui/properties_data_speaker.py +++ b/release/scripts/startup/bl_ui/properties_data_speaker.py @@ -66,10 +66,7 @@ class DATA_PT_speaker(DataButtonsPanel, bpy.types.Panel): split.template_ID(speaker, "sound", open="sound.open_mono") split.prop(speaker, "muted") - split = layout.split() - - row = split.row() - + row = layout.row() row.prop(speaker, "volume") row.prop(speaker, "pitch") @@ -84,15 +81,14 @@ class DATA_PT_distance(DataButtonsPanel, bpy.types.Panel): speaker = context.speaker split = layout.split() + col = split.column() - col.label("Volume:") col.prop(speaker, "volume_min", text="Minimum") col.prop(speaker, "volume_max", text="Maximum") col.prop(speaker, "attenuation") col = split.column() - col.label("Distance:") col.prop(speaker, "distance_max", text="Maximum") col.prop(speaker, "distance_reference", text="Reference") diff --git a/release/scripts/startup/bl_ui/properties_object_constraint.py b/release/scripts/startup/bl_ui/properties_object_constraint.py index c74a0000499..ba237e74fb7 100644 --- a/release/scripts/startup/bl_ui/properties_object_constraint.py +++ b/release/scripts/startup/bl_ui/properties_object_constraint.py @@ -235,7 +235,6 @@ class ConstraintButtonsPanel(): row.label() def LIMIT_ROTATION(self, context, layout, con): - split = layout.split() col = split.column(align=True) @@ -259,9 +258,7 @@ class ConstraintButtonsPanel(): sub.prop(con, "min_z", text="Min") sub.prop(con, "max_z", text="Max") - row = layout.row() - row.prop(con, "use_transform_limit") - row.label() + layout.prop(con, "use_transform_limit") row = layout.row() row.label(text="Convert:") diff --git a/release/scripts/startup/bl_ui/properties_render.py b/release/scripts/startup/bl_ui/properties_render.py index c906013e094..395cfc6934e 100644 --- a/release/scripts/startup/bl_ui/properties_render.py +++ b/release/scripts/startup/bl_ui/properties_render.py @@ -592,12 +592,9 @@ class RENDER_PT_encoding(RenderButtonsPanel, Panel): if rd.ffmpeg_format not in {'MP3'}: layout.prop(rd, "ffmpeg_audio_codec", text="Audio Codec") - split = layout.split() - - col = split.column() - col.prop(rd, "ffmpeg_audio_bitrate") - col = split.column() - col.prop(rd, "ffmpeg_audio_volume", slider=True) + row = layout.row() + row.prop(rd, "ffmpeg_audio_bitrate") + row.prop(rd, "ffmpeg_audio_volume", slider=True) class RENDER_PT_bake(RenderButtonsPanel, Panel): diff --git a/release/scripts/startup/bl_ui/properties_scene.py b/release/scripts/startup/bl_ui/properties_scene.py index fd7fc8ed462..66f967bb6e1 100644 --- a/release/scripts/startup/bl_ui/properties_scene.py +++ b/release/scripts/startup/bl_ui/properties_scene.py @@ -59,14 +59,12 @@ class SCENE_PT_audio(SceneButtonsPanel, Panel): split = layout.split() col = split.column() - col.label("Listener:") col.prop(scene, "audio_distance_model", text="") col.prop(scene, "audio_doppler_speed", text="Speed") col.prop(scene, "audio_doppler_factor", text="Doppler") col = split.column() - col.label("Format:") col.prop(rd, "ffmpeg_audio_channels", text="") col.prop(rd, "ffmpeg_audio_mixrate", text="Rate") -- cgit v1.2.3 From b20c9b0ba368d5685d3c996572780befe852b889 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Tue, 30 Aug 2011 10:49:58 +0000 Subject: minor edits, pep8 - also correct float -> double promotion for blf. --- release/scripts/modules/bpy_types.py | 2 +- release/scripts/startup/bl_operators/nla.py | 4 +-- release/scripts/startup/bl_operators/object.py | 27 ++++++++++-------- .../startup/bl_ui/properties_data_armature.py | 1 + .../startup/bl_ui/properties_object_constraint.py | 1 - .../scripts/startup/bl_ui/properties_texture.py | 8 +++--- release/scripts/startup/bl_ui/space_dopesheet.py | 2 +- release/scripts/startup/bl_ui/space_nla.py | 4 +-- release/scripts/startup/bl_ui/space_sequencer.py | 1 - release/scripts/startup/bl_ui/space_view3d.py | 5 ++-- release/scripts/startup/keyingsets_builtins.py | 33 +++++++++++----------- 11 files changed, 46 insertions(+), 42 deletions(-) (limited to 'release/scripts') diff --git a/release/scripts/modules/bpy_types.py b/release/scripts/modules/bpy_types.py index e4a0d30efcf..101416f4943 100644 --- a/release/scripts/modules/bpy_types.py +++ b/release/scripts/modules/bpy_types.py @@ -413,7 +413,7 @@ TypeMap = {} class Sound(bpy_types.ID): __slots__ = () - + @property def factory(self): """The aud.Factory object of the sound.""" diff --git a/release/scripts/startup/bl_operators/nla.py b/release/scripts/startup/bl_operators/nla.py index 085cd75f9f9..c764f7d62f1 100644 --- a/release/scripts/startup/bl_operators/nla.py +++ b/release/scripts/startup/bl_operators/nla.py @@ -269,10 +269,8 @@ class BakeAction(Operator): wm = context.window_manager return wm.invoke_props_dialog(self) -################################# - -class ClearUselessActions(bpy.types.Operator): +class ClearUselessActions(Operator): '''Mark actions with no F-Curves for deletion after save+reload of file preserving "action libraries"''' bl_idname = "anim.clear_useless_actions" bl_label = "Clear Useless Actions" diff --git a/release/scripts/startup/bl_operators/object.py b/release/scripts/startup/bl_operators/object.py index 88f863b8e55..6c9f27afaa5 100644 --- a/release/scripts/startup/bl_operators/object.py +++ b/release/scripts/startup/bl_operators/object.py @@ -686,26 +686,30 @@ class ClearAllRestrictRender(Operator): obj.hide_render = False return {'FINISHED'} -class TransformsToDeltasAnim(bpy.types.Operator): + +class TransformsToDeltasAnim(Operator): '''Convert object animation for normal transforms to delta transforms''' bl_idname = "object.anim_transforms_to_deltas" bl_label = "Animated Transforms to Deltas" bl_options = {'REGISTER', 'UNDO'} - + @classmethod def poll(cls, context): obs = context.selected_editable_objects return (obs is not None) - + def execute(self, context): for obj in context.selected_editable_objects: # get animation data adt = obj.animation_data if (adt is None) or (adt.action is None): - self.report({'WARNING'}, "No animation data to convert on object: " + obj.name) + self.report({'WARNING'}, + "No animation data to convert on object: %r" % + obj.name) continue - - # if F-Curve uses standard transform path, just append "delta_" to this path + + # if F-Curve uses standard transform path + # just append "delta_" to this path for fcu in adt.action.fcurves: if fcu.data_path == "location": fcu.data_path = "delta_location" @@ -716,13 +720,14 @@ class TransformsToDeltasAnim(bpy.types.Operator): elif fcu.data_path == "rotation_quaternion": fcu.data_path = "delta_rotation_quaternion" obj.rotation_quaternion.identity() - #elif fcu.data_path == "rotation_axis_angle": # XXX: currently not implemented - # fcu.data_path = "delta_rotation_axis_angle" + # XXX: currently not implemented + # elif fcu.data_path == "rotation_axis_angle": + # fcu.data_path = "delta_rotation_axis_angle" elif fcu.data_path == "scale": fcu.data_path = "delta_scale" - obj.scale = (1, 1, 1) - + obj.scale = 1.0, 1.0, 1.0 + # hack: force animsys flush by changing frame, so that deltas get run context.scene.frame_set(context.scene.frame_current) - + return {'FINISHED'} diff --git a/release/scripts/startup/bl_ui/properties_data_armature.py b/release/scripts/startup/bl_ui/properties_data_armature.py index b2c09469067..3d1903f4cbc 100644 --- a/release/scripts/startup/bl_ui/properties_data_armature.py +++ b/release/scripts/startup/bl_ui/properties_data_armature.py @@ -74,6 +74,7 @@ class DATA_PT_skeleton(ArmatureButtonsPanel, Panel): if context.scene.render.engine == "BLENDER_GAME": layout.row().prop(arm, "vert_deformer", expand=True, text="") + class DATA_PT_display(ArmatureButtonsPanel, Panel): bl_label = "Display" diff --git a/release/scripts/startup/bl_ui/properties_object_constraint.py b/release/scripts/startup/bl_ui/properties_object_constraint.py index ba237e74fb7..05fac2026a0 100644 --- a/release/scripts/startup/bl_ui/properties_object_constraint.py +++ b/release/scripts/startup/bl_ui/properties_object_constraint.py @@ -478,7 +478,6 @@ class ConstraintButtonsPanel(): row.prop(con, "use_transform_limit") row.label() - def STRETCH_TO(self, context, layout, con): self.target_template(layout, con) diff --git a/release/scripts/startup/bl_ui/properties_texture.py b/release/scripts/startup/bl_ui/properties_texture.py index 0172fbcbadd..34f5a948ee7 100644 --- a/release/scripts/startup/bl_ui/properties_texture.py +++ b/release/scripts/startup/bl_ui/properties_texture.py @@ -414,7 +414,7 @@ class TEXTURE_PT_image_sampling(TextureTypePanel, Panel): row = col.row() row.active = tex.use_normal_map row.prop(slot, "normal_map_space", text="") - + row = col.row() row.active = not tex.use_normal_map row.prop(tex, "use_derivative_map") @@ -1029,14 +1029,14 @@ class TEXTURE_PT_influence(TextureSlotPanel, Panel): # only show bump settings if activated but not for normalmap images row = layout.row() - + sub = row.row() sub.active = (tex.use_map_normal or tex.use_map_warp) and not (tex.texture.type == 'IMAGE' and (tex.texture.use_normal_map or tex.texture.use_derivative_map)) sub.prop(tex, "bump_method", text="Method") - # the space setting is supported for: derivmaps + bumpmaps (DEFAULT,BEST_QUALITY), not for normalmaps + # the space setting is supported for: derivmaps + bumpmaps (DEFAULT,BEST_QUALITY), not for normalmaps sub = row.row() - sub.active = (tex.use_map_normal or tex.use_map_warp) and not (tex.texture.type == 'IMAGE' and tex.texture.use_normal_map) and ((tex.bump_method in {'BUMP_DEFAULT', 'BUMP_BEST_QUALITY'}) or (tex.texture.type == 'IMAGE' and tex.texture.use_derivative_map)) + sub.active = (tex.use_map_normal or tex.use_map_warp) and not (tex.texture.type == 'IMAGE' and tex.texture.use_normal_map) and ((tex.bump_method in {'BUMP_DEFAULT', 'BUMP_BEST_QUALITY'}) or (tex.texture.type == 'IMAGE' and tex.texture.use_derivative_map)) sub.prop(tex, "bump_objectspace", text="Space") diff --git a/release/scripts/startup/bl_ui/space_dopesheet.py b/release/scripts/startup/bl_ui/space_dopesheet.py index 5ed79f45fbc..90dcc99e6d7 100644 --- a/release/scripts/startup/bl_ui/space_dopesheet.py +++ b/release/scripts/startup/bl_ui/space_dopesheet.py @@ -36,7 +36,7 @@ def dopesheet_filter(layout, context, genericFiltersOnly=False): if is_nla: row.prop(dopesheet, "show_missing_nla", text="") - + if not genericFiltersOnly: if bpy.data.groups: row = layout.row(align=True) diff --git a/release/scripts/startup/bl_ui/space_nla.py b/release/scripts/startup/bl_ui/space_nla.py index 1d4b7c6828f..ffead81c507 100644 --- a/release/scripts/startup/bl_ui/space_nla.py +++ b/release/scripts/startup/bl_ui/space_nla.py @@ -69,11 +69,11 @@ class NLA_MT_view(Menu): layout.separator() layout.operator("anim.previewrange_set") layout.operator("anim.previewrange_clear") - + layout.separator() layout.operator("nla.view_all") layout.operator("nla.view_selected") - + layout.separator() layout.operator("screen.area_dupli") layout.operator("screen.screen_full_area") diff --git a/release/scripts/startup/bl_ui/space_sequencer.py b/release/scripts/startup/bl_ui/space_sequencer.py index 8592cc2fcc0..36f606da635 100644 --- a/release/scripts/startup/bl_ui/space_sequencer.py +++ b/release/scripts/startup/bl_ui/space_sequencer.py @@ -804,7 +804,6 @@ class SEQUENCER_PT_proxy(SequencerButtonsPanel, Panel): col.prop(strip.proxy, "timecode") - class SEQUENCER_PT_preview(SequencerButtonsPanel_Output, Panel): bl_label = "Scene Preview/Render" bl_space_type = 'SEQUENCE_EDITOR' diff --git a/release/scripts/startup/bl_ui/space_view3d.py b/release/scripts/startup/bl_ui/space_view3d.py index 64cbc4c3d98..9f96df1eb66 100644 --- a/release/scripts/startup/bl_ui/space_view3d.py +++ b/release/scripts/startup/bl_ui/space_view3d.py @@ -179,9 +179,9 @@ class VIEW3D_MT_transform(Menu): layout.operator("object.randomize_transform") layout.operator("object.align") - + layout.separator() - + layout.operator("object.anim_transforms_to_deltas") @@ -1273,6 +1273,7 @@ class VIEW3D_MT_pose_transform(Menu): layout.operator("pose.user_transforms_clear", text="Reset unkeyed") + class VIEW3D_MT_pose_slide(Menu): bl_label = "In-Betweens" diff --git a/release/scripts/startup/keyingsets_builtins.py b/release/scripts/startup/keyingsets_builtins.py index dcc1afed74b..6b12c95e072 100644 --- a/release/scripts/startup/keyingsets_builtins.py +++ b/release/scripts/startup/keyingsets_builtins.py @@ -32,13 +32,14 @@ in lost (i.e. unkeyed) animation. import bpy import keyingsets_utils +from bpy.types import KeyingSetInfo ############################### # Built-In KeyingSets # Location -class BUILTIN_KSI_Location(bpy.types.KeyingSetInfo): +class BUILTIN_KSI_Location(KeyingSetInfo): bl_label = "Location" # poll - use predefined callback for selected bones/objects @@ -52,7 +53,7 @@ class BUILTIN_KSI_Location(bpy.types.KeyingSetInfo): # Rotation -class BUILTIN_KSI_Rotation(bpy.types.KeyingSetInfo): +class BUILTIN_KSI_Rotation(KeyingSetInfo): bl_label = "Rotation" # poll - use predefined callback for selected bones/objects @@ -66,7 +67,7 @@ class BUILTIN_KSI_Rotation(bpy.types.KeyingSetInfo): # Scale -class BUILTIN_KSI_Scaling(bpy.types.KeyingSetInfo): +class BUILTIN_KSI_Scaling(KeyingSetInfo): bl_label = "Scaling" # poll - use predefined callback for selected bones/objects @@ -82,7 +83,7 @@ class BUILTIN_KSI_Scaling(bpy.types.KeyingSetInfo): # LocRot -class BUILTIN_KSI_LocRot(bpy.types.KeyingSetInfo): +class BUILTIN_KSI_LocRot(KeyingSetInfo): bl_label = "LocRot" # poll - use predefined callback for selected bones/objects @@ -100,7 +101,7 @@ class BUILTIN_KSI_LocRot(bpy.types.KeyingSetInfo): # LocScale -class BUILTIN_KSI_LocScale(bpy.types.KeyingSetInfo): +class BUILTIN_KSI_LocScale(KeyingSetInfo): bl_label = "LocScale" # poll - use predefined callback for selected bones/objects @@ -118,7 +119,7 @@ class BUILTIN_KSI_LocScale(bpy.types.KeyingSetInfo): # LocRotScale -class BUILTIN_KSI_LocRotScale(bpy.types.KeyingSetInfo): +class BUILTIN_KSI_LocRotScale(KeyingSetInfo): bl_label = "LocRotScale" # poll - use predefined callback for selected bones/objects @@ -138,7 +139,7 @@ class BUILTIN_KSI_LocRotScale(bpy.types.KeyingSetInfo): # RotScale -class BUILTIN_KSI_RotScale(bpy.types.KeyingSetInfo): +class BUILTIN_KSI_RotScale(KeyingSetInfo): bl_label = "RotScale" # poll - use predefined callback for selected bones/objects @@ -158,7 +159,7 @@ class BUILTIN_KSI_RotScale(bpy.types.KeyingSetInfo): # Location -class BUILTIN_KSI_VisualLoc(bpy.types.KeyingSetInfo): +class BUILTIN_KSI_VisualLoc(KeyingSetInfo): bl_label = "Visual Location" bl_options = {'INSERTKEY_VISUAL'} @@ -174,7 +175,7 @@ class BUILTIN_KSI_VisualLoc(bpy.types.KeyingSetInfo): # Rotation -class BUILTIN_KSI_VisualRot(bpy.types.KeyingSetInfo): +class BUILTIN_KSI_VisualRot(KeyingSetInfo): bl_label = "Visual Rotation" bl_options = {'INSERTKEY_VISUAL'} @@ -190,7 +191,7 @@ class BUILTIN_KSI_VisualRot(bpy.types.KeyingSetInfo): # VisualLocRot -class BUILTIN_KSI_VisualLocRot(bpy.types.KeyingSetInfo): +class BUILTIN_KSI_VisualLocRot(KeyingSetInfo): bl_label = "Visual LocRot" bl_options = {'INSERTKEY_VISUAL'} @@ -212,7 +213,7 @@ class BUILTIN_KSI_VisualLocRot(bpy.types.KeyingSetInfo): # Available -class BUILTIN_KSI_Available(bpy.types.KeyingSetInfo): +class BUILTIN_KSI_Available(KeyingSetInfo): bl_label = "Available" # poll - selected objects or selected object with animation data @@ -234,7 +235,7 @@ class BUILTIN_KSI_Available(bpy.types.KeyingSetInfo): # All properties that are likely to get animated in a character rig -class BUILTIN_KSI_WholeCharacter(bpy.types.KeyingSetInfo): +class BUILTIN_KSI_WholeCharacter(KeyingSetInfo): bl_label = "Whole Character" # these prefixes should be avoided, as they are not really bones @@ -265,7 +266,7 @@ class BUILTIN_KSI_WholeCharacter(bpy.types.KeyingSetInfo): # loc, rot, scale - only include unlocked ones ksi.doLoc(ks, bone) - if bone.rotation_mode in ('QUATERNION', 'AXIS_ANGLE'): + if bone.rotation_mode in {'QUATERNION', 'AXIS_ANGLE'}: ksi.doRot4d(ks, bone) else: ksi.doRot3d(ks, bone) @@ -365,7 +366,7 @@ class BUILTIN_KSI_WholeCharacter(bpy.types.KeyingSetInfo): # Delta Location -class BUILTIN_KSI_DeltaLocation(bpy.types.KeyingSetInfo): +class BUILTIN_KSI_DeltaLocation(KeyingSetInfo): bl_label = "Delta Location" # poll - selected objects only (and only if active object in object mode) @@ -390,7 +391,7 @@ class BUILTIN_KSI_DeltaLocation(bpy.types.KeyingSetInfo): # Delta Rotation -class BUILTIN_KSI_DeltaRotation(bpy.types.KeyingSetInfo): +class BUILTIN_KSI_DeltaRotation(KeyingSetInfo): bl_label = "Delta Rotation" # poll - selected objects only (and only if active object in object mode) @@ -423,7 +424,7 @@ class BUILTIN_KSI_DeltaRotation(bpy.types.KeyingSetInfo): # Delta Scale -class BUILTIN_KSI_DeltaScale(bpy.types.KeyingSetInfo): +class BUILTIN_KSI_DeltaScale(KeyingSetInfo): bl_label = "Delta Scale" # poll - selected objects only (and only if active object in object mode) -- cgit v1.2.3 From 047e8224b14aa7836db3b067824258e49e8602db Mon Sep 17 00:00:00 2001 From: Thomas Dinges Date: Tue, 30 Aug 2011 11:31:48 +0000 Subject: Fix for my last commit. --- release/scripts/startup/bl_ui/properties_data_armature.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'release/scripts') diff --git a/release/scripts/startup/bl_ui/properties_data_armature.py b/release/scripts/startup/bl_ui/properties_data_armature.py index 3d1903f4cbc..e17064178b8 100644 --- a/release/scripts/startup/bl_ui/properties_data_armature.py +++ b/release/scripts/startup/bl_ui/properties_data_armature.py @@ -72,8 +72,7 @@ class DATA_PT_skeleton(ArmatureButtonsPanel, Panel): flow.prop(arm, "use_deform_preserve_volume", text="Quaternion") if context.scene.render.engine == "BLENDER_GAME": - layout.row().prop(arm, "vert_deformer", expand=True, text="") - + layout.row().prop(arm, "vert_deformer", expand=True) class DATA_PT_display(ArmatureButtonsPanel, Panel): bl_label = "Display" -- cgit v1.2.3 From 0de9af375b5a437ab50d1d1725195ddaa35607c9 Mon Sep 17 00:00:00 2001 From: Thomas Dinges Date: Tue, 30 Aug 2011 12:45:56 +0000 Subject: 2.5 Game UI: *Fix for clutter after pepper merge, 3 booleans in one row, is 1 too much here ;-) --- release/scripts/startup/bl_ui/properties_game.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'release/scripts') diff --git a/release/scripts/startup/bl_ui/properties_game.py b/release/scripts/startup/bl_ui/properties_game.py index e3c576e7093..161e4b10cff 100644 --- a/release/scripts/startup/bl_ui/properties_game.py +++ b/release/scripts/startup/bl_ui/properties_game.py @@ -340,10 +340,12 @@ class RENDER_PT_game_performance(RenderButtonsPanel, Panel): layout = self.layout gs = context.scene.game_settings - row = layout.row() + col = layout.column() + row = col.row() row.prop(gs, "use_frame_rate") row.prop(gs, "use_display_lists") - row.prop(gs, "restrict_animation_updates") + + col.prop(gs, "restrict_animation_updates") class RENDER_PT_game_display(RenderButtonsPanel, Panel): -- cgit v1.2.3