From 9d12ac29ea2589a57ac21faa91e985b0e3588705 Mon Sep 17 00:00:00 2001 From: Pratik Borhade Date: Thu, 27 Feb 2025 13:32:35 +0100 Subject: [PATCH 1/3] Fix: Grease Pencil: Add channel_color RNA prop to layer groups Property wasn't added to earlier because it was part of tree-node. But it was moved to tree-node just in main, see: 3ef2ee7c53 Failing right now in 4.4. Pull Request: https://projects.blender.org/blender/blender/pulls/135225 --- source/blender/makesrna/intern/rna_grease_pencil.cc | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/source/blender/makesrna/intern/rna_grease_pencil.cc b/source/blender/makesrna/intern/rna_grease_pencil.cc index 49d062625b7..1e8ee03e3a8 100644 --- a/source/blender/makesrna/intern/rna_grease_pencil.cc +++ b/source/blender/makesrna/intern/rna_grease_pencil.cc @@ -1239,6 +1239,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) From 566f51c24a5a15916d9290025ac7a149de7967ff Mon Sep 17 00:00:00 2001 From: Christoph Lendenfeld Date: Thu, 27 Feb 2025 14:46:36 +0100 Subject: [PATCH 2/3] 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 --- scripts/addons_core/pose_library/operators.py | 4 +- .../addons_core/pose_library/pose_usage.py | 43 ++++++++++++++++--- scripts/modules/bpy_extras/anim_utils.py | 18 +++++--- 3 files changed, 51 insertions(+), 14 deletions(-) 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} From f8057af3df4b48cabe4d9dffce76982cb1337a8d Mon Sep 17 00:00:00 2001 From: Sebastian Parborg Date: Thu, 27 Feb 2025 15:15:21 +0100 Subject: [PATCH 3/3] Libs: Update libs to include the patched openEXR lib See #128577 for more information --- lib/linux_x64 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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