Remove the operators to convert a legacy pose library (from Blender 2.93 and older) to the current system (introduced in Blender 3.0). The removal is mostly because the pose markers do not play well with slotted Actions. The conversion code was never updated for those, and by now it's easier to remove them than to update the code to the current data model. Technically it was still possible to create a legacy pose library (an Action with pose markers) in current versions of Blender, and then use this operator to convert that to a modern pose library. I don't know of anybody doing that, though. This is part of #146586 Pull Request: https://projects.blender.org/blender/blender/pulls/147061
228 lines
7.0 KiB
Python
228 lines
7.0 KiB
Python
# SPDX-FileCopyrightText: 2021-2023 Blender Foundation
|
|
#
|
|
# SPDX-License-Identifier: GPL-2.0-or-later
|
|
|
|
"""
|
|
Pose Library - GUI definition.
|
|
"""
|
|
|
|
import bpy
|
|
from bpy.types import (
|
|
AssetRepresentation,
|
|
Context,
|
|
Menu,
|
|
Panel,
|
|
UILayout,
|
|
UIList,
|
|
)
|
|
from bl_ui_utils.layout import operator_context
|
|
|
|
|
|
class VIEW3D_MT_pose_modify(Menu):
|
|
bl_label = "Modify Pose Asset"
|
|
|
|
def draw(self, _context):
|
|
layout = self.layout
|
|
|
|
layout.operator("poselib.asset_modify", text="Replace").mode = "REPLACE"
|
|
layout.operator("poselib.asset_modify", text="Add Selected Bones").mode = "ADD"
|
|
layout.operator("poselib.asset_modify", text="Remove Selected Bones").mode = "REMOVE"
|
|
|
|
|
|
class PoseLibraryPanel:
|
|
@classmethod
|
|
def pose_library_panel_poll(cls, context: Context) -> bool:
|
|
return context.mode == 'POSE'
|
|
|
|
@classmethod
|
|
def poll(cls, context: Context) -> bool:
|
|
return cls.pose_library_panel_poll(context)
|
|
|
|
|
|
class VIEW3D_AST_pose_library(bpy.types.AssetShelf):
|
|
bl_space_type = "VIEW_3D"
|
|
bl_activate_operator = "POSELIB_OT_apply_pose_asset"
|
|
bl_drag_operator = "POSELIB_OT_blend_pose_asset"
|
|
|
|
@classmethod
|
|
def poll(cls, context: Context) -> bool:
|
|
return PoseLibraryPanel.poll(context)
|
|
|
|
@classmethod
|
|
def asset_poll(cls, asset: AssetRepresentation) -> bool:
|
|
return asset.id_type == 'ACTION'
|
|
|
|
@classmethod
|
|
def draw_context_menu(cls, _context: Context, _asset: AssetRepresentation, layout: UILayout):
|
|
layout.operator("poselib.apply_pose_asset", text="Apply Pose").flipped = False
|
|
layout.operator("poselib.apply_pose_asset", text="Apply Pose Flipped").flipped = True
|
|
|
|
with operator_context(layout, 'INVOKE_DEFAULT'):
|
|
layout.operator("poselib.blend_pose_asset", text="Blend Pose").flipped = False
|
|
layout.operator("poselib.blend_pose_asset", text="Blend Pose Flipped").flipped = True
|
|
|
|
layout.separator()
|
|
props = layout.operator("poselib.pose_asset_select_bones", text="Select Pose Bones")
|
|
props.select = True
|
|
props = layout.operator("poselib.pose_asset_select_bones", text="Deselect Pose Bones")
|
|
props.select = False
|
|
|
|
layout.separator()
|
|
layout.operator("poselib.asset_modify", text="Adjust Pose Asset").mode = 'ADJUST'
|
|
layout.menu("VIEW3D_MT_pose_modify")
|
|
layout.operator("poselib.asset_delete")
|
|
|
|
layout.separator()
|
|
layout.operator("asset.open_containing_blend_file")
|
|
|
|
|
|
def pose_library_asset_browser_context_menu(self: UIList, context: Context) -> None:
|
|
def is_pose_library_asset_browser() -> bool:
|
|
asset_library_ref = getattr(context, "asset_library_reference", None)
|
|
if not asset_library_ref:
|
|
return False
|
|
asset = getattr(context, "asset", None)
|
|
if not asset:
|
|
return False
|
|
return bool(asset.id_type == 'ACTION')
|
|
|
|
if not is_pose_library_asset_browser():
|
|
return
|
|
|
|
layout = self.layout
|
|
|
|
layout.separator()
|
|
|
|
layout.operator("poselib.apply_pose_asset", text="Apply Pose").flipped = False
|
|
layout.operator("poselib.apply_pose_asset", text="Apply Pose Flipped").flipped = True
|
|
|
|
with operator_context(layout, 'INVOKE_DEFAULT'):
|
|
layout.operator("poselib.blend_pose_asset", text="Blend Pose").flipped = False
|
|
layout.operator("poselib.blend_pose_asset", text="Blend Pose Flipped").flipped = True
|
|
|
|
layout.separator()
|
|
props = layout.operator("poselib.pose_asset_select_bones", text="Select Pose Bones")
|
|
props.select = True
|
|
props = layout.operator("poselib.pose_asset_select_bones", text="Deselect Pose Bones")
|
|
props.select = False
|
|
|
|
layout.separator()
|
|
layout.operator("poselib.asset_modify", text="Adjust Pose Asset").mode = 'ADJUST'
|
|
layout.menu("VIEW3D_MT_pose_modify")
|
|
with operator_context(layout, 'INVOKE_DEFAULT'):
|
|
layout.operator("poselib.asset_delete")
|
|
|
|
layout.separator()
|
|
layout.operator("asset.assign_action")
|
|
|
|
layout.separator()
|
|
|
|
|
|
class DOPESHEET_PT_asset_panel(PoseLibraryPanel, Panel):
|
|
bl_space_type = "DOPESHEET_EDITOR"
|
|
bl_region_type = "UI"
|
|
bl_label = "Create Pose Asset"
|
|
bl_category = "Action"
|
|
|
|
def draw(self, context: Context) -> None:
|
|
layout = self.layout
|
|
col = layout.column(align=True)
|
|
row = col.row(align=True)
|
|
row.operator("poselib.create_pose_asset")
|
|
if bpy.types.POSELIB_OT_restore_previous_action.poll(context):
|
|
row.operator("poselib.restore_previous_action", text="", icon='LOOP_BACK')
|
|
col.operator("poselib.copy_as_asset", icon="COPYDOWN")
|
|
|
|
|
|
def pose_library_list_item_asset_menu(self: UIList, context: Context) -> None:
|
|
layout = self.layout
|
|
layout.menu("ASSETBROWSER_MT_asset")
|
|
|
|
|
|
class ASSETBROWSER_MT_asset(Menu):
|
|
bl_label = "Asset"
|
|
|
|
@classmethod
|
|
def poll(cls, context):
|
|
from bpy_extras.asset_utils import SpaceAssetInfo
|
|
|
|
return SpaceAssetInfo.is_asset_browser_poll(context)
|
|
|
|
def draw(self, context: Context) -> None:
|
|
layout = self.layout
|
|
|
|
layout.operator("poselib.paste_asset", icon='PASTEDOWN')
|
|
layout.separator()
|
|
layout.operator("poselib.create_pose_asset")
|
|
|
|
|
|
# Messagebus subscription to monitor asset library changes.
|
|
_msgbus_owner = object()
|
|
|
|
|
|
def _on_asset_library_changed() -> None:
|
|
"""Update areas when a different asset library is selected."""
|
|
refresh_area_types = {'DOPESHEET_EDITOR', 'VIEW_3D'}
|
|
for win in bpy.context.window_manager.windows:
|
|
for area in win.screen.areas:
|
|
if area.type not in refresh_area_types:
|
|
continue
|
|
|
|
area.tag_redraw()
|
|
|
|
|
|
def register_message_bus() -> None:
|
|
bpy.msgbus.subscribe_rna(
|
|
key=(bpy.types.FileAssetSelectParams, "asset_library_reference"),
|
|
owner=_msgbus_owner,
|
|
args=(),
|
|
notify=_on_asset_library_changed,
|
|
options={'PERSISTENT'},
|
|
)
|
|
|
|
|
|
def unregister_message_bus() -> None:
|
|
bpy.msgbus.clear_by_owner(_msgbus_owner)
|
|
|
|
|
|
@bpy.app.handlers.persistent
|
|
def _on_blendfile_load_pre(none, other_none) -> None:
|
|
# The parameters are required, but both are None.
|
|
unregister_message_bus()
|
|
|
|
|
|
@bpy.app.handlers.persistent
|
|
def _on_blendfile_load_post(none, other_none) -> None:
|
|
# The parameters are required, but both are None.
|
|
register_message_bus()
|
|
|
|
|
|
classes = (
|
|
DOPESHEET_PT_asset_panel,
|
|
ASSETBROWSER_MT_asset,
|
|
VIEW3D_MT_pose_modify,
|
|
VIEW3D_AST_pose_library,
|
|
)
|
|
|
|
_register, _unregister = bpy.utils.register_classes_factory(classes)
|
|
|
|
|
|
def register() -> None:
|
|
_register()
|
|
|
|
bpy.types.ASSETBROWSER_MT_context_menu.prepend(pose_library_asset_browser_context_menu)
|
|
bpy.types.ASSETBROWSER_MT_editor_menus.append(pose_library_list_item_asset_menu)
|
|
|
|
register_message_bus()
|
|
bpy.app.handlers.load_pre.append(_on_blendfile_load_pre)
|
|
bpy.app.handlers.load_post.append(_on_blendfile_load_post)
|
|
|
|
|
|
def unregister() -> None:
|
|
_unregister()
|
|
|
|
unregister_message_bus()
|
|
|
|
bpy.types.ASSETBROWSER_MT_context_menu.remove(pose_library_asset_browser_context_menu)
|
|
bpy.types.ASSETBROWSER_MT_editor_menus.remove(pose_library_list_item_asset_menu)
|