diff --git a/lib/linux_x64 b/lib/linux_x64 index 85a8dca8860..eacf548b2e6 160000 --- a/lib/linux_x64 +++ b/lib/linux_x64 @@ -1 +1 @@ -Subproject commit 85a8dca8860c7a7ee318530bcf0111d825993dd2 +Subproject commit eacf548b2e6c10d1a6d5879218ecaa11f102310a diff --git a/scripts/addons_core/pose_library/operators.py b/scripts/addons_core/pose_library/operators.py index 9c6b791669c..57f125e6721 100644 --- a/scripts/addons_core/pose_library/operators.py +++ b/scripts/addons_core/pose_library/operators.py @@ -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: diff --git a/scripts/addons_core/pose_library/pose_usage.py b/scripts/addons_core/pose_library/pose_usage.py index 1a85f56ec55..a091fc63a3f 100644 --- a/scripts/addons_core/pose_library/pose_usage.py +++ b/scripts/addons_core/pose_library/pose_usage.py @@ -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 diff --git a/scripts/modules/bpy_extras/anim_utils.py b/scripts/modules/bpy_extras/anim_utils.py index 20e537feaee..e566daad758 100644 --- a/scripts/modules/bpy_extras/anim_utils.py +++ b/scripts/modules/bpy_extras/anim_utils.py @@ -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} diff --git a/source/blender/makesrna/intern/rna_grease_pencil.cc b/source/blender/makesrna/intern/rna_grease_pencil.cc index 76d520d817a..848ae400bd3 100644 --- a/source/blender/makesrna/intern/rna_grease_pencil.cc +++ b/source/blender/makesrna/intern/rna_grease_pencil.cc @@ -1202,6 +1202,11 @@ static void rna_def_grease_pencil_layer_group(BlenderRNA *brna) prop = RNA_def_property(srna, "color_tag", PROP_ENUM, PROP_NONE); RNA_def_property_enum_funcs(prop, "rna_group_color_tag_get", "rna_group_color_tag_set", nullptr); RNA_def_property_enum_items(prop, enum_layergroup_color_items); + + prop = RNA_def_property(srna, "channel_color", PROP_FLOAT, PROP_COLOR); + RNA_def_property_float_sdna(prop, "GreasePencilLayerTreeNode", "color"); + RNA_def_property_array(prop, 3); + RNA_def_property_update(prop, NC_GPENCIL | NA_EDITED, nullptr); } static void rna_def_grease_pencil_layer_groups(BlenderRNA *brna, PropertyRNA *cprop)