Fix: selecting bones of pose assets not respecting multiple slots
The code for selecting bones from a pose was still using the legacy api, thus it didn't work properly for selecting bones of all slots. Pull Request: https://projects.blender.org/blender/blender/pulls/134912
This commit is contained in:
committed by
Christoph Lendenfeld
parent
9d12ac29ea
commit
566f51c24a
@@ -272,8 +272,8 @@ class POSELIB_OT_pose_asset_select_bones(PoseAssetUser, Operator):
|
||||
flipped: BoolProperty(name="Flipped", default=False) # type: ignore
|
||||
|
||||
def use_pose(self, context: Context, pose_asset: Action) -> Set[str]:
|
||||
arm_object: Object = context.object
|
||||
pose_usage.select_bones(arm_object, pose_asset, select=self.select, flipped=self.flipped)
|
||||
for object in context.selected_objects:
|
||||
pose_usage.select_bones(object, pose_asset, select=self.select, flipped=self.flipped)
|
||||
if self.select:
|
||||
msg = tip_("Selected bones from %s") % pose_asset.name
|
||||
else:
|
||||
|
||||
@@ -9,26 +9,59 @@ Pose Library - usage functions.
|
||||
from typing import Set
|
||||
import re
|
||||
import bpy
|
||||
from bpy_extras import anim_utils
|
||||
|
||||
from bpy.types import (
|
||||
Action,
|
||||
Object,
|
||||
ActionSlot,
|
||||
)
|
||||
|
||||
|
||||
def _find_best_slot(action: Action, object: Object) -> ActionSlot | None:
|
||||
"""
|
||||
Trying to find a slot that is the best match for the given object.
|
||||
The best slot is either
|
||||
the slot of the given object if that exists in the action,
|
||||
or the first slot of type object
|
||||
"""
|
||||
if not action.slots:
|
||||
return None
|
||||
# For the selection code, the object doesn't need to be animated yet, so anim_data may be None.
|
||||
anim_data = object.animation_data
|
||||
|
||||
# last_slot_identifier will equal to the current slot identifier if one is assigned.
|
||||
if anim_data and anim_data.last_slot_identifier in action.slots:
|
||||
return action.slots[anim_data.last_slot_identifier]
|
||||
|
||||
for slot in action.slots:
|
||||
if slot.target_id_type == 'OBJECT':
|
||||
return slot
|
||||
return None
|
||||
|
||||
|
||||
def select_bones(arm_object: Object, action: Action, *, select: bool, flipped: bool) -> None:
|
||||
pose_bone_re = re.compile(r'pose.bones\["([^"]+)"\]')
|
||||
pose = arm_object.pose
|
||||
if not pose:
|
||||
return
|
||||
|
||||
slot = _find_best_slot(action, arm_object)
|
||||
if not slot:
|
||||
return
|
||||
|
||||
seen_bone_names: Set[str] = set()
|
||||
channelbag = anim_utils.action_get_channelbag_for_slot(action, slot)
|
||||
if not channelbag:
|
||||
return
|
||||
|
||||
for fcurve in action.fcurves:
|
||||
pose_bone_re = re.compile(r'pose.bones\["([^"]+)"\]')
|
||||
for fcurve in channelbag.fcurves:
|
||||
data_path: str = fcurve.data_path
|
||||
match = pose_bone_re.match(data_path)
|
||||
if not match:
|
||||
regex_match = pose_bone_re.match(data_path)
|
||||
if not regex_match:
|
||||
continue
|
||||
|
||||
bone_name = match.group(1)
|
||||
bone_name = regex_match.group(1)
|
||||
|
||||
if bone_name in seen_bone_names:
|
||||
continue
|
||||
|
||||
@@ -13,7 +13,7 @@ __all__ = (
|
||||
)
|
||||
|
||||
import bpy
|
||||
from bpy.types import Action, ActionSlot
|
||||
from bpy.types import Action, ActionSlot, ActionChannelbag
|
||||
from dataclasses import dataclass
|
||||
|
||||
from collections.abc import (
|
||||
@@ -75,14 +75,18 @@ class BakeOptions:
|
||||
"""Bake custom properties."""
|
||||
|
||||
|
||||
def _get_channelbag_for_slot(action: Action, slot: ActionSlot):
|
||||
# This is on purpose limited to the first layer and strip. To support more
|
||||
# than 1 layer, a rewrite of this operator is needed which ideally would
|
||||
# happen in C++.
|
||||
def action_get_channelbag_for_slot(action: Action, slot: ActionSlot) -> ActionChannelbag | None:
|
||||
"""
|
||||
Returns the first channelbag found for the slot.
|
||||
In case there are multiple layers or strips they are iterated until a
|
||||
channelbag for that slot is found. In case no matching channelbag is found, returns None.
|
||||
"""
|
||||
for layer in action.layers:
|
||||
for strip in layer.strips:
|
||||
channelbag = strip.channelbag(slot)
|
||||
return channelbag
|
||||
if channelbag:
|
||||
return channelbag
|
||||
return None
|
||||
|
||||
|
||||
def _ensure_channelbag_exists(action: Action, slot: ActionSlot):
|
||||
@@ -409,7 +413,7 @@ def bake_action_iter(
|
||||
# pose
|
||||
lookup_fcurves = {}
|
||||
assert action.is_action_layered
|
||||
channelbag = _get_channelbag_for_slot(action, atd.action_slot)
|
||||
channelbag = action_get_channelbag_for_slot(action, atd.action_slot)
|
||||
if channelbag:
|
||||
# channelbag can be None if no layers or strips exist in the action.
|
||||
lookup_fcurves = {(fcurve.data_path, fcurve.array_index): fcurve for fcurve in channelbag.fcurves}
|
||||
|
||||
Reference in New Issue
Block a user