Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender-addons.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSybren A. Stüvel <sybren@stuvel.eu>2021-07-05 18:35:44 +0300
committerSybren A. Stüvel <sybren@stuvel.eu>2021-07-05 18:35:44 +0300
commit55b0e323ba2e8b09a27db0817b30a3978e2b45f6 (patch)
tree49a274f0b296c453f441dd65ff139cfc7cf6e138
parenta7838b9b29bb6cd02f306931cae2588b4e548fa8 (diff)
Pose Library: allow converting old pose libraries to new pose assets
The Dopesheet side-panel now has a button "Convert old-style pose library" that converts each frame with a pose marker to a pose asset. The preview images are rendered on the current frame.
-rw-r--r--pose_library/__init__.py3
-rw-r--r--pose_library/conversion.py78
-rw-r--r--pose_library/gui.py2
-rw-r--r--pose_library/operators.py32
-rw-r--r--pose_library/pose_creation.py74
5 files changed, 163 insertions, 26 deletions
diff --git a/pose_library/__init__.py b/pose_library/__init__.py
index a9f880f5..c72d9426 100644
--- a/pose_library/__init__.py
+++ b/pose_library/__init__.py
@@ -36,7 +36,7 @@ bl_info = {
from typing import List, Tuple
_need_reload = "operators" in locals()
-from . import gui, keymaps, macros, operators
+from . import gui, keymaps, macros, operators, conversion
if _need_reload:
import importlib
@@ -45,6 +45,7 @@ if _need_reload:
keymaps = importlib.reload(keymaps)
macros = importlib.reload(macros)
operators = importlib.reload(operators)
+ conversion = importlib.reload(conversion)
import bpy
diff --git a/pose_library/conversion.py b/pose_library/conversion.py
new file mode 100644
index 00000000..43a5d3a4
--- /dev/null
+++ b/pose_library/conversion.py
@@ -0,0 +1,78 @@
+# ##### 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 #####
+
+"""
+Pose Library - Conversion of old pose libraries.
+"""
+
+from typing import Optional
+from collections.abc import Collection
+
+if "pose_creation" not in locals():
+ from . import pose_creation
+else:
+ import importlib
+
+ pose_creation = importlib.reload(pose_creation)
+
+import bpy
+from bpy.types import (
+ Action,
+ TimelineMarker,
+)
+
+
+def convert_old_poselib(old_poselib: Action) -> Collection[Action]:
+ """Convert an old-style pose library to a set of pose Actions.
+
+ Old pose libraries were one Action with multiple pose markers. Each pose
+ marker will be converted to an Action by itself and marked as asset.
+ """
+
+ pose_assets = [
+ action
+ for marker in old_poselib.pose_markers
+ if (action := convert_old_pose(old_poselib, marker))
+ ]
+
+ # Mark all Actions as assets in one go. Ideally this would be done on an
+ # appropriate frame in the scene (to set up things like the background
+ # colour), but the old-style poselib doesn't contain such information. All
+ # we can do is just render on the current frame.
+ bpy.ops.asset.mark({'selected_ids': pose_assets})
+
+ return pose_assets
+
+
+def convert_old_pose(old_poselib: Action, marker: TimelineMarker) -> Optional[Action]:
+ """Convert an old-style pose library pose to a pose action."""
+
+ frame: int = marker.frame
+ action: Optional[Action] = None
+
+ for fcurve in old_poselib.fcurves:
+ key = pose_creation.find_keyframe(fcurve, frame)
+ if not key:
+ continue
+
+ if action is None:
+ action = bpy.data.actions.new(marker.name)
+
+ pose_creation.create_single_key_fcurve(action, fcurve, key)
+
+ return action
diff --git a/pose_library/gui.py b/pose_library/gui.py
index bd963f1a..bf2693e9 100644
--- a/pose_library/gui.py
+++ b/pose_library/gui.py
@@ -174,6 +174,8 @@ class DOPESHEET_PT_asset_panel(Panel):
row.operator("poselib.restore_previous_action", text="", icon='LOOP_BACK')
col.operator("poselib.copy_as_asset", icon="COPYDOWN")
+ layout.operator("poselib.convert_old_poselib")
+
classes = (
ASSETBROWSER_PT_pose_library_editing,
diff --git a/pose_library/operators.py b/pose_library/operators.py
index fee94bad..0474f6e7 100644
--- a/pose_library/operators.py
+++ b/pose_library/operators.py
@@ -393,10 +393,42 @@ class POSELIB_OT_apply_pose_asset_for_keymap(Operator):
return bpy.ops.poselib.apply_pose_asset(context.copy(), 'EXEC_DEFAULT', flipped=flipped)
+class POSELIB_OT_convert_old_poselib(Operator):
+ bl_idname = "poselib.convert_old_poselib"
+ bl_label = "Convert old-style pose library"
+ bl_description = "Create a pose asset for each pose marker in the current action"
+ bl_options = {"REGISTER", "UNDO"}
+
+ @classmethod
+ def poll(cls, context: Context) -> bool:
+ action = context.object and context.object.animation_data and context.object.animation_data.action
+ if not action:
+ cls.poll_message_set("Active object has no Action")
+ return False
+ if not action.pose_markers:
+ cls.poll_message_set("Action %r is not a old-style pose library" % action.name)
+ return False
+ return True
+
+ def execute(self, context: Context) -> Set[str]:
+ from . import conversion
+
+ old_poselib = context.object.animation_data.action
+ new_actions = conversion.convert_old_poselib(old_poselib)
+
+ if not new_actions:
+ self.report({'ERROR'}, "Unable to convert to pose assets")
+ return {'CANCELLED'}
+
+ self.report({'INFO'}, "Converted %d poses to pose assets" % len(new_actions))
+ return {'FINISHED'}
+
+
classes = (
ASSET_OT_assign_action,
POSELIB_OT_apply_pose_asset_for_keymap,
POSELIB_OT_blend_pose_asset_for_keymap,
+ POSELIB_OT_convert_old_poselib,
POSELIB_OT_copy_as_asset,
POSELIB_OT_create_pose_asset,
POSELIB_OT_paste_asset,
diff --git a/pose_library/pose_creation.py b/pose_library/pose_creation.py
index da72ed64..79efcae4 100644
--- a/pose_library/pose_creation.py
+++ b/pose_library/pose_creation.py
@@ -355,35 +355,59 @@ def copy_fcurves(
keyframe = find_keyframe(fcurve, src_frame_nr)
if keyframe is None:
continue
- # Create an FCurve and copy some properties.
- src_group_name = fcurve.group.name if fcurve.group else ""
- dst_fcurve = dst_action.fcurves.new(
- fcurve.data_path, index=fcurve.array_index, action_group=src_group_name
- )
- for propname in {"auto_smoothing", "color", "color_mode", "extrapolation"}:
- setattr(dst_fcurve, propname, getattr(fcurve, propname))
-
- dst_keyframe = dst_fcurve.keyframe_points.insert(
- keyframe.co.x, keyframe.co.y, keyframe_type=keyframe.type
- )
-
- for propname in {
- "amplitude",
- "back",
- "easing",
- "handle_left",
- "handle_left_type",
- "handle_right",
- "handle_right_type",
- "interpolation",
- "period",
- }:
- setattr(dst_keyframe, propname, getattr(keyframe, propname))
- dst_fcurve.update()
+ create_single_key_fcurve(dst_action, fcurve, keyframe)
num_fcurves_copied += 1
return num_fcurves_copied
+def create_single_key_fcurve(
+ dst_action: Action, src_fcurve: FCurve, src_keyframe: Keyframe
+) -> FCurve:
+ """Create a copy of the source FCurve, but only for the given keyframe.
+
+ Returns a new FCurve with just one keyframe.
+ """
+
+ dst_fcurve = copy_fcurve_without_keys(dst_action, src_fcurve)
+ copy_keyframe(dst_fcurve, src_keyframe)
+ return dst_fcurve
+
+
+def copy_fcurve_without_keys(dst_action: Action, src_fcurve: FCurve) -> FCurve:
+ """Create a new FCurve and copy some properties."""
+
+ src_group_name = src_fcurve.group.name if src_fcurve.group else ""
+ dst_fcurve = dst_action.fcurves.new(
+ src_fcurve.data_path, index=src_fcurve.array_index, action_group=src_group_name
+ )
+ for propname in {"auto_smoothing", "color", "color_mode", "extrapolation"}:
+ setattr(dst_fcurve, propname, getattr(src_fcurve, propname))
+ return dst_fcurve
+
+
+def copy_keyframe(dst_fcurve: FCurve, src_keyframe: Keyframe) -> Keyframe:
+ """Copy a keyframe from one FCurve to the other."""
+
+ dst_keyframe = dst_fcurve.keyframe_points.insert(
+ src_keyframe.co.x, src_keyframe.co.y, options={'FAST'}, keyframe_type=src_keyframe.type
+ )
+
+ for propname in {
+ "amplitude",
+ "back",
+ "easing",
+ "handle_left",
+ "handle_left_type",
+ "handle_right",
+ "handle_right_type",
+ "interpolation",
+ "period",
+ }:
+ setattr(dst_keyframe, propname, getattr(src_keyframe, propname))
+ dst_fcurve.update()
+ return dst_keyframe
+
+
def find_keyframe(fcurve: FCurve, frame: float) -> Optional[Keyframe]:
# Binary search adapted from https://pythonguides.com/python-binary-search/
keyframes = fcurve.keyframe_points