remove bl_operators/nla.py, move bake_action function into bpy_extras.anim_utils...
authorCampbell Barton <ideasman42@gmail.com>
Thu, 22 Sep 2011 22:51:54 +0000 (22:51 +0000)
committerCampbell Barton <ideasman42@gmail.com>
Thu, 22 Sep 2011 22:51:54 +0000 (22:51 +0000)
release/scripts/modules/bpy_extras/__init__.py
release/scripts/modules/bpy_extras/anim_utils.py [new file with mode: 0644]
release/scripts/startup/bl_operators/__init__.py
release/scripts/startup/bl_operators/anim.py
release/scripts/startup/bl_operators/nla.py [deleted file]

index d853d5f..fd653a4 100644 (file)
@@ -23,6 +23,7 @@ Utility modules assosiated with the bpy module.
 """
 
 __all__ = (
+    "anim_utils",
     "object_utils",
     "io_utils",
     "image_utils",
diff --git a/release/scripts/modules/bpy_extras/anim_utils.py b/release/scripts/modules/bpy_extras/anim_utils.py
new file mode 100644 (file)
index 0000000..9482dc3
--- /dev/null
@@ -0,0 +1,247 @@
+# ##### BEGIN GPL LICENSE BLOCK #####
+#
+#  This program is free software; you can redistribute it and/or
+#  modify it under the terms of the GNU General Public License
+#  as published by the Free Software Foundation; either version 2
+#  of the License, or (at your option) any later version.
+#
+#  This program is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+#
+#  You should have received a copy of the GNU General Public License
+#  along with this program; if not, write to the Free Software Foundation,
+#  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# ##### END GPL LICENSE BLOCK #####
+
+# <pep8-80 compliant>
+
+__all__ = (
+    "bake_action",
+    )
+
+import bpy
+
+
+def bake_action(frame_start,
+                frame_end,
+                frame_step=1,
+                only_selected=False,
+                do_pose=True,
+                do_object=True,
+                do_constraint_clear=False,
+                do_clean=False,
+                action=None,
+                ):
+
+    """
+    Return an image from the file path with options to search multiple paths
+    and return a placeholder if its not found.
+
+    :arg frame_start: First frame to bake.
+    :type frame_start: int
+    :arg frame_end: Last frame to bake.
+    :type frame_end: int
+    :arg frame_step: Frame step.
+    :type frame_step: int
+    :arg only_selected: Only bake selected data.
+    :type only_selected: bool
+    :arg do_pose: Bake pose channels.
+    :type do_pose: bool
+    :arg do_object: Bake objects.
+    :type do_object: bool
+    :arg do_constraint_clear: Remove constraints.
+    :type do_constraint_clear: bool
+    :arg do_clean: Remove redundant keyframes after baking.
+    :type do_clean: bool
+    :arg action: An action to bake the data into, or None for a new action
+       to be created.
+    :type action: :class:`bpy.types.Action` or None
+    
+    :return: an action or None
+    :rtype: :class:`bpy.types.Action`
+    """
+
+    # -------------------------------------------------------------------------
+    # Helper Functions
+
+    def pose_frame_info(obj):
+        from mathutils import Matrix
+
+        info = {}
+
+        pose = obj.pose
+
+        pose_items = pose.bones.items()
+
+        for name, pbone in pose_items:
+            binfo = {}
+            bone = pbone.bone
+
+            binfo["parent"] = getattr(bone.parent, "name", None)
+            binfo["bone"] = bone
+            binfo["pbone"] = pbone
+            binfo["matrix_local"] = bone.matrix_local.copy()
+            try:
+                binfo["matrix_local_inv"] = binfo["matrix_local"].inverted()
+            except:
+                binfo["matrix_local_inv"] = Matrix()
+
+            binfo["matrix"] = bone.matrix.copy()
+            binfo["matrix_pose"] = pbone.matrix.copy()
+            try:
+                binfo["matrix_pose_inv"] = binfo["matrix_pose"].inverted()
+            except:
+                binfo["matrix_pose_inv"] = Matrix()
+
+            info[name] = binfo
+
+        for name, pbone in pose_items:
+            binfo = info[name]
+            binfo_parent = binfo.get("parent", None)
+            if binfo_parent:
+                binfo_parent = info[binfo_parent]
+
+            matrix = binfo["matrix_pose"]
+            rest_matrix = binfo["matrix_local"]
+
+            if binfo_parent:
+                matrix = binfo_parent["matrix_pose_inv"] * matrix
+                rest_matrix = binfo_parent["matrix_local_inv"] * rest_matrix
+
+            binfo["matrix_key"] = rest_matrix.inverted() * matrix
+
+        return info
+
+
+    def obj_frame_info(obj):
+        info = {}
+        # parent = obj.parent
+        info["matrix_key"] = obj.matrix_local.copy()
+        return info
+
+    # -------------------------------------------------------------------------
+    # Setup the Context
+
+    # TODO, pass data rather then grabbing from the context!
+    scene = bpy.context.scene
+    obj = bpy.context.object
+    pose = obj.pose
+    frame_back = scene.frame_current
+
+    if pose is None:
+        do_pose = False
+
+    if do_pose is None and do_object is None:
+        return None
+
+    pose_info = []
+    obj_info = []
+
+    frame_range = range(frame_start, frame_end + 1, frame_step)
+
+    # -------------------------------------------------------------------------
+    # Collect transformations
+
+    # could speed this up by applying steps here too...
+    for f in frame_range:
+        scene.frame_set(f)
+
+        if do_pose:
+            pose_info.append(pose_frame_info(obj))
+        if do_object:
+            obj_info.append(obj_frame_info(obj))
+
+        f += 1
+
+    # -------------------------------------------------------------------------
+    # Create action
+
+    # incase animation data hassnt been created
+    atd = obj.animation_data_create()
+    if action is None:
+        action = bpy.data.actions.new("Action")
+    atd.action = action
+
+    if do_pose:
+        pose_items = pose.bones.items()
+    else:
+        pose_items = []  # skip
+
+    # -------------------------------------------------------------------------
+    # Apply transformations to action
+
+    # pose
+    for name, pbone in (pose_items if do_pose else ()):
+        if only_selected and not pbone.bone.select:
+            continue
+
+        if do_constraint_clear:
+            while pbone.constraints:
+                pbone.constraints.remove(pbone.constraints[0])
+
+        for f in frame_range:
+            matrix = pose_info[(f - frame_start) // frame_step][name]["matrix_key"]
+
+            # pbone.location = matrix.to_translation()
+            # pbone.rotation_quaternion = matrix.to_quaternion()
+            pbone.matrix_basis = matrix
+
+            pbone.keyframe_insert("location", -1, f, name)
+
+            rotation_mode = pbone.rotation_mode
+
+            if rotation_mode == 'QUATERNION':
+                pbone.keyframe_insert("rotation_quaternion", -1, f, name)
+            elif rotation_mode == 'AXIS_ANGLE':
+                pbone.keyframe_insert("rotation_axis_angle", -1, f, name)
+            else:  # euler, XYZ, ZXY etc
+                pbone.keyframe_insert("rotation_euler", -1, f, name)
+
+            pbone.keyframe_insert("scale", -1, f, name)
+
+    # object. TODO. multiple objects
+    if do_object:
+        if do_constraint_clear:
+            while obj.constraints:
+                obj.constraints.remove(obj.constraints[0])
+
+        for f in frame_range:
+            matrix = obj_info[(f - frame_start) // frame_step]["matrix_key"]
+            obj.matrix_local = matrix
+
+            obj.keyframe_insert("location", -1, f)
+
+            rotation_mode = obj.rotation_mode
+
+            if rotation_mode == 'QUATERNION':
+                obj.keyframe_insert("rotation_quaternion", -1, f)
+            elif rotation_mode == 'AXIS_ANGLE':
+                obj.keyframe_insert("rotation_axis_angle", -1, f)
+            else:  # euler, XYZ, ZXY etc
+                obj.keyframe_insert("rotation_euler", -1, f)
+
+            obj.keyframe_insert("scale", -1, f)
+
+    scene.frame_set(frame_back)
+
+    # -------------------------------------------------------------------------
+    # Clean
+
+    if do_clean:
+        for fcu in action.fcurves:
+            keyframe_points = fcu.keyframe_points
+            i = 1
+            while i < len(fcu.keyframe_points) - 1:
+                val_prev = keyframe_points[i - 1].co[1]
+                val_next = keyframe_points[i + 1].co[1]
+                val = keyframe_points[i].co[1]
+
+                if abs(val - val_prev) + abs(val - val_next) < 0.0001:
+                    keyframe_points.remove(keyframe_points[i])
+                else:
+                    i += 1
+
+    return action
index 918e915..f5f8b99 100644 (file)
@@ -29,7 +29,6 @@ _modules = (
     "console",
     "image",
     "mesh",
-    "nla",
     "object_align",
     "object",
     "object_randomize_transform",
index f33d561..660194a 100644 (file)
 
 # <pep8-80 compliant>
 
+if "bpy" in locals():
+    import imp
+    if "anim_utils" in locals():
+        imp.reload(anim_utils)
+
 import bpy
 from bpy.types import Operator
+from bpy.props import IntProperty, BoolProperty, EnumProperty
 
 
 class ANIM_OT_keying_set_export(Operator):
@@ -125,3 +131,105 @@ class ANIM_OT_keying_set_export(Operator):
         wm = context.window_manager
         wm.fileselect_add(self)
         return {'RUNNING_MODAL'}
+
+
+class BakeAction(Operator):
+    '''Bake animation to an Action'''
+    bl_idname = "nla.bake"
+    bl_label = "Bake Action"
+    bl_options = {'REGISTER', 'UNDO'}
+
+    frame_start = IntProperty(
+            name="Start Frame",
+            description="Start frame for baking",
+            min=0, max=300000,
+            default=1,
+            )
+    frame_end = IntProperty(
+            name="End Frame",
+            description="End frame for baking",
+            min=1, max=300000,
+            default=250,
+            )
+    step = IntProperty(
+            name="Frame Step",
+            description="Frame Step",
+            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'},
+            items=(('POSE', "Pose", ""),
+                   ('OBJECT', "Object", ""),
+                   ),
+            default={'POSE'},
+            )
+
+    def execute(self, context):
+
+        from bpy_extras import anim_utils
+
+        action = anim_utils.bake_action(self.frame_start,
+                                        self.frame_end,
+                                        self.step,
+                                        self.only_selected,
+                                        'POSE' in self.bake_types,
+                                        'OBJECT' in self.bake_types,
+                                        self.clear_consraints,
+                                        True,
+                                 )
+
+        if action is None:
+            self.report({'INFO'}, "Nothing to bake")
+            return {'CANCELLED'}
+
+        return {'FINISHED'}
+
+    def invoke(self, context, event):
+        wm = context.window_manager
+        return wm.invoke_props_dialog(self)
+
+
+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"
+    bl_options = {'REGISTER', 'UNDO'}
+
+    only_unused = BoolProperty(name="Only Unused",
+            description="Only unused (Fake User only) actions get considered",
+            default=True)
+
+    @classmethod
+    def poll(cls, context):
+        return len(bpy.data.actions) != 0
+
+    def execute(self, context):
+        removed = 0
+
+        for action in bpy.data.actions:
+            # if only user is "fake" user...
+            if ((self.only_unused is False) or
+                (action.use_fake_user and action.users == 1)):
+
+                # if it has F-Curves, then it's a "action library"
+                # (i.e. walk, wave, jump, etc.)
+                # and should be left alone as that's what fake users are for!
+                if not action.fcurves:
+                    # mark action for deletion
+                    action.user_clear()
+                    removed += 1
+
+        self.report({'INFO'}, "Removed %d empty and/or fake-user only Actions"
+                              % removed)
+        return {'FINISHED'}
diff --git a/release/scripts/startup/bl_operators/nla.py b/release/scripts/startup/bl_operators/nla.py
deleted file mode 100644 (file)
index feb0016..0000000
+++ /dev/null
@@ -1,306 +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 #####
-
-# <pep8-80 compliant>
-
-import bpy
-from bpy.types import Operator
-
-
-def pose_frame_info(obj):
-    from mathutils import Matrix
-
-    info = {}
-
-    pose = obj.pose
-
-    pose_items = pose.bones.items()
-
-    for name, pbone in pose_items:
-        binfo = {}
-        bone = pbone.bone
-
-        binfo["parent"] = getattr(bone.parent, "name", None)
-        binfo["bone"] = bone
-        binfo["pbone"] = pbone
-        binfo["matrix_local"] = bone.matrix_local.copy()
-        try:
-            binfo["matrix_local_inv"] = binfo["matrix_local"].inverted()
-        except:
-            binfo["matrix_local_inv"] = Matrix()
-
-        binfo["matrix"] = bone.matrix.copy()
-        binfo["matrix_pose"] = pbone.matrix.copy()
-        try:
-            binfo["matrix_pose_inv"] = binfo["matrix_pose"].inverted()
-        except:
-            binfo["matrix_pose_inv"] = Matrix()
-
-        info[name] = binfo
-
-    for name, pbone in pose_items:
-        binfo = info[name]
-        binfo_parent = binfo.get("parent", None)
-        if binfo_parent:
-            binfo_parent = info[binfo_parent]
-
-        matrix = binfo["matrix_pose"]
-        rest_matrix = binfo["matrix_local"]
-
-        if binfo_parent:
-            matrix = binfo_parent["matrix_pose_inv"] * matrix
-            rest_matrix = binfo_parent["matrix_local_inv"] * rest_matrix
-
-        binfo["matrix_key"] = rest_matrix.inverted() * matrix
-
-    return info
-
-
-def obj_frame_info(obj):
-    info = {}
-    # parent = obj.parent
-    info["matrix_key"] = obj.matrix_local.copy()
-    return info
-
-
-def bake(frame_start,
-         frame_end, step=1,
-         only_selected=False,
-         do_pose=True,
-         do_object=True,
-         do_constraint_clear=False,
-         action=None):
-
-    scene = bpy.context.scene
-    obj = bpy.context.object
-    pose = obj.pose
-    frame_back = scene.frame_current
-
-    if pose is None:
-        do_pose = False
-
-    if do_pose is None and do_object is None:
-        return None
-
-    pose_info = []
-    obj_info = []
-
-    frame_range = range(frame_start, frame_end + 1, step)
-
-    # -------------------------------------------------------------------------
-    # Collect transformations
-
-    # could speed this up by applying steps here too...
-    for f in frame_range:
-        scene.frame_set(f)
-
-        if do_pose:
-            pose_info.append(pose_frame_info(obj))
-        if do_object:
-            obj_info.append(obj_frame_info(obj))
-
-        f += 1
-
-    # -------------------------------------------------------------------------
-    # Create action
-
-    # incase animation data hassnt been created
-    atd = obj.animation_data_create()
-    if action is None:
-        action = bpy.data.actions.new("Action")
-    atd.action = action
-
-    if do_pose:
-        pose_items = pose.bones.items()
-    else:
-        pose_items = []  # skip
-
-    # -------------------------------------------------------------------------
-    # Apply transformations to action
-
-    # pose
-    for name, pbone in (pose_items if do_pose else ()):
-        if only_selected and not pbone.bone.select:
-            continue
-
-        if do_constraint_clear:
-            while pbone.constraints:
-                pbone.constraints.remove(pbone.constraints[0])
-
-        for f in frame_range:
-            matrix = pose_info[(f - frame_start) // step][name]["matrix_key"]
-
-            # pbone.location = matrix.to_translation()
-            # pbone.rotation_quaternion = matrix.to_quaternion()
-            pbone.matrix_basis = matrix
-
-            pbone.keyframe_insert("location", -1, f, name)
-
-            rotation_mode = pbone.rotation_mode
-
-            if rotation_mode == 'QUATERNION':
-                pbone.keyframe_insert("rotation_quaternion", -1, f, name)
-            elif rotation_mode == 'AXIS_ANGLE':
-                pbone.keyframe_insert("rotation_axis_angle", -1, f, name)
-            else:  # euler, XYZ, ZXY etc
-                pbone.keyframe_insert("rotation_euler", -1, f, name)
-
-            pbone.keyframe_insert("scale", -1, f, name)
-
-    # object. TODO. multiple objects
-    if do_object:
-        if do_constraint_clear:
-            while obj.constraints:
-                obj.constraints.remove(obj.constraints[0])
-
-        for f in frame_range:
-            matrix = obj_info[(f - frame_start) // step]["matrix_key"]
-            obj.matrix_local = matrix
-
-            obj.keyframe_insert("location", -1, f)
-
-            rotation_mode = obj.rotation_mode
-
-            if rotation_mode == 'QUATERNION':
-                obj.keyframe_insert("rotation_quaternion", -1, f)
-            elif rotation_mode == 'AXIS_ANGLE':
-                obj.keyframe_insert("rotation_axis_angle", -1, f)
-            else:  # euler, XYZ, ZXY etc
-                obj.keyframe_insert("rotation_euler", -1, f)
-
-            obj.keyframe_insert("scale", -1, f)
-
-    scene.frame_set(frame_back)
-
-    return action
-
-
-from bpy.props import IntProperty, BoolProperty, EnumProperty
-
-
-class BakeAction(Operator):
-    '''Bake animation to an Action'''
-    bl_idname = "nla.bake"
-    bl_label = "Bake Action"
-    bl_options = {'REGISTER', 'UNDO'}
-
-    frame_start = IntProperty(
-            name="Start Frame",
-            description="Start frame for baking",
-            min=0, max=300000,
-            default=1,
-            )
-    frame_end = IntProperty(
-            name="End Frame",
-            description="End frame for baking",
-            min=1, max=300000,
-            default=250,
-            )
-    step = IntProperty(
-            name="Frame Step",
-            description="Frame Step",
-            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'},
-            items=(('POSE', "Pose", ""),
-                   ('OBJECT', "Object", ""),
-                   ),
-            default={'POSE'},
-            )
-
-    def execute(self, context):
-
-        action = bake(self.frame_start,
-                      self.frame_end,
-                      self.step,
-                      self.only_selected,
-                      'POSE' in self.bake_types,
-                      'OBJECT' in self.bake_types,
-                      self.clear_consraints,
-                      )
-
-        if action is None:
-            self.report({'INFO'}, "Nothing to bake")
-            return {'CANCELLED'}
-
-        # basic cleanup, could move elsewhere
-        for fcu in action.fcurves:
-            keyframe_points = fcu.keyframe_points
-            i = 1
-            while i < len(fcu.keyframe_points) - 1:
-                val_prev = keyframe_points[i - 1].co[1]
-                val_next = keyframe_points[i + 1].co[1]
-                val = keyframe_points[i].co[1]
-
-                if abs(val - val_prev) + abs(val - val_next) < 0.0001:
-                    keyframe_points.remove(keyframe_points[i])
-                else:
-                    i += 1
-
-        return {'FINISHED'}
-
-    def invoke(self, context, event):
-        wm = context.window_manager
-        return wm.invoke_props_dialog(self)
-
-
-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"
-    bl_options = {'REGISTER', 'UNDO'}
-
-    only_unused = BoolProperty(name="Only Unused",
-            description="Only unused (Fake User only) actions get considered",
-            default=True)
-
-    @classmethod
-    def poll(cls, context):
-        return len(bpy.data.actions) != 0
-
-    def execute(self, context):
-        removed = 0
-
-        for action in bpy.data.actions:
-            # if only user is "fake" user...
-            if ((self.only_unused is False) or
-                (action.use_fake_user and action.users == 1)):
-
-                # if it has F-Curves, then it's a "action library"
-                # (i.e. walk, wave, jump, etc.)
-                # and should be left alone as that's what fake users are for!
-                if not action.fcurves:
-                    # mark action for deletion
-                    action.user_clear()
-                    removed += 1
-
-        self.report({'INFO'}, "Removed %d empty and/or fake-user only Actions"
-                              % removed)
-        return {'FINISHED'}