Anim: replace Bone Groups & Armature Layers with Bone Collections
Armature layers (the 32 little dots) and bone groups are replaced with Bone Collections: - Bone collections are stored on the armature, and have a name that is unique within that armature. - An armature can have an arbitrary number of bone collections (instead of the fixed 32 layers). - Bones can be assigned to zero or more bone collections. - Bone collections have a visibility setting, just like objects in scene collections. - When a bone is in at least one collection, and all its collections in are hidden, the bone is hidden. In other cases (in any visible collection, or in no collection at all), the bone visibility is determined by its own 'hidden' flag. - For now, bone collections cannot be nested; they are a flat list just like bone groups were. Nestability of bone collections is intended to be implemented in a later 4.x release. - Since bone collections are defined on the armature, they can be used from both pose mode and edit mode. Versioning converts bone groups and armature layers to new bone collections. Layers that do not contain any bones are skipped. The old data structures remain in DNA and are unaltered, for limited forward compatibility. That way at least a save with Blender 4.0 will not immediately erase the bone group and armature layers and their bone assignments. Shortcuts: - M/Shift+M in pose/edit mode: move to collection (M) and add to collection (shift+M). This works similar to the M/Shift+M menus for objects & scene collections. - Ctrl+G in pose mode shows a port of the old 'bone groups' menu. This is likely to be removed in the near future, as the functionality overlaps with the M/Shift+M menus. This is the first commit of a series; the bone collections feature will be improved before the Blender 4.0 release. See #108941 for more info. Pull request: https://projects.blender.org/blender/blender/pulls/109976
This commit is contained in:
committed by
Nathan Vegdahl
parent
042c5347f4
commit
998136f7a7
@@ -4761,13 +4761,13 @@ def km_pose(params):
|
||||
("pose.constraints_clear", {"type": 'C', "value": 'PRESS', "ctrl": True, "alt": True}, None),
|
||||
("pose.ik_add", {"type": 'I', "value": 'PRESS', "shift": True}, None),
|
||||
("pose.ik_clear", {"type": 'I', "value": 'PRESS', "ctrl": True, "alt": True}, None),
|
||||
op_menu("VIEW3D_MT_pose_group", {"type": 'G', "value": 'PRESS', "ctrl": True}),
|
||||
op_menu("VIEW3D_MT_bone_collections", {"type": 'G', "value": 'PRESS', "ctrl": True}),
|
||||
op_menu("VIEW3D_MT_bone_options_toggle", {"type": 'W', "value": 'PRESS', "shift": True}),
|
||||
op_menu("VIEW3D_MT_bone_options_enable", {"type": 'W', "value": 'PRESS', "shift": True, "ctrl": True}),
|
||||
op_menu("VIEW3D_MT_bone_options_disable", {"type": 'W', "value": 'PRESS', "alt": True}),
|
||||
("armature.layers_show_all", {"type": 'ACCENT_GRAVE', "value": 'PRESS', "ctrl": True}, None),
|
||||
("armature.armature_layers", {"type": 'M', "value": 'PRESS', "shift": True}, None),
|
||||
("pose.bone_layers", {"type": 'M', "value": 'PRESS'}, None),
|
||||
("armature.assign_to_collection", {"type": 'M', "value": 'PRESS', "shift": True}, None),
|
||||
("armature.move_to_collection", {"type": 'M', "value": 'PRESS'}, None),
|
||||
("transform.bbone_resize", {"type": 'S', "value": 'PRESS', "shift": True, "ctrl": True, "alt": True}, None),
|
||||
("anim.keyframe_insert_menu", {"type": 'I', "value": 'PRESS'}, None),
|
||||
("anim.keyframe_delete_v3d", {"type": 'I', "value": 'PRESS', "alt": True}, None),
|
||||
|
||||
@@ -49,12 +49,6 @@ class DATA_PT_skeleton(ArmatureButtonsPanel, Panel):
|
||||
|
||||
layout.row().prop(arm, "pose_position", expand=True)
|
||||
|
||||
col = layout.column()
|
||||
col.label(text="Layers:")
|
||||
col.prop(arm, "layers", text="")
|
||||
col.label(text="Protected Layers:")
|
||||
col.prop(arm, "layers_protected", text="")
|
||||
|
||||
|
||||
class DATA_PT_display(ArmatureButtonsPanel, Panel):
|
||||
bl_label = "Viewport Display"
|
||||
@@ -88,23 +82,19 @@ class DATA_PT_display(ArmatureButtonsPanel, Panel):
|
||||
sub.prop(arm, "relation_line_position", text="Relations", expand=True)
|
||||
|
||||
|
||||
class DATA_MT_bone_group_context_menu(Menu):
|
||||
bl_label = "Bone Group Specials"
|
||||
class DATA_UL_bone_collections(UIList):
|
||||
def draw_item(self, _context, layout, armature, bcoll, _icon, _active_data, _active_propname, _index):
|
||||
active_bone = armature.edit_bones.active or armature.bones.active
|
||||
has_active_bone = active_bone and bcoll.name in active_bone.collections
|
||||
|
||||
def draw(self, _context):
|
||||
layout = self.layout
|
||||
|
||||
layout.operator("pose.group_sort", icon='SORTALPHA')
|
||||
layout.prop(bcoll, "name", text="", emboss=False,
|
||||
icon='DOT' if has_active_bone else 'BLANK1')
|
||||
layout.prop(bcoll, "is_visible", text="", emboss=False,
|
||||
icon='HIDE_OFF' if bcoll.is_visible else 'HIDE_ON')
|
||||
|
||||
|
||||
class DATA_UL_bone_groups(UIList):
|
||||
def draw_item(self, _context, layout, _data, item, _icon, _active_data, _active_propname, _index):
|
||||
layout.prop(item, "name", text="", emboss=False, icon='GROUP_BONE')
|
||||
|
||||
|
||||
class DATA_PT_bone_groups(ArmatureButtonsPanel, Panel):
|
||||
bl_label = "Bone Groups"
|
||||
bl_options = {'DEFAULT_CLOSED'}
|
||||
class DATA_PT_bone_collections(ArmatureButtonsPanel, Panel):
|
||||
bl_label = "Bone Collections"
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
@@ -115,44 +105,42 @@ class DATA_PT_bone_groups(ArmatureButtonsPanel, Panel):
|
||||
layout = self.layout
|
||||
|
||||
ob = context.object
|
||||
pose = ob.pose
|
||||
group = pose.bone_groups.active
|
||||
arm = ob.data
|
||||
active_bcoll = arm.collections.active
|
||||
|
||||
row = layout.row()
|
||||
|
||||
rows = 1
|
||||
if group:
|
||||
if active_bcoll:
|
||||
rows = 4
|
||||
|
||||
row.template_list(
|
||||
"DATA_UL_bone_groups",
|
||||
"bone_groups",
|
||||
pose,
|
||||
"bone_groups",
|
||||
pose.bone_groups,
|
||||
"DATA_UL_bone_collections",
|
||||
"collections",
|
||||
arm,
|
||||
"collections",
|
||||
arm.collections,
|
||||
"active_index",
|
||||
rows=rows,
|
||||
)
|
||||
|
||||
col = row.column(align=True)
|
||||
col.operator("pose.group_add", icon='ADD', text="")
|
||||
col.operator("pose.group_remove", icon='REMOVE', text="")
|
||||
col.menu("DATA_MT_bone_group_context_menu", icon='DOWNARROW_HLT', text="")
|
||||
if group:
|
||||
col.operator("armature.collection_add", icon='ADD', text="")
|
||||
col.operator("armature.collection_remove", icon='REMOVE', text="")
|
||||
if active_bcoll:
|
||||
col.separator()
|
||||
col.operator("pose.group_move", icon='TRIA_UP', text="").direction = 'UP'
|
||||
col.operator("pose.group_move", icon='TRIA_DOWN', text="").direction = 'DOWN'
|
||||
col.operator("armature.collection_move", icon='TRIA_UP', text="").direction = 'UP'
|
||||
col.operator("armature.collection_move", icon='TRIA_DOWN', text="").direction = 'DOWN'
|
||||
|
||||
row = layout.row()
|
||||
|
||||
sub = row.row(align=True)
|
||||
sub.operator("pose.group_assign", text="Assign")
|
||||
# row.operator("pose.bone_group_remove_from", text="Remove")
|
||||
sub.operator("pose.group_unassign", text="Remove")
|
||||
sub.operator("armature.collection_assign", text="Assign")
|
||||
sub.operator("armature.collection_unassign", text="Remove")
|
||||
|
||||
sub = row.row(align=True)
|
||||
sub.operator("pose.group_select", text="Select")
|
||||
sub.operator("pose.group_deselect", text="Deselect")
|
||||
sub.operator("armature.collection_select", text="Select")
|
||||
sub.operator("armature.collection_deselect", text="Deselect")
|
||||
|
||||
|
||||
class DATA_PT_iksolver_itasc(ArmatureButtonsPanel, Panel):
|
||||
@@ -258,9 +246,8 @@ class DATA_PT_custom_props_arm(ArmatureButtonsPanel, PropertyPanel, Panel):
|
||||
classes = (
|
||||
DATA_PT_context_arm,
|
||||
DATA_PT_skeleton,
|
||||
DATA_MT_bone_group_context_menu,
|
||||
DATA_PT_bone_groups,
|
||||
DATA_UL_bone_groups,
|
||||
DATA_PT_bone_collections,
|
||||
DATA_UL_bone_collections,
|
||||
DATA_PT_motion_paths,
|
||||
DATA_PT_motion_paths_display,
|
||||
DATA_PT_display,
|
||||
|
||||
@@ -3815,7 +3815,7 @@ class VIEW3D_MT_pose(Menu):
|
||||
layout.separator()
|
||||
|
||||
layout.menu("VIEW3D_MT_pose_motion")
|
||||
layout.menu("VIEW3D_MT_pose_group")
|
||||
layout.menu("VIEW3D_MT_bone_collections")
|
||||
|
||||
layout.separator()
|
||||
|
||||
@@ -3900,26 +3900,35 @@ class VIEW3D_MT_pose_motion(Menu):
|
||||
layout.operator("pose.paths_clear", text="Clear")
|
||||
|
||||
|
||||
class VIEW3D_MT_pose_group(Menu):
|
||||
bl_label = "Bone Groups"
|
||||
class VIEW3D_MT_bone_collections(Menu):
|
||||
bl_label = "Bone Collections"
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return context.active_object and context.active_object.type == 'ARMATURE'
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
|
||||
pose = context.active_object.pose
|
||||
props = layout.operator("armature.collection_assign",
|
||||
text="Assign to New Collection")
|
||||
props.name = "New Collection"
|
||||
|
||||
layout.operator_context = 'EXEC_AREA'
|
||||
layout.operator("pose.group_assign", text="Assign to New Group").type = 0
|
||||
arm = context.active_object.data
|
||||
if not arm.collections.active:
|
||||
return
|
||||
|
||||
if pose.bone_groups:
|
||||
active_group = pose.bone_groups.active_index + 1
|
||||
layout.operator("pose.group_assign", text="Assign to Group").type = active_group
|
||||
layout.separator()
|
||||
|
||||
layout.separator()
|
||||
layout.operator("armature.collection_assign",
|
||||
text="Assign to '%s'" % arm.collections.active.name)
|
||||
layout.operator("armature.collection_unassign",
|
||||
text="Unassign from '%s'" % arm.collections.active.name)
|
||||
|
||||
# layout.operator_context = 'INVOKE_AREA'
|
||||
layout.operator("pose.group_unassign")
|
||||
layout.operator("pose.group_remove")
|
||||
layout.separator()
|
||||
|
||||
layout.operator("armature.collection_remove",
|
||||
text="Remove Collection '%s'" % arm.collections.active.name)
|
||||
|
||||
|
||||
class VIEW3D_MT_pose_ik(Menu):
|
||||
@@ -8438,7 +8447,7 @@ classes = (
|
||||
VIEW3D_MT_pose_slide,
|
||||
VIEW3D_MT_pose_propagate,
|
||||
VIEW3D_MT_pose_motion,
|
||||
VIEW3D_MT_pose_group,
|
||||
VIEW3D_MT_bone_collections,
|
||||
VIEW3D_MT_pose_ik,
|
||||
VIEW3D_MT_pose_constraints,
|
||||
VIEW3D_MT_pose_names,
|
||||
|
||||
@@ -25,10 +25,121 @@ extern "C" {
|
||||
|
||||
struct bArmature;
|
||||
struct Bone;
|
||||
struct BoneCollection;
|
||||
struct bPoseChannel;
|
||||
struct EditBone;
|
||||
|
||||
/**
|
||||
* Construct a new #BoneCollection with the given name.
|
||||
*
|
||||
* The caller owns the returned pointer.
|
||||
*
|
||||
* You don't typically use this function directly, but rather create a bone collection on a
|
||||
* bArmature.
|
||||
*
|
||||
* \see #ANIM_armature_bonecoll_new
|
||||
*/
|
||||
struct BoneCollection *ANIM_bonecoll_new(const char *name) ATTR_WARN_UNUSED_RESULT;
|
||||
|
||||
/**
|
||||
* Free the bone collection.
|
||||
*
|
||||
* You don't typically need this function, unless you created a bone collection outside the scope
|
||||
* of a bArmature. Normally bone collections are owned (and thus managed) by the armature.
|
||||
*
|
||||
* \see ANIM_armature_bonecoll_remove
|
||||
*/
|
||||
void ANIM_bonecoll_free(struct BoneCollection *bcoll);
|
||||
|
||||
/**
|
||||
* Recalculate the armature & bone runtime data.
|
||||
*
|
||||
* NOTE: this should only be used when the runtime structs on the Armature and Bones are still
|
||||
* empty. Any data allocated there will NOT be freed.
|
||||
*
|
||||
* TODO: move to BKE?
|
||||
*/
|
||||
void ANIM_armature_runtime_refresh(struct bArmature *armature);
|
||||
|
||||
/**
|
||||
* Free armature & bone runtime data.
|
||||
* TODO: move to BKE?
|
||||
*/
|
||||
void ANIM_armature_runtime_free(struct bArmature *armature);
|
||||
|
||||
/**
|
||||
* Add a new bone collection to the given armature.
|
||||
*
|
||||
* The Armature owns the returned pointer.
|
||||
*/
|
||||
struct BoneCollection *ANIM_armature_bonecoll_new(struct bArmature *armature, const char *name);
|
||||
|
||||
/**
|
||||
* Remove a bone collection from the armature.
|
||||
*/
|
||||
void ANIM_armature_bonecoll_remove(struct bArmature *armature, struct BoneCollection *bcoll);
|
||||
|
||||
/**
|
||||
* Set the given bone collection as the active one.
|
||||
*
|
||||
* Pass `nullptr` to clear the active bone collection.
|
||||
*/
|
||||
void ANIM_armature_bonecoll_active_set(struct bArmature *armature, struct BoneCollection *bcoll);
|
||||
|
||||
/**
|
||||
* Set the bone collection with the given index as the active one.
|
||||
*
|
||||
* Pass -1 to clear the active bone collection.
|
||||
*/
|
||||
void ANIM_armature_bonecoll_active_index_set(struct bArmature *armature,
|
||||
int bone_collection_index);
|
||||
|
||||
/**
|
||||
* Move the bone collection by \a step places up/down.
|
||||
*
|
||||
* \return whether the move actually happened.
|
||||
*/
|
||||
bool ANIM_armature_bonecoll_move(struct bArmature *armature,
|
||||
struct BoneCollection *bcoll,
|
||||
int step);
|
||||
|
||||
struct BoneCollection *ANIM_armature_bonecoll_get_by_name(
|
||||
struct bArmature *armature, const char *name) ATTR_WARN_UNUSED_RESULT;
|
||||
|
||||
void ANIM_armature_bonecoll_name_set(struct bArmature *armature,
|
||||
struct BoneCollection *bcoll,
|
||||
const char *name);
|
||||
|
||||
void ANIM_bonecoll_hide(struct BoneCollection *bcoll);
|
||||
|
||||
/**
|
||||
* Assign the bone to the bone collection.
|
||||
*
|
||||
* No-op if the bone is already a member of the collection.
|
||||
*
|
||||
* \return true if the bone was actually assigned, false if not (f.e. when it already was assigned
|
||||
* previously).
|
||||
*/
|
||||
bool ANIM_armature_bonecoll_assign(struct BoneCollection *bcoll, struct Bone *bone);
|
||||
bool ANIM_armature_bonecoll_assign_editbone(struct BoneCollection *bcoll, struct EditBone *ebone);
|
||||
bool ANIM_armature_bonecoll_assign_and_move(struct BoneCollection *bcoll, struct Bone *bone);
|
||||
bool ANIM_armature_bonecoll_unassign(struct BoneCollection *bcoll, struct Bone *bone);
|
||||
bool ANIM_armature_bonecoll_unassign_editbone(struct BoneCollection *bcoll,
|
||||
struct EditBone *ebone);
|
||||
|
||||
/* Assign the edit bone to the armature's active collection. */
|
||||
void ANIM_armature_bonecoll_assign_active(const struct bArmature *armature,
|
||||
struct EditBone *ebone);
|
||||
|
||||
/**
|
||||
* Reconstruct the bone collection memberships, based on the bone runtime data.
|
||||
*
|
||||
* This is needed to transition out of armature edit mode. That removes all bones, and
|
||||
* recreates them from the editbones.
|
||||
*/
|
||||
void ANIM_armature_bonecoll_reconstruct(struct bArmature *armature);
|
||||
|
||||
/*
|
||||
* Armature/Bone Layer abstractions. These functions are intended as the sole
|
||||
* accessors for `bone->layer`, `armature->layer`, etc. to get a grip on which
|
||||
* queries & operations are performed.
|
||||
@@ -38,10 +149,8 @@ struct EditBone;
|
||||
* first step towards replacement.
|
||||
*/
|
||||
|
||||
inline bool ANIM_bonecoll_is_visible(const struct bArmature *armature, const struct Bone *bone)
|
||||
{
|
||||
return armature->layer & bone->layer;
|
||||
}
|
||||
/** Return true when any of the bone's collections is visible. */
|
||||
bool ANIM_bonecoll_is_visible(const struct bArmature *armature, const struct Bone *bone);
|
||||
|
||||
inline bool ANIM_bone_is_visible(const struct bArmature *armature, const struct Bone *bone)
|
||||
{
|
||||
@@ -49,10 +158,14 @@ inline bool ANIM_bone_is_visible(const struct bArmature *armature, const struct
|
||||
return bone_itself_visible && ANIM_bonecoll_is_visible(armature, bone);
|
||||
}
|
||||
|
||||
inline bool ANIM_bonecoll_is_visible_editbone(const struct bArmature *armature,
|
||||
const struct EditBone *ebone)
|
||||
bool ANIM_bonecoll_is_visible_editbone(const struct bArmature *armature,
|
||||
const struct EditBone *ebone);
|
||||
|
||||
inline bool ANIM_bone_is_visible_editbone(const struct bArmature *armature,
|
||||
const struct EditBone *ebone)
|
||||
{
|
||||
return armature->layer & ebone->layer;
|
||||
const bool bone_itself_visible = (ebone->flag & BONE_HIDDEN_A) == 0;
|
||||
return bone_itself_visible && ANIM_bonecoll_is_visible_editbone(armature, ebone);
|
||||
}
|
||||
|
||||
inline bool ANIM_bonecoll_is_visible_pchan(const struct bArmature *armature,
|
||||
@@ -66,18 +179,18 @@ inline bool ANIM_bonecoll_is_visible_actbone(const struct bArmature *armature)
|
||||
return ANIM_bonecoll_is_visible(armature, armature->act_bone);
|
||||
}
|
||||
|
||||
void ANIM_armature_bonecoll_show_all(struct bArmature *armature);
|
||||
void ANIM_armature_bonecoll_hide_all(struct bArmature *armature);
|
||||
|
||||
/* Only used by the Collada I/O code: */
|
||||
void ANIM_armature_enable_layers(struct bArmature *armature, const int layers);
|
||||
void ANIM_armature_disable_all_layers(struct bArmature *armature);
|
||||
void ANIM_bone_set_layer_ebone(struct EditBone *ebone, int layer);
|
||||
void ANIM_bone_set_ebone_layer_from_armature(struct EditBone *ebone,
|
||||
const struct bArmature *armature);
|
||||
void ANIM_armature_ensure_first_layer_enabled(struct bArmature *armature);
|
||||
void ANIM_armature_ensure_layer_enabled_from_bone(struct bArmature *armature,
|
||||
const struct Bone *bone);
|
||||
void ANIM_armature_ensure_layer_enabled_from_ebone(struct bArmature *armature,
|
||||
const struct EditBone *ebone);
|
||||
void ANIM_armature_ensure_layer_enabled_from_pchan(struct bArmature *armature,
|
||||
const struct bPoseChannel *pchan);
|
||||
|
||||
void ANIM_armature_bonecoll_show_from_bone(struct bArmature *armature, const struct Bone *bone);
|
||||
void ANIM_armature_bonecoll_show_from_ebone(struct bArmature *armature,
|
||||
const struct EditBone *ebone);
|
||||
void ANIM_armature_bonecoll_show_from_pchan(struct bArmature *armature,
|
||||
const struct bPoseChannel *pchan);
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -25,18 +25,21 @@ set(LIB
|
||||
bf_blenkernel
|
||||
bf::blenlib
|
||||
bf::dna
|
||||
PRIVATE bf_editor_interface
|
||||
PRIVATE bf::intern::guardedalloc
|
||||
)
|
||||
|
||||
|
||||
blender_add_lib(bf_animrig "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
|
||||
add_library(bf::animrig ALIAS bf_animrig)
|
||||
|
||||
# if(WITH_GTESTS)
|
||||
# set(TEST_SRC
|
||||
# )
|
||||
# set(TEST_LIB
|
||||
# PRIVATE bf::animrig
|
||||
# )
|
||||
# include(GTestTesting)
|
||||
# blender_add_test_lib(bf_animrig_tests "${TEST_SRC}" "${INC};${TEST_INC}" "${INC_SYS}" "${LIB};${TEST_LIB}")
|
||||
# endif()
|
||||
if(WITH_GTESTS)
|
||||
set(TEST_SRC
|
||||
intern/bone_collections_test.cc
|
||||
)
|
||||
set(TEST_LIB
|
||||
PRIVATE bf::animrig
|
||||
)
|
||||
include(GTestTesting)
|
||||
blender_add_test_lib(bf_animrig_tests "${TEST_SRC}" "${INC};${TEST_INC}" "${INC_SYS}" "${LIB};${TEST_LIB}")
|
||||
endif()
|
||||
|
||||
@@ -6,57 +6,416 @@
|
||||
* \ingroup animrig
|
||||
*/
|
||||
|
||||
#include "BLI_linklist.h"
|
||||
#include "BLI_math_color.h"
|
||||
#include "BLI_string.h"
|
||||
#include "BLI_string_utils.h"
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
#include "DNA_armature_types.h"
|
||||
|
||||
#include "BLI_math_bits.h"
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "ANIM_armature_iter.hh"
|
||||
#include "ANIM_bone_collections.h"
|
||||
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
|
||||
using std::strcmp;
|
||||
|
||||
using namespace blender::animrig;
|
||||
|
||||
namespace {
|
||||
|
||||
/** Default flags for new bone collections. */
|
||||
constexpr eBoneCollection_Flag default_flags = BONE_COLLECTION_VISIBLE |
|
||||
BONE_COLLECTION_SELECTABLE;
|
||||
} // namespace
|
||||
|
||||
BoneCollection *ANIM_bonecoll_new(const char *name)
|
||||
{
|
||||
if (name == nullptr || name[0] == '\0') {
|
||||
/* Use a default name if no name was given. */
|
||||
name = "Bones";
|
||||
}
|
||||
|
||||
/* Note: the collection name may change after the collection is added to an
|
||||
* armature, to ensure it is unique within the armature. */
|
||||
std::string alloc_name = std::string(__func__) + "('" + name + "')";
|
||||
BoneCollection *bcoll = MEM_cnew<BoneCollection>(alloc_name.c_str());
|
||||
|
||||
BLI_strncpy(bcoll->name, name, sizeof(bcoll->name));
|
||||
bcoll->flags = default_flags;
|
||||
|
||||
return bcoll;
|
||||
}
|
||||
|
||||
void ANIM_bonecoll_free(BoneCollection *bcoll)
|
||||
{
|
||||
BLI_assert_msg(BLI_listbase_is_empty(&bcoll->bones),
|
||||
"bone collection still has bones assigned to it, will cause dangling pointers in "
|
||||
"bone runtime data");
|
||||
MEM_delete(bcoll);
|
||||
}
|
||||
|
||||
void ANIM_armature_runtime_refresh(bArmature *armature)
|
||||
{
|
||||
ANIM_armature_runtime_free(armature);
|
||||
|
||||
ANIM_armature_bonecoll_active_set(armature, armature->active_collection);
|
||||
|
||||
/* Construct the bone-to-collections mapping. */
|
||||
LISTBASE_FOREACH (BoneCollection *, bcoll, &armature->collections) {
|
||||
LISTBASE_FOREACH (BoneCollectionMember *, member, &bcoll->bones) {
|
||||
BoneCollectionReference *ref = MEM_cnew<BoneCollectionReference>(__func__);
|
||||
ref->bcoll = bcoll;
|
||||
BLI_addtail(&member->bone->runtime.collections, ref);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ANIM_armature_runtime_free(bArmature *armature)
|
||||
{
|
||||
/* Free the bone-to-its-collections mapping. */
|
||||
ANIM_armature_foreach_bone(&armature->bonebase,
|
||||
[&](Bone *bone) { BLI_freelistN(&bone->runtime.collections); });
|
||||
}
|
||||
|
||||
static void bonecoll_ensure_name_unique(bArmature *armature, BoneCollection *bcoll)
|
||||
{
|
||||
BLI_uniquename(&armature->collections,
|
||||
bcoll,
|
||||
bcoll->name,
|
||||
'.',
|
||||
offsetof(BoneCollection, name),
|
||||
sizeof(bcoll->name));
|
||||
}
|
||||
|
||||
BoneCollection *ANIM_armature_bonecoll_new(bArmature *armature, const char *name)
|
||||
{
|
||||
BoneCollection *bcoll = ANIM_bonecoll_new(name);
|
||||
bonecoll_ensure_name_unique(armature, bcoll);
|
||||
BLI_addtail(&armature->collections, bcoll);
|
||||
return bcoll;
|
||||
}
|
||||
|
||||
static void armature_bonecoll_active_clear(bArmature *armature)
|
||||
{
|
||||
armature->runtime.active_collection_index = -1;
|
||||
armature->active_collection = nullptr;
|
||||
}
|
||||
|
||||
void ANIM_armature_bonecoll_active_set(bArmature *armature, BoneCollection *bcoll)
|
||||
{
|
||||
if (bcoll == nullptr) {
|
||||
armature_bonecoll_active_clear(armature);
|
||||
return;
|
||||
}
|
||||
|
||||
const int index = BLI_findindex(&armature->collections, bcoll);
|
||||
if (index == -1) {
|
||||
/* TODO: print warning? Or just ignore this case? */
|
||||
armature_bonecoll_active_clear(armature);
|
||||
return;
|
||||
}
|
||||
|
||||
armature->runtime.active_collection_index = index;
|
||||
armature->active_collection = bcoll;
|
||||
}
|
||||
|
||||
void ANIM_armature_bonecoll_active_index_set(bArmature *armature, const int bone_collection_index)
|
||||
{
|
||||
if (bone_collection_index < 0) {
|
||||
armature_bonecoll_active_clear(armature);
|
||||
return;
|
||||
}
|
||||
|
||||
void *found_link = BLI_findlink(&armature->collections, bone_collection_index);
|
||||
BoneCollection *bcoll = static_cast<BoneCollection *>(found_link);
|
||||
if (bcoll == nullptr) {
|
||||
/* TODO: print warning? Or just ignore this case? */
|
||||
armature_bonecoll_active_clear(armature);
|
||||
return;
|
||||
}
|
||||
|
||||
armature->runtime.active_collection_index = bone_collection_index;
|
||||
armature->active_collection = bcoll;
|
||||
}
|
||||
|
||||
bool ANIM_armature_bonecoll_move(bArmature *armature, BoneCollection *bcoll, const int step)
|
||||
{
|
||||
if (bcoll == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!BLI_listbase_link_move(&armature->collections, bcoll, step)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (bcoll == armature->active_collection) {
|
||||
armature->runtime.active_collection_index = BLI_findindex(&armature->collections, bcoll);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void ANIM_armature_bonecoll_name_set(bArmature *armature, BoneCollection *bcoll, const char *name)
|
||||
{
|
||||
BLI_strncpy(bcoll->name, name, sizeof(bcoll->name));
|
||||
bonecoll_ensure_name_unique(armature, bcoll);
|
||||
}
|
||||
|
||||
void ANIM_armature_bonecoll_remove(bArmature *armature, BoneCollection *bcoll)
|
||||
{
|
||||
LISTBASE_FOREACH_MUTABLE (BoneCollectionMember *, member, &bcoll->bones) {
|
||||
ANIM_armature_bonecoll_unassign(bcoll, member->bone);
|
||||
}
|
||||
if (armature->edbo) {
|
||||
LISTBASE_FOREACH (EditBone *, ebone, armature->edbo) {
|
||||
ANIM_armature_bonecoll_unassign_editbone(bcoll, ebone);
|
||||
}
|
||||
}
|
||||
|
||||
BLI_remlink_safe(&armature->collections, bcoll);
|
||||
ANIM_bonecoll_free(bcoll);
|
||||
|
||||
/* Make sure the active collection is correct. */
|
||||
const int num_collections = BLI_listbase_count(&armature->collections);
|
||||
const int active_index = min_ii(armature->runtime.active_collection_index, num_collections - 1);
|
||||
ANIM_armature_bonecoll_active_index_set(armature, active_index);
|
||||
}
|
||||
|
||||
BoneCollection *ANIM_armature_bonecoll_get_by_name(bArmature *armature, const char *name)
|
||||
{
|
||||
LISTBASE_FOREACH (BoneCollection *, bcoll, &armature->collections) {
|
||||
if (STREQ(bcoll->name, name)) {
|
||||
return bcoll;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void ANIM_bonecoll_hide(BoneCollection *bcoll)
|
||||
{
|
||||
bcoll->flags &= ~BONE_COLLECTION_VISIBLE;
|
||||
}
|
||||
|
||||
/* Store the bone's membership on the collection. */
|
||||
static void add_membership(BoneCollection *bcoll, Bone *bone)
|
||||
{
|
||||
BoneCollectionMember *member = MEM_cnew<BoneCollectionMember>(__func__);
|
||||
member->bone = bone;
|
||||
BLI_addtail(&bcoll->bones, member);
|
||||
}
|
||||
|
||||
bool ANIM_armature_bonecoll_assign(BoneCollection *bcoll, Bone *bone)
|
||||
{
|
||||
/* Precondition check: bail out if already a member. */
|
||||
LISTBASE_FOREACH (BoneCollectionMember *, member, &bcoll->bones) {
|
||||
if (member->bone == bone) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
add_membership(bcoll, bone);
|
||||
|
||||
/* Store reverse membership on the bone. */
|
||||
BoneCollectionReference *ref = MEM_cnew<BoneCollectionReference>(__func__);
|
||||
ref->bcoll = bcoll;
|
||||
BLI_addtail(&bone->runtime.collections, ref);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ANIM_armature_bonecoll_assign_editbone(BoneCollection *bcoll, EditBone *ebone)
|
||||
{
|
||||
/* Precondition check: bail out if already a member. */
|
||||
LISTBASE_FOREACH (BoneCollectionReference *, ref, &ebone->bone_collections) {
|
||||
if (ref->bcoll == bcoll) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/* Store membership on the edit bone. Bones will be rebuilt when the armature
|
||||
* goes out of edit mode, and by then the newly created bones will be added to
|
||||
* the actual collection on the Armature. */
|
||||
BoneCollectionReference *ref = MEM_cnew<BoneCollectionReference>(__func__);
|
||||
ref->bcoll = bcoll;
|
||||
BLI_addtail(&ebone->bone_collections, ref);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ANIM_armature_bonecoll_assign_and_move(BoneCollection *bcoll, Bone *bone)
|
||||
{
|
||||
/* Remove the bone from all its current collections. */
|
||||
LISTBASE_FOREACH_MUTABLE (BoneCollectionReference *, ref, &bone->runtime.collections) {
|
||||
ANIM_armature_bonecoll_unassign(ref->bcoll, bone);
|
||||
}
|
||||
/* Assign the new collection. */
|
||||
return ANIM_armature_bonecoll_assign(bcoll, bone);
|
||||
}
|
||||
|
||||
bool ANIM_armature_bonecoll_unassign(BoneCollection *bcoll, Bone *bone)
|
||||
{
|
||||
bool was_found = false;
|
||||
|
||||
/* Remove membersip from collection. */
|
||||
LISTBASE_FOREACH_MUTABLE (BoneCollectionMember *, member, &bcoll->bones) {
|
||||
if (member->bone == bone) {
|
||||
BLI_freelinkN(&bcoll->bones, member);
|
||||
was_found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Remove reverse membership from the bone.
|
||||
* For data consistency sake, this is always done, regardless of whether the
|
||||
* above loop found the membership. */
|
||||
LISTBASE_FOREACH_MUTABLE (BoneCollectionReference *, ref, &bone->runtime.collections) {
|
||||
if (ref->bcoll == bcoll) {
|
||||
BLI_freelinkN(&bone->runtime.collections, ref);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return was_found;
|
||||
}
|
||||
|
||||
bool ANIM_armature_bonecoll_unassign_editbone(BoneCollection *bcoll, EditBone *ebone)
|
||||
{
|
||||
bool was_found = false;
|
||||
|
||||
/* Edit bone membership is only stored on the edit bone itself. */
|
||||
LISTBASE_FOREACH_MUTABLE (BoneCollectionReference *, ref, &ebone->bone_collections) {
|
||||
if (ref->bcoll == bcoll) {
|
||||
BLI_freelinkN(&ebone->bone_collections, ref);
|
||||
was_found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return was_found;
|
||||
}
|
||||
|
||||
void ANIM_armature_bonecoll_reconstruct(struct bArmature *armature)
|
||||
{
|
||||
/* Remove all the old collection memberships. */
|
||||
LISTBASE_FOREACH (BoneCollection *, bcoll, &armature->collections) {
|
||||
BLI_freelistN(&bcoll->bones);
|
||||
}
|
||||
|
||||
/* For all bones, restore their collection memberships. */
|
||||
ANIM_armature_foreach_bone(&armature->bonebase, [&](Bone *bone) {
|
||||
LISTBASE_FOREACH (BoneCollectionReference *, ref, &bone->runtime.collections) {
|
||||
add_membership(ref->bcoll, bone);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
static bool any_bone_collection_visible(const ListBase /*BoneCollectionRef*/ *collection_refs)
|
||||
{
|
||||
/* Special case: when a bone is not in any collection, it is visible. */
|
||||
if (BLI_listbase_is_empty(collection_refs)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
LISTBASE_FOREACH (const BoneCollectionReference *, bcoll_ref, collection_refs) {
|
||||
const BoneCollection *bcoll = bcoll_ref->bcoll;
|
||||
if (bcoll->flags & BONE_COLLECTION_VISIBLE) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/* TODO: these two functions were originally implemented for armature layers, hence the armature
|
||||
* parameters. These should be removed at some point. */
|
||||
bool ANIM_bonecoll_is_visible(const struct bArmature * /*armature*/, const struct Bone *bone)
|
||||
{
|
||||
return any_bone_collection_visible(&bone->runtime.collections);
|
||||
}
|
||||
bool ANIM_bonecoll_is_visible_editbone(const struct bArmature * /*armature*/,
|
||||
const struct EditBone *ebone)
|
||||
{
|
||||
return any_bone_collection_visible(&ebone->bone_collections);
|
||||
}
|
||||
|
||||
void ANIM_armature_bonecoll_show_all(bArmature *armature)
|
||||
{
|
||||
LISTBASE_FOREACH (BoneCollection *, bcoll, &armature->collections) {
|
||||
bcoll->flags |= BONE_COLLECTION_VISIBLE;
|
||||
}
|
||||
}
|
||||
|
||||
void ANIM_armature_bonecoll_hide_all(bArmature *armature)
|
||||
{
|
||||
LISTBASE_FOREACH (BoneCollection *, bcoll, &armature->collections) {
|
||||
bcoll->flags &= ~BONE_COLLECTION_VISIBLE;
|
||||
}
|
||||
}
|
||||
|
||||
/* ********************************* */
|
||||
/* Armature Layers transitional API. */
|
||||
|
||||
void ANIM_armature_enable_layers(bArmature *armature, const int layers)
|
||||
void ANIM_armature_enable_layers(bArmature *armature, const int /*layers*/)
|
||||
{
|
||||
armature->layer |= layers;
|
||||
}
|
||||
|
||||
void ANIM_armature_disable_all_layers(bArmature *armature)
|
||||
{
|
||||
armature->layer = 0;
|
||||
// TODO: reimplement properly.
|
||||
// armature->layer |= layers;
|
||||
ANIM_armature_bonecoll_show_all(armature);
|
||||
}
|
||||
|
||||
void ANIM_bone_set_layer_ebone(EditBone *ebone, const int layer)
|
||||
{
|
||||
// TODO: reimplement for bone collections.
|
||||
ebone->layer = layer;
|
||||
}
|
||||
|
||||
void ANIM_bone_set_ebone_layer_from_armature(EditBone *ebone, const bArmature *armature)
|
||||
void ANIM_armature_bonecoll_assign_active(const bArmature *armature, EditBone *ebone)
|
||||
{
|
||||
ebone->layer = armature->layer;
|
||||
if (armature->active_collection == nullptr) {
|
||||
/* No active collection, do not assign to any. */
|
||||
printf("ANIM_armature_bonecoll_assign_active(%s, %s): no active collection\n",
|
||||
ebone->name,
|
||||
armature->id.name);
|
||||
return;
|
||||
}
|
||||
|
||||
ANIM_armature_bonecoll_assign_editbone(armature->active_collection, ebone);
|
||||
}
|
||||
|
||||
void ANIM_armature_ensure_first_layer_enabled(bArmature *armature)
|
||||
{
|
||||
armature->layer = 1;
|
||||
}
|
||||
|
||||
void ANIM_armature_ensure_layer_enabled_from_bone(bArmature *armature, const Bone *bone)
|
||||
void ANIM_armature_bonecoll_show_from_bone(bArmature *armature, const Bone *bone)
|
||||
{
|
||||
if (ANIM_bonecoll_is_visible(armature, bone)) {
|
||||
return;
|
||||
}
|
||||
armature->layer |= 1U << bitscan_forward_uint(bone->layer);
|
||||
|
||||
/* Making the first collection visible is enough to make the bone visible.
|
||||
*
|
||||
* Since bones without collection are considered visible,
|
||||
* bone->runtime.collections.first is certainly a valid pointer. */
|
||||
BoneCollectionReference *ref = static_cast<BoneCollectionReference *>(
|
||||
bone->runtime.collections.first);
|
||||
ref->bcoll->flags |= BONE_COLLECTION_VISIBLE;
|
||||
}
|
||||
|
||||
void ANIM_armature_ensure_layer_enabled_from_ebone(bArmature *armature, const EditBone *ebone)
|
||||
void ANIM_armature_bonecoll_show_from_ebone(bArmature *armature, const EditBone *ebone)
|
||||
{
|
||||
if (ANIM_bonecoll_is_visible_editbone(armature, ebone)) {
|
||||
return;
|
||||
}
|
||||
armature->layer |= 1U << bitscan_forward_uint(ebone->layer);
|
||||
|
||||
/* Making the first collection visible is enough to make the bone visible.
|
||||
*
|
||||
* Since bones without collection are considered visible,
|
||||
* ebone->bone_collections.first is certainly a valid pointer. */
|
||||
BoneCollectionReference *ref = static_cast<BoneCollectionReference *>(
|
||||
ebone->bone_collections.first);
|
||||
ref->bcoll->flags |= BONE_COLLECTION_VISIBLE;
|
||||
}
|
||||
|
||||
void ANIM_armature_ensure_layer_enabled_from_pchan(bArmature *armature, const bPoseChannel *pchan)
|
||||
void ANIM_armature_bonecoll_show_from_pchan(bArmature *armature, const bPoseChannel *pchan)
|
||||
{
|
||||
ANIM_armature_ensure_layer_enabled_from_bone(armature, pchan->bone);
|
||||
ANIM_armature_bonecoll_show_from_bone(armature, pchan->bone);
|
||||
}
|
||||
|
||||
115
source/blender/animrig/intern/bone_collections_test.cc
Normal file
115
source/blender/animrig/intern/bone_collections_test.cc
Normal file
@@ -0,0 +1,115 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "BLI_string.h"
|
||||
|
||||
#include "ANIM_bone_collections.h"
|
||||
|
||||
#include "testing/testing.h"
|
||||
|
||||
namespace blender::animrig::tests {
|
||||
|
||||
TEST(ANIM_bone_collections, bonecoll_new_free)
|
||||
{
|
||||
BoneCollection *bcoll = ANIM_bonecoll_new("some name");
|
||||
EXPECT_NE(nullptr, bcoll);
|
||||
EXPECT_EQ("some name", std::string(bcoll->name));
|
||||
EXPECT_TRUE(BLI_listbase_is_empty(&bcoll->bones));
|
||||
EXPECT_EQ(BONE_COLLECTION_VISIBLE | BONE_COLLECTION_SELECTABLE, bcoll->flags);
|
||||
ANIM_bonecoll_free(bcoll);
|
||||
}
|
||||
|
||||
TEST(ANIM_bone_collections, bonecoll_default_name)
|
||||
{
|
||||
{
|
||||
BoneCollection *bcoll = ANIM_bonecoll_new("");
|
||||
EXPECT_EQ("Bones", std::string(bcoll->name));
|
||||
ANIM_bonecoll_free(bcoll);
|
||||
}
|
||||
|
||||
{
|
||||
BoneCollection *bcoll = ANIM_bonecoll_new(nullptr);
|
||||
EXPECT_EQ("Bones", std::string(bcoll->name));
|
||||
ANIM_bonecoll_free(bcoll);
|
||||
}
|
||||
}
|
||||
|
||||
class ANIM_armature_bone_collections : public testing::Test {
|
||||
protected:
|
||||
bArmature arm;
|
||||
Bone bone1, bone2, bone3;
|
||||
|
||||
void SetUp() override
|
||||
{
|
||||
STRNCPY(bone1.name, "bone1");
|
||||
STRNCPY(bone2.name, "bone2");
|
||||
STRNCPY(bone3.name, "bone3");
|
||||
|
||||
memset(&arm, 0, sizeof(arm));
|
||||
bone1.childbase = {nullptr, nullptr};
|
||||
bone2.childbase = {nullptr, nullptr};
|
||||
bone3.childbase = {nullptr, nullptr};
|
||||
|
||||
BLI_addtail(&arm.bonebase, &bone1); /* bone1 is root bone. */
|
||||
BLI_addtail(&arm.bonebase, &bone2); /* bone2 is root bone. */
|
||||
BLI_addtail(&bone2.childbase, &bone3); /* bone3 has bone2 as parent. */
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(ANIM_armature_bone_collections, armature_owned_collections)
|
||||
{
|
||||
BoneCollection *bcoll1 = ANIM_armature_bonecoll_new(&arm, "collection");
|
||||
BoneCollection *bcoll2 = ANIM_armature_bonecoll_new(&arm, "collection");
|
||||
|
||||
EXPECT_EQ(std::string("collection"), std::string(bcoll1->name));
|
||||
EXPECT_EQ(std::string("collection.001"), std::string(bcoll2->name));
|
||||
|
||||
ANIM_armature_bonecoll_remove(&arm, bcoll1);
|
||||
ANIM_armature_bonecoll_remove(&arm, bcoll2);
|
||||
}
|
||||
|
||||
TEST_F(ANIM_armature_bone_collections, bones_assign_unassign)
|
||||
{
|
||||
BoneCollection *bcoll = ANIM_armature_bonecoll_new(&arm, "collection");
|
||||
|
||||
ANIM_armature_bonecoll_assign(bcoll, &bone1);
|
||||
ANIM_armature_bonecoll_assign(bcoll, &bone2);
|
||||
|
||||
ASSERT_EQ(2, BLI_listbase_count(&bcoll->bones)) << "expecting two bones in collection";
|
||||
EXPECT_EQ(&bone1, static_cast<BoneCollectionMember *>(BLI_findlink(&bcoll->bones, 0))->bone);
|
||||
EXPECT_EQ(&bone2, static_cast<BoneCollectionMember *>(BLI_findlink(&bcoll->bones, 1))->bone);
|
||||
|
||||
EXPECT_EQ(bcoll, static_cast<BoneCollectionReference *>(bone1.runtime.collections.first)->bcoll)
|
||||
<< "expecting back-reference to collection in bone1 runtime data";
|
||||
EXPECT_EQ(bcoll, static_cast<BoneCollectionReference *>(bone2.runtime.collections.first)->bcoll)
|
||||
<< "expecting back-reference to collection in bone2 runtime data";
|
||||
|
||||
ANIM_armature_bonecoll_unassign(bcoll, &bone1);
|
||||
ANIM_armature_bonecoll_unassign(bcoll, &bone2);
|
||||
|
||||
EXPECT_EQ(0, BLI_listbase_count(&bone1.runtime.collections))
|
||||
<< "expecting back-references in bone1 runtime data to be cleared when unassigned";
|
||||
EXPECT_EQ(0, BLI_listbase_count(&bone2.runtime.collections))
|
||||
<< "expecting back-references in bone2 runtime data to be cleared when unassigned";
|
||||
|
||||
ANIM_armature_bonecoll_remove(&arm, bcoll);
|
||||
}
|
||||
|
||||
TEST_F(ANIM_armature_bone_collections, bones_assign_remove)
|
||||
{
|
||||
BoneCollection *bcoll = ANIM_armature_bonecoll_new(&arm, "collection");
|
||||
|
||||
ANIM_armature_bonecoll_assign(bcoll, &bone1);
|
||||
ANIM_armature_bonecoll_assign(bcoll, &bone2);
|
||||
ANIM_armature_bonecoll_remove(&arm, bcoll);
|
||||
|
||||
EXPECT_EQ(0, BLI_listbase_count(&bone1.runtime.collections))
|
||||
<< "expecting back-references in bone1 runtime data to be cleared when the collection is "
|
||||
"removed";
|
||||
EXPECT_EQ(0, BLI_listbase_count(&bone2.runtime.collections))
|
||||
<< "expecting back-references in bone2 runtime data to be cleared when the collection is "
|
||||
"removed";
|
||||
}
|
||||
|
||||
} // namespace blender::animrig::tests
|
||||
@@ -104,6 +104,7 @@ typedef struct EditBone {
|
||||
struct EditBone *bbone_child;
|
||||
|
||||
BoneColor color; /* MUST be named the same as in bPoseChannel and Bone structs. */
|
||||
ListBase /*BoneCollectionReference*/ bone_collections;
|
||||
|
||||
/* Used to store temporary data */
|
||||
union {
|
||||
@@ -204,8 +205,6 @@ void BKE_armature_bone_hash_free(struct bArmature *arm);
|
||||
|
||||
bool BKE_armature_bone_flag_test_recursive(const struct Bone *bone, int flag);
|
||||
|
||||
void BKE_armature_refresh_layer_used(struct Depsgraph *depsgraph, struct bArmature *arm);
|
||||
|
||||
/**
|
||||
* Using `vec` with dist to bone `b1 - b2`.
|
||||
*/
|
||||
@@ -569,11 +568,8 @@ void BKE_pchan_bbone_deform_segment_index(const struct bPoseChannel *pchan,
|
||||
int *r_index,
|
||||
float *r_blend_next);
|
||||
|
||||
/* like EBONE_VISIBLE */
|
||||
#define PBONE_VISIBLE(arm, bone) \
|
||||
(CHECK_TYPE_INLINE(arm, bArmature *), \
|
||||
CHECK_TYPE_INLINE(bone, Bone *), \
|
||||
(((bone)->layer & (arm)->layer) && !((bone)->flag & BONE_HIDDEN_P)))
|
||||
/* like EBONE_VISIBLE, be sure to #include "ANIM_bone_collections.h". */
|
||||
#define PBONE_VISIBLE(arm, bone) ANIM_bone_is_visible(arm, bone)
|
||||
|
||||
#define PBONE_SELECTABLE(arm, bone) \
|
||||
(PBONE_VISIBLE(arm, bone) && !((bone)->flag & BONE_UNSELECTABLE))
|
||||
|
||||
@@ -48,6 +48,8 @@
|
||||
#include "BKE_object.h"
|
||||
#include "BKE_scene.h"
|
||||
|
||||
#include "ANIM_bone_collections.h"
|
||||
|
||||
#include "DEG_depsgraph_build.h"
|
||||
#include "DEG_depsgraph_query.h"
|
||||
|
||||
@@ -81,6 +83,10 @@ static void armature_init_data(ID *id)
|
||||
BLI_assert(MEMCMP_STRUCT_AFTER_IS_ZERO(armature, id));
|
||||
|
||||
MEMCPY_STRUCT_AFTER(armature, DNA_struct_default_get(bArmature), id);
|
||||
|
||||
/* Give the Armature its default bone collection. */
|
||||
BoneCollection *default_bonecoll = ANIM_bonecoll_new("");
|
||||
BLI_addhead(&armature->collections, default_bonecoll);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -131,12 +137,32 @@ static void armature_copy_data(Main * /*bmain*/, ID *id_dst, const ID *id_src, c
|
||||
|
||||
armature_dst->edbo = nullptr;
|
||||
armature_dst->act_edbone = nullptr;
|
||||
|
||||
/* Duplicate bone collections & assignments. */
|
||||
BLI_duplicatelist(&armature_dst->collections, &armature_src->collections);
|
||||
LISTBASE_FOREACH (BoneCollection *, bcoll, &armature_dst->collections) {
|
||||
BLI_duplicatelist(&bcoll->bones, &bcoll->bones);
|
||||
LISTBASE_FOREACH (BoneCollectionMember *, member, &bcoll->bones) {
|
||||
member->bone = BKE_armature_find_bone_name(armature_dst, member->bone->name);
|
||||
}
|
||||
}
|
||||
|
||||
ANIM_armature_bonecoll_active_index_set(armature_dst,
|
||||
armature_src->runtime.active_collection_index);
|
||||
ANIM_armature_runtime_refresh(armature_dst);
|
||||
}
|
||||
|
||||
/** Free (or release) any data used by this armature (does not free the armature itself). */
|
||||
static void armature_free_data(ID *id)
|
||||
{
|
||||
bArmature *armature = (bArmature *)id;
|
||||
ANIM_armature_runtime_free(armature);
|
||||
|
||||
/* Free all BoneCollectionMembership objects. */
|
||||
LISTBASE_FOREACH_MUTABLE (BoneCollection *, bcoll, &armature->collections) {
|
||||
BLI_freelistN(&bcoll->bones);
|
||||
}
|
||||
BLI_freelistN(&armature->collections);
|
||||
|
||||
BKE_armature_bone_hash_free(armature);
|
||||
BKE_armature_bonelist_free(&armature->bonebase, false);
|
||||
@@ -190,8 +216,11 @@ static void write_bone(BlendWriter *writer, Bone *bone)
|
||||
/* PATCH for upward compatibility after 2.37+ armature recode */
|
||||
bone->size[0] = bone->size[1] = bone->size[2] = 1.0f;
|
||||
|
||||
/* Write this bone */
|
||||
/* Write this bone, except for its runtime data. */
|
||||
const Bone_Runtime runtime_backup = bone->runtime;
|
||||
memset(&bone->runtime, 0, sizeof(bone->runtime));
|
||||
BLO_write_struct(writer, Bone, bone);
|
||||
bone->runtime = runtime_backup;
|
||||
|
||||
/* Write ID Properties -- and copy this comment EXACTLY for easy finding
|
||||
* of library blocks that implement this. */
|
||||
@@ -205,6 +234,19 @@ static void write_bone(BlendWriter *writer, Bone *bone)
|
||||
}
|
||||
}
|
||||
|
||||
static void write_bone_collection(BlendWriter *writer, BoneCollection *bcoll)
|
||||
{
|
||||
/* Write this bone collection. */
|
||||
BLO_write_struct(writer, BoneCollection, bcoll);
|
||||
BLO_write_struct_list(writer, BoneCollectionMember, &bcoll->bones);
|
||||
|
||||
// /* Write ID Properties -- and copy this comment EXACTLY for easy finding
|
||||
// * of library blocks that implement this. */
|
||||
// if (bcoll->prop) {
|
||||
// IDP_BlendWrite(writer, bcoll->prop);
|
||||
// }
|
||||
}
|
||||
|
||||
static void armature_blend_write(BlendWriter *writer, ID *id, const void *id_address)
|
||||
{
|
||||
bArmature *arm = (bArmature *)id;
|
||||
@@ -216,13 +258,21 @@ static void armature_blend_write(BlendWriter *writer, ID *id, const void *id_add
|
||||
arm->needs_flush_to_id = 0;
|
||||
arm->act_edbone = nullptr;
|
||||
|
||||
const bArmature_Runtime runtime_backup = arm->runtime;
|
||||
memset(&arm->runtime, 0, sizeof(arm->runtime));
|
||||
|
||||
BLO_write_id_struct(writer, bArmature, id_address, &arm->id);
|
||||
BKE_id_blend_write(writer, &arm->id);
|
||||
|
||||
arm->runtime = runtime_backup;
|
||||
|
||||
/* Direct data */
|
||||
LISTBASE_FOREACH (Bone *, bone, &arm->bonebase) {
|
||||
write_bone(writer, bone);
|
||||
}
|
||||
LISTBASE_FOREACH (BoneCollection *, bcoll, &arm->collections) {
|
||||
write_bone_collection(writer, bcoll);
|
||||
}
|
||||
}
|
||||
|
||||
static void direct_link_bones(BlendDataReader *reader, Bone *bone)
|
||||
@@ -241,6 +291,19 @@ static void direct_link_bones(BlendDataReader *reader, Bone *bone)
|
||||
LISTBASE_FOREACH (Bone *, child, &bone->childbase) {
|
||||
direct_link_bones(reader, child);
|
||||
}
|
||||
|
||||
memset(&bone->runtime, 0, sizeof(bone->runtime));
|
||||
}
|
||||
|
||||
static void direct_link_bone_collection(BlendDataReader *reader, BoneCollection *bcoll)
|
||||
{
|
||||
BLO_read_list(reader, &bcoll->bones);
|
||||
LISTBASE_FOREACH (BoneCollectionMember *, member, &bcoll->bones) {
|
||||
BLO_read_data_address(reader, &member->bone);
|
||||
}
|
||||
|
||||
// BLO_read_data_address(reader, &bcoll->prop);
|
||||
// IDP_BlendDataRead(reader, &bcoll->prop);
|
||||
}
|
||||
|
||||
static void armature_blend_read_data(BlendDataReader *reader, ID *id)
|
||||
@@ -256,10 +319,19 @@ static void armature_blend_read_data(BlendDataReader *reader, ID *id)
|
||||
direct_link_bones(reader, bone);
|
||||
}
|
||||
|
||||
BLO_read_list(reader, &arm->collections);
|
||||
LISTBASE_FOREACH (BoneCollection *, bcoll, &arm->collections) {
|
||||
direct_link_bone_collection(reader, bcoll);
|
||||
}
|
||||
BLO_read_data_address(reader, &arm->active_collection);
|
||||
|
||||
BLO_read_data_address(reader, &arm->act_bone);
|
||||
arm->act_edbone = nullptr;
|
||||
|
||||
BKE_armature_bone_hash_make(arm);
|
||||
|
||||
memset(&arm->runtime, 0, sizeof(arm->runtime));
|
||||
ANIM_armature_runtime_refresh(arm);
|
||||
}
|
||||
|
||||
IDTypeInfo IDType_ID_AR = {
|
||||
@@ -329,6 +401,7 @@ void BKE_armature_bonelist_free(ListBase *lb, const bool do_id_user)
|
||||
if (bone->prop) {
|
||||
IDP_FreeProperty_ex(bone->prop, do_id_user);
|
||||
}
|
||||
BLI_freelistN(&bone->runtime.collections);
|
||||
BKE_armature_bonelist_free(&bone->childbase, do_id_user);
|
||||
}
|
||||
|
||||
@@ -362,6 +435,11 @@ static void copy_bonechildren(Bone *bone_dst,
|
||||
bone_dst->prop = IDP_CopyProperty_ex(bone_src->prop, flag);
|
||||
}
|
||||
|
||||
/* Clear the runtime cache of the collection relations, these will be
|
||||
* reconstructed after the entire armature duplication is done. Don't free,
|
||||
* just clear, as these pointers refer to the original and not the copy. */
|
||||
BLI_listbase_clear(&bone_dst->runtime.collections);
|
||||
|
||||
/* Copy this bone's list */
|
||||
BLI_duplicatelist(&bone_dst->childbase, &bone_src->childbase);
|
||||
|
||||
@@ -626,38 +704,7 @@ bool BKE_armature_bone_flag_test_recursive(const Bone *bone, int flag)
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Armature Layer Refresh Used
|
||||
* \{ */
|
||||
|
||||
static void armature_refresh_layer_used_recursive(bArmature *arm, ListBase *bones)
|
||||
{
|
||||
LISTBASE_FOREACH (Bone *, bone, bones) {
|
||||
arm->layer_used |= bone->layer;
|
||||
armature_refresh_layer_used_recursive(arm, &bone->childbase);
|
||||
}
|
||||
}
|
||||
|
||||
void BKE_armature_refresh_layer_used(Depsgraph *depsgraph, bArmature *arm)
|
||||
{
|
||||
if (arm->edbo != nullptr) {
|
||||
/* Don't perform this update when the armature is in edit mode. In that case it should be
|
||||
* handled by ED_armature_edit_refresh_layer_used(). */
|
||||
return;
|
||||
}
|
||||
|
||||
arm->layer_used = 0;
|
||||
armature_refresh_layer_used_recursive(arm, &arm->bonebase);
|
||||
|
||||
if (depsgraph == nullptr || DEG_is_active(depsgraph)) {
|
||||
bArmature *arm_orig = (bArmature *)DEG_get_original_id(&arm->id);
|
||||
arm_orig->layer_used = arm->layer_used;
|
||||
}
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Armature Layer Refresh Used
|
||||
/** \name Bone auto-side name support
|
||||
* \{ */
|
||||
|
||||
bool bone_autoside_name(
|
||||
|
||||
@@ -13,6 +13,8 @@
|
||||
#include "DNA_action_types.h"
|
||||
#include "DNA_armature_types.h"
|
||||
|
||||
#include "ANIM_bone_collections.h"
|
||||
|
||||
namespace blender::bke {
|
||||
|
||||
namespace {
|
||||
|
||||
@@ -11,6 +11,8 @@
|
||||
|
||||
#include "DNA_armature_types.h"
|
||||
|
||||
#include "ANIM_bone_collections.h"
|
||||
|
||||
#include "testing/testing.h"
|
||||
|
||||
namespace blender::bke::tests {
|
||||
@@ -369,9 +371,6 @@ class BKE_armature_find_selected_bones_test : public testing::Test {
|
||||
BLI_addtail(&arm.bonebase, &bone1); /* bone1 is root bone. */
|
||||
BLI_addtail(&arm.bonebase, &bone2); /* bone2 is root bone. */
|
||||
BLI_addtail(&bone2.childbase, &bone3); /* bone3 has bone2 as parent. */
|
||||
|
||||
/* Make sure the armature & its bones are visible, to make them selectable. */
|
||||
arm.layer = bone1.layer = bone2.layer = bone3.layer = 1;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
#include "BLI_set.hh"
|
||||
#include "BLI_string_ref.hh"
|
||||
|
||||
#include "BKE_armature.h"
|
||||
#include "BKE_effect.h"
|
||||
#include "BKE_grease_pencil.hh"
|
||||
#include "BKE_idprop.hh"
|
||||
@@ -42,6 +43,11 @@
|
||||
#include "BKE_scene.h"
|
||||
#include "BKE_tracking.h"
|
||||
|
||||
#include "ANIM_armature_iter.hh"
|
||||
#include "ANIM_bone_collections.h"
|
||||
|
||||
#include "ED_armature.hh"
|
||||
|
||||
#include "BLT_translation.h"
|
||||
|
||||
#include "BLO_read_write.hh"
|
||||
@@ -108,6 +114,110 @@ static void version_bonegroup_migrate_color(Main *bmain)
|
||||
}
|
||||
}
|
||||
|
||||
static void version_bonelayers_to_bonecollections(Main *bmain)
|
||||
{
|
||||
char bcoll_name[MAX_NAME];
|
||||
char custom_prop_name[MAX_NAME];
|
||||
|
||||
LISTBASE_FOREACH (Object *, ob, &bmain->objects) {
|
||||
if (ob->type != OB_ARMATURE || !ob->pose) {
|
||||
continue;
|
||||
}
|
||||
|
||||
bArmature *arm = reinterpret_cast<bArmature *>(ob->data);
|
||||
IDProperty *arm_idprops = IDP_GetProperties(&arm->id, false);
|
||||
|
||||
BLI_assert_msg(arm->edbo == nullptr, "did not expect an Armature to be saved in edit mode");
|
||||
const uint layer_used = arm->layer_used;
|
||||
|
||||
/* Construct a bone collection for each layer that contains at least one bone. */
|
||||
blender::Vector<std::pair<uint, BoneCollection *>> layermask_collection;
|
||||
for (uint layer = 0; layer < 32; ++layer) {
|
||||
const uint layer_mask = 1u << layer;
|
||||
if ((layer_used & layer_mask) == 0) {
|
||||
/* Layer is empty, so no need to convert to collection. */
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Construct a suitable name for this bone layer. */
|
||||
bcoll_name[0] = '\0';
|
||||
if (arm_idprops) {
|
||||
/* See if we can use the layer name from the Bone Manager add-on. This is a popular add-on
|
||||
* for managing bone layers and giving them names. */
|
||||
BLI_snprintf(custom_prop_name, sizeof(custom_prop_name), "layer_name_%u", layer);
|
||||
IDProperty *prop = IDP_GetPropertyFromGroup(arm_idprops, custom_prop_name);
|
||||
if (prop != nullptr && prop->type == IDP_STRING && IDP_String(prop)[0] != '\0') {
|
||||
BLI_snprintf(
|
||||
bcoll_name, sizeof(bcoll_name), "Layer %u - %s", layer + 1, IDP_String(prop));
|
||||
}
|
||||
}
|
||||
if (bcoll_name[0] == '\0') {
|
||||
/* Either there was no name defined in the custom property, or
|
||||
* it was the empty string. */
|
||||
BLI_snprintf(bcoll_name, sizeof(bcoll_name), "Layer %u", layer + 1);
|
||||
}
|
||||
|
||||
/* Create a new bone collection for this layer. */
|
||||
BoneCollection *bcoll = ANIM_armature_bonecoll_new(arm, bcoll_name);
|
||||
layermask_collection.append(std::make_pair(layer_mask, bcoll));
|
||||
|
||||
if ((arm->layer & layer_mask) == 0) {
|
||||
ANIM_bonecoll_hide(bcoll);
|
||||
}
|
||||
}
|
||||
|
||||
/* Iterate over the bones to assign them to their layers. */
|
||||
blender::animrig::ANIM_armature_foreach_bone(&arm->bonebase, [&](Bone *bone) {
|
||||
for (auto layer_bcoll : layermask_collection) {
|
||||
const uint layer_mask = layer_bcoll.first;
|
||||
if ((bone->layer & layer_mask) == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
BoneCollection *bcoll = layer_bcoll.second;
|
||||
ANIM_armature_bonecoll_assign(bcoll, bone);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
static void version_bonegroups_to_bonecollections(Main *bmain)
|
||||
{
|
||||
LISTBASE_FOREACH (Object *, ob, &bmain->objects) {
|
||||
if (ob->type != OB_ARMATURE || !ob->pose) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Convert the bone groups on a bone-by-bone basis. */
|
||||
bArmature *arm = reinterpret_cast<bArmature *>(ob->data);
|
||||
bPose *pose = ob->pose;
|
||||
LISTBASE_FOREACH (bPoseChannel *, pchan, &pose->chanbase) {
|
||||
/* Find the bone group of this pose channel. */
|
||||
const bActionGroup *bgrp = (const bActionGroup *)BLI_findlink(&pose->agroups,
|
||||
(pchan->agrp_index - 1));
|
||||
if (!bgrp) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Get or create the bone collection. */
|
||||
BoneCollection *bcoll = ANIM_armature_bonecoll_get_by_name(arm, bgrp->name);
|
||||
if (!bcoll) {
|
||||
bcoll = ANIM_armature_bonecoll_new(arm, bgrp->name);
|
||||
|
||||
ANIM_bonecoll_hide(bcoll);
|
||||
}
|
||||
|
||||
/* Assign the bone. */
|
||||
ANIM_armature_bonecoll_assign(bcoll, pchan->bone);
|
||||
}
|
||||
|
||||
/* The list of bone groups (pose->agroups) is intentionally left alone here. This will allow
|
||||
* for older versions of Blender to open the file with bone groups intact. Of course the bone
|
||||
* groups will not be updated any more, but this way the data at least survives an accidental
|
||||
* save with Blender 4.0. */
|
||||
}
|
||||
}
|
||||
|
||||
void do_versions_after_linking_400(FileData *fd, Main *bmain)
|
||||
{
|
||||
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 400, 9)) {
|
||||
@@ -183,6 +293,11 @@ void do_versions_after_linking_400(FileData *fd, Main *bmain)
|
||||
if (!DNA_struct_elem_find(fd->filesdna, "bPoseChannel", "BoneColor", "color")) {
|
||||
version_bonegroup_migrate_color(bmain);
|
||||
}
|
||||
|
||||
if (!DNA_struct_elem_find(fd->filesdna, "bArmature", "ListBase", "collections")) {
|
||||
version_bonelayers_to_bonecollections(bmain);
|
||||
version_bonegroups_to_bonecollections(bmain);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1792,14 +1792,14 @@ void DepsgraphNodeBuilder::build_armature(bArmature *armature)
|
||||
build_idproperties(armature->id.properties);
|
||||
build_animdata(&armature->id);
|
||||
build_parameters(&armature->id);
|
||||
/* Make sure pose is up-to-date with armature updates. */
|
||||
bArmature *armature_cow = (bArmature *)get_cow_id(&armature->id);
|
||||
add_operation_node(&armature->id,
|
||||
NodeType::ARMATURE,
|
||||
OperationCode::ARMATURE_EVAL,
|
||||
[armature_cow](::Depsgraph *depsgraph) {
|
||||
BKE_armature_refresh_layer_used(depsgraph, armature_cow);
|
||||
});
|
||||
/* This operation is no longer necessary, as it was updating things with the bone layers (which
|
||||
* got replaced by bone collections). However, it's still used by other depsgraph components as a
|
||||
* dependency, so for now the node itself is kept as a no-op.
|
||||
* TODO: remove this node & the references to it, if eventually it turns out we really don't need
|
||||
* this.
|
||||
*/
|
||||
add_operation_node(
|
||||
&armature->id, NodeType::ARMATURE, OperationCode::ARMATURE_EVAL, [](::Depsgraph *) {});
|
||||
build_armature_bones(&armature->bonebase);
|
||||
}
|
||||
|
||||
|
||||
@@ -31,6 +31,7 @@ set(SRC
|
||||
armature_select.cc
|
||||
armature_skinning.cc
|
||||
armature_utils.cc
|
||||
bone_collections.cc
|
||||
editarmature_undo.cc
|
||||
meshlaplacian.cc
|
||||
pose_edit.cc
|
||||
|
||||
@@ -70,7 +70,7 @@ EditBone *ED_armature_ebone_add(bArmature *arm, const char *name)
|
||||
bone->rad_head = 0.10f;
|
||||
bone->rad_tail = 0.05f;
|
||||
bone->segments = 1;
|
||||
ANIM_bone_set_ebone_layer_from_armature(bone, arm);
|
||||
ANIM_armature_bonecoll_assign_active(arm, bone);
|
||||
|
||||
/* Bendy-Bone parameters */
|
||||
bone->roll1 = 0.0f;
|
||||
@@ -918,13 +918,19 @@ static void copy_pchan(EditBone *src_bone, EditBone *dst_bone, Object *src_ob, O
|
||||
}
|
||||
}
|
||||
|
||||
void ED_armature_ebone_copy(EditBone *dest, const EditBone *source)
|
||||
{
|
||||
memcpy(dest, source, sizeof(*dest));
|
||||
BLI_duplicatelist(&dest->bone_collections, &dest->bone_collections);
|
||||
}
|
||||
|
||||
EditBone *duplicateEditBoneObjects(
|
||||
EditBone *cur_bone, const char *name, ListBase *editbones, Object *src_ob, Object *dst_ob)
|
||||
{
|
||||
EditBone *e_bone = static_cast<EditBone *>(MEM_mallocN(sizeof(EditBone), "addup_editbone"));
|
||||
|
||||
/* Copy data from old bone to new bone */
|
||||
memcpy(e_bone, cur_bone, sizeof(EditBone));
|
||||
ED_armature_ebone_copy(e_bone, cur_bone);
|
||||
|
||||
cur_bone->temp.ebone = e_bone;
|
||||
e_bone->temp.ebone = cur_bone;
|
||||
@@ -1649,8 +1655,6 @@ static int armature_bone_primitive_add_exec(bContext *C, wmOperator *op)
|
||||
add_v3_v3v3(bone->tail, bone->head, imat[2]); /* bone with unit length 1, pointing up Z */
|
||||
}
|
||||
|
||||
ED_armature_edit_refresh_layer_used(static_cast<bArmature *>(obedit->data));
|
||||
|
||||
/* NOTE: notifier might evolve. */
|
||||
WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, obedit);
|
||||
DEG_id_tag_update(&obedit->id, ID_RECALC_SELECT);
|
||||
|
||||
@@ -846,7 +846,6 @@ static int armature_fill_bones_exec(bContext *C, wmOperator *op)
|
||||
}
|
||||
|
||||
/* updates */
|
||||
ED_armature_edit_refresh_layer_used(arm);
|
||||
WM_event_add_notifier(C, NC_OBJECT | ND_POSE, obedit);
|
||||
DEG_id_tag_update(&arm->id, ID_RECALC_COPY_ON_WRITE);
|
||||
|
||||
@@ -1264,7 +1263,6 @@ static int armature_delete_selected_exec(bContext *C, wmOperator * /*op*/)
|
||||
changed_multi = true;
|
||||
|
||||
ED_armature_edit_sync_selection(arm->edbo);
|
||||
ED_armature_edit_refresh_layer_used(arm);
|
||||
BKE_pose_tag_recalc(CTX_data_main(C), obedit->pose);
|
||||
WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, obedit);
|
||||
DEG_id_tag_update(&arm->id, ID_RECALC_SELECT);
|
||||
@@ -1445,7 +1443,6 @@ static int armature_dissolve_selected_exec(bContext *C, wmOperator * /*op*/)
|
||||
if (changed) {
|
||||
changed_multi = true;
|
||||
ED_armature_edit_sync_selection(arm->edbo);
|
||||
ED_armature_edit_refresh_layer_used(arm);
|
||||
WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, obedit);
|
||||
DEG_id_tag_update(&arm->id, ID_RECALC_SELECT);
|
||||
ED_outliner_select_sync_from_edit_bone_tag(C);
|
||||
|
||||
@@ -74,6 +74,17 @@ void ARMATURE_OT_layers_show_all(struct wmOperatorType *ot);
|
||||
void ARMATURE_OT_armature_layers(struct wmOperatorType *ot);
|
||||
void ARMATURE_OT_bone_layers(struct wmOperatorType *ot);
|
||||
|
||||
void ARMATURE_OT_collection_add(struct wmOperatorType *ot);
|
||||
void ARMATURE_OT_collection_remove(struct wmOperatorType *ot);
|
||||
void ARMATURE_OT_collection_move(struct wmOperatorType *ot);
|
||||
void ARMATURE_OT_collection_assign(struct wmOperatorType *ot);
|
||||
void ARMATURE_OT_collection_unassign(struct wmOperatorType *ot);
|
||||
void ARMATURE_OT_collection_select(struct wmOperatorType *ot);
|
||||
void ARMATURE_OT_collection_deselect(struct wmOperatorType *ot);
|
||||
|
||||
void ARMATURE_OT_move_to_collection(struct wmOperatorType *ot);
|
||||
void ARMATURE_OT_assign_to_collection(struct wmOperatorType *ot);
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
|
||||
@@ -49,6 +49,8 @@
|
||||
#include "ED_armature.hh"
|
||||
#include "ED_screen.hh"
|
||||
|
||||
#include "ANIM_bone_collections.h"
|
||||
|
||||
#include "armature_intern.h"
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
|
||||
@@ -63,6 +63,17 @@ void ED_operatortypes_armature()
|
||||
WM_operatortype_append(ARMATURE_OT_armature_layers);
|
||||
WM_operatortype_append(ARMATURE_OT_bone_layers);
|
||||
|
||||
WM_operatortype_append(ARMATURE_OT_collection_add);
|
||||
WM_operatortype_append(ARMATURE_OT_collection_remove);
|
||||
WM_operatortype_append(ARMATURE_OT_collection_move);
|
||||
WM_operatortype_append(ARMATURE_OT_collection_assign);
|
||||
WM_operatortype_append(ARMATURE_OT_collection_unassign);
|
||||
WM_operatortype_append(ARMATURE_OT_collection_select);
|
||||
WM_operatortype_append(ARMATURE_OT_collection_deselect);
|
||||
|
||||
WM_operatortype_append(ARMATURE_OT_move_to_collection);
|
||||
WM_operatortype_append(ARMATURE_OT_assign_to_collection);
|
||||
|
||||
/* POSE */
|
||||
WM_operatortype_append(POSE_OT_hide);
|
||||
WM_operatortype_append(POSE_OT_reveal);
|
||||
|
||||
@@ -50,6 +50,8 @@
|
||||
#include "UI_interface.hh"
|
||||
#include "UI_resources.hh"
|
||||
|
||||
#include "ANIM_bone_collections.h"
|
||||
|
||||
#include "armature_intern.h"
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
@@ -676,7 +678,6 @@ static int separate_armature_exec(bContext *C, wmOperator *op)
|
||||
|
||||
/* 5) restore original conditions */
|
||||
ED_armature_to_edit(static_cast<bArmature *>(ob_old->data));
|
||||
ED_armature_edit_refresh_layer_used(static_cast<bArmature *>(ob_old->data));
|
||||
|
||||
/* parents tips remain selected when connected children are removed. */
|
||||
ED_armature_edit_deselect_all(ob_old);
|
||||
|
||||
@@ -43,6 +43,8 @@
|
||||
|
||||
#include "GPU_select.h"
|
||||
|
||||
#include "ANIM_bone_collections.h"
|
||||
|
||||
#include "armature_intern.h"
|
||||
|
||||
/* utility macros for storing a temp int in the bone (selection flag) */
|
||||
|
||||
@@ -33,6 +33,8 @@
|
||||
|
||||
#include "armature_intern.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Validation
|
||||
* \{ */
|
||||
@@ -72,14 +74,6 @@ void ED_armature_edit_validate_active(bArmature *arm)
|
||||
}
|
||||
}
|
||||
|
||||
void ED_armature_edit_refresh_layer_used(bArmature *arm)
|
||||
{
|
||||
arm->layer_used = 0;
|
||||
LISTBASE_FOREACH (EditBone *, ebo, arm->edbo) {
|
||||
arm->layer_used |= ebo->layer;
|
||||
}
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
@@ -440,6 +434,16 @@ void ED_armature_edit_transform_mirror_update(Object *obedit)
|
||||
/** \name Armature EditMode Conversions
|
||||
* \{ */
|
||||
|
||||
/** Copy the bone collection membership info from the bones to the ebones.
|
||||
*
|
||||
* Operations on eBones (like subdividing, extruding, etc.) will have to deal
|
||||
* with collection assignments of those eBones as well. */
|
||||
static void copy_bonecollection_membership(EditBone *eBone, const Bone *bone)
|
||||
{
|
||||
BLI_assert(BLI_listbase_is_empty(&eBone->bone_collections));
|
||||
BLI_duplicatelist(&eBone->bone_collections, &bone->runtime.collections);
|
||||
}
|
||||
|
||||
/* converts Bones to EditBone list, used for tools as well */
|
||||
static EditBone *make_boneList_recursive(ListBase *edbo,
|
||||
ListBase *bones,
|
||||
@@ -518,6 +522,7 @@ static EditBone *make_boneList_recursive(ListBase *edbo,
|
||||
eBone->bbone_next_flag = curBone->bbone_next_flag;
|
||||
|
||||
eBone->color = curBone->color;
|
||||
copy_bonecollection_membership(eBone, curBone);
|
||||
|
||||
if (curBone->prop) {
|
||||
eBone->prop = IDP_CopyProperty(curBone->prop);
|
||||
@@ -732,6 +737,12 @@ void ED_armature_from_edit(Main *bmain, bArmature *arm)
|
||||
|
||||
newBone->color = eBone->color;
|
||||
|
||||
LISTBASE_FOREACH (BoneCollectionReference *, ref, &eBone->bone_collections) {
|
||||
BoneCollectionReference *newBoneRef = MEM_cnew<BoneCollectionReference>(
|
||||
"ED_armature_from_edit", *ref);
|
||||
BLI_addtail(&newBone->runtime.collections, newBoneRef);
|
||||
}
|
||||
|
||||
if (eBone->prop) {
|
||||
newBone->prop = IDP_CopyProperty(eBone->prop);
|
||||
}
|
||||
@@ -763,6 +774,7 @@ void ED_armature_from_edit(Main *bmain, bArmature *arm)
|
||||
|
||||
/* Finalize definition of rest-pose data (roll, bone_mat, arm_mat, head/tail...). */
|
||||
armature_finalize_restpose(&arm->bonebase, arm->edbo);
|
||||
ANIM_armature_bonecoll_reconstruct(arm);
|
||||
|
||||
BKE_armature_bone_hash_make(arm);
|
||||
|
||||
@@ -787,6 +799,7 @@ void ED_armature_edit_free(bArmature *arm)
|
||||
if (eBone->prop) {
|
||||
IDP_FreeProperty(eBone->prop);
|
||||
}
|
||||
BLI_freelistN(&eBone->bone_collections);
|
||||
}
|
||||
|
||||
BLI_freelistN(arm->edbo);
|
||||
@@ -852,6 +865,18 @@ void ED_armature_ebone_listbase_copy(ListBase *lb_dst, ListBase *lb_src, const b
|
||||
if (ebone_dst->bbone_prev) {
|
||||
ebone_dst->bbone_prev = ebone_dst->bbone_prev->temp.ebone;
|
||||
}
|
||||
|
||||
/* TODO: WORKAROUND: this is a temporary hack to avoid segfaults when
|
||||
* undoing, because bone collections are not handled properly by the
|
||||
* armature undo code yet. This just discards all collection membership
|
||||
* data to avoid dangling references. This MUST be addressed properly
|
||||
* before release.
|
||||
* See: https://projects.blender.org/blender/blender/pulls/109976#issuecomment-1008429
|
||||
*/
|
||||
BoneCollectionReference *bcoll_ref = (BoneCollectionReference *)(&ebone_dst->bone_collections);
|
||||
bcoll_ref->next = nullptr;
|
||||
bcoll_ref->prev = nullptr;
|
||||
bcoll_ref->bcoll = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
783
source/blender/editors/armature/bone_collections.cc
Normal file
783
source/blender/editors/armature/bone_collections.cc
Normal file
@@ -0,0 +1,783 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup edarmature
|
||||
* Implementation of Bone Collection operators and editing API's.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "ANIM_bone_collections.h"
|
||||
|
||||
#include "DNA_ID.h"
|
||||
#include "DNA_object_types.h"
|
||||
|
||||
#include "BKE_context.h"
|
||||
#include "BKE_layer.h"
|
||||
|
||||
#include "DEG_depsgraph.h"
|
||||
|
||||
#include "RNA_access.hh"
|
||||
#include "RNA_define.hh"
|
||||
#include "RNA_enum_types.hh"
|
||||
|
||||
#include "WM_api.hh"
|
||||
#include "WM_types.hh"
|
||||
|
||||
#include "ED_armature.hh"
|
||||
#include "ED_object.hh"
|
||||
#include "ED_outliner.hh"
|
||||
#include "ED_screen.hh"
|
||||
|
||||
#include "UI_interface.hh"
|
||||
#include "UI_resources.hh"
|
||||
|
||||
#include "armature_intern.h"
|
||||
|
||||
struct wmOperator;
|
||||
|
||||
/* ********************************************** */
|
||||
/* Bone collections */
|
||||
|
||||
static bool bone_collection_poll(bContext *C)
|
||||
{
|
||||
Object *ob = ED_object_context(C);
|
||||
if (ob == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (ID_IS_OVERRIDE_LIBRARY(ob)) {
|
||||
CTX_wm_operator_poll_msg_set(C, "Cannot edit bone collections for library overrides");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (ob->type != OB_ARMATURE) {
|
||||
CTX_wm_operator_poll_msg_set(C, "Bone collections can only be edited on an Armature");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool active_bone_collection_poll(bContext *C)
|
||||
{
|
||||
if (!bone_collection_poll(C)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Object *ob = ED_object_context(C);
|
||||
if (ob == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bArmature *armature = static_cast<bArmature *>(ob->data);
|
||||
if (armature->active_collection == nullptr) {
|
||||
CTX_wm_operator_poll_msg_set(C, "Armature has no active bone collection, select one first");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static int bone_collection_add_exec(bContext *C, wmOperator * /* op */)
|
||||
{
|
||||
Object *ob = ED_object_context(C);
|
||||
if (ob == nullptr) {
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
bArmature *armature = static_cast<bArmature *>(ob->data);
|
||||
ANIM_armature_bonecoll_new(armature, nullptr);
|
||||
|
||||
WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob);
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
void ARMATURE_OT_collection_add(wmOperatorType *ot)
|
||||
{
|
||||
/* identifiers */
|
||||
ot->name = "Add Bone Collection";
|
||||
ot->idname = "ARMATURE_OT_collection_add";
|
||||
ot->description = "Add a new bone collection";
|
||||
|
||||
/* api callbacks */
|
||||
ot->exec = bone_collection_add_exec;
|
||||
ot->poll = bone_collection_poll;
|
||||
|
||||
/* flags */
|
||||
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
||||
}
|
||||
|
||||
static int bone_collection_remove_exec(bContext *C, wmOperator * /* op */)
|
||||
{
|
||||
Object *ob = ED_object_context(C);
|
||||
if (ob == nullptr) {
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
/* The poll function ensures armature->active_collection is not NULL. */
|
||||
bArmature *armature = static_cast<bArmature *>(ob->data);
|
||||
ANIM_armature_bonecoll_remove(armature, armature->active_collection);
|
||||
|
||||
/* notifiers for updates */
|
||||
WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob);
|
||||
DEG_id_tag_update(&armature->id, ID_RECALC_SELECT);
|
||||
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
void ARMATURE_OT_collection_remove(wmOperatorType *ot)
|
||||
{
|
||||
/* identifiers */
|
||||
ot->name = "Remove Bone Collection";
|
||||
ot->idname = "ARMATURE_OT_collection_remove";
|
||||
ot->description = "Remove the active bone collection";
|
||||
|
||||
/* api callbacks */
|
||||
ot->exec = bone_collection_remove_exec;
|
||||
ot->poll = active_bone_collection_poll;
|
||||
|
||||
/* flags */
|
||||
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
||||
}
|
||||
|
||||
static int bone_collection_move_exec(bContext *C, wmOperator *op)
|
||||
{
|
||||
Object *ob = ED_object_context(C);
|
||||
if (ob == nullptr) {
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
const int direction = RNA_enum_get(op->ptr, "direction");
|
||||
|
||||
/* Poll function makes sure this is valid. */
|
||||
bArmature *armature = static_cast<bArmature *>(ob->data);
|
||||
|
||||
const bool ok = ANIM_armature_bonecoll_move(armature, armature->active_collection, direction);
|
||||
if (!ok) {
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob);
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
void ARMATURE_OT_collection_move(wmOperatorType *ot)
|
||||
{
|
||||
static const EnumPropertyItem bcoll_slot_move[] = {
|
||||
{-1, "UP", 0, "Up", ""},
|
||||
{1, "DOWN", 0, "Down", ""},
|
||||
{0, NULL, 0, NULL, NULL},
|
||||
};
|
||||
|
||||
/* identifiers */
|
||||
ot->name = "Move Bone Collection";
|
||||
ot->idname = "ARMATURE_OT_collection_move";
|
||||
ot->description = "Change position of active Bone Collection in list of Bone collections";
|
||||
|
||||
/* api callbacks */
|
||||
ot->exec = bone_collection_move_exec;
|
||||
ot->poll = active_bone_collection_poll;
|
||||
|
||||
/* flags */
|
||||
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
||||
|
||||
RNA_def_enum(ot->srna,
|
||||
"direction",
|
||||
bcoll_slot_move,
|
||||
0,
|
||||
"Direction",
|
||||
"Direction to move the active Bone Collection towards");
|
||||
}
|
||||
|
||||
typedef enum eMayCreate {
|
||||
FAIL_IF_MISSING = 0,
|
||||
CREATE_IF_MISSING = 1,
|
||||
} eMayCreate;
|
||||
|
||||
static BoneCollection *get_bonecoll_named_or_active(bContext * /*C*/,
|
||||
wmOperator *op,
|
||||
Object *ob,
|
||||
const eMayCreate may_create)
|
||||
{
|
||||
bArmature *armature = static_cast<bArmature *>(ob->data);
|
||||
|
||||
char bcoll_name[MAX_NAME];
|
||||
RNA_string_get(op->ptr, "name", bcoll_name);
|
||||
|
||||
if (bcoll_name[0] == '\0') {
|
||||
return armature->active_collection;
|
||||
}
|
||||
|
||||
BoneCollection *bcoll = ANIM_armature_bonecoll_get_by_name(armature, bcoll_name);
|
||||
if (bcoll) {
|
||||
return bcoll;
|
||||
}
|
||||
|
||||
switch (may_create) {
|
||||
case CREATE_IF_MISSING:
|
||||
bcoll = ANIM_armature_bonecoll_new(armature, bcoll_name);
|
||||
ANIM_armature_bonecoll_active_set(armature, bcoll);
|
||||
return bcoll;
|
||||
case FAIL_IF_MISSING:
|
||||
WM_reportf(RPT_ERROR, "No bone collection named '%s'", bcoll_name);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
using assign_bone_func = bool (*)(BoneCollection *bcoll, Bone *bone);
|
||||
using assign_ebone_func = bool (*)(BoneCollection *bcoll, EditBone *ebone);
|
||||
|
||||
/* The following 3 functions either assign or unassign, depending on the
|
||||
* 'assign_bone_func'/'assign_ebone_func' they get passed. */
|
||||
|
||||
static void bone_collection_assign_pchans(bContext *C,
|
||||
Object *ob,
|
||||
BoneCollection *bcoll,
|
||||
assign_bone_func assign_func,
|
||||
bool *made_any_changes,
|
||||
bool *had_bones_to_assign)
|
||||
{
|
||||
/* TODO: support multi-object pose mode. */
|
||||
FOREACH_PCHAN_SELECTED_IN_OBJECT_BEGIN (ob, pchan) {
|
||||
*made_any_changes |= assign_func(bcoll, pchan->bone);
|
||||
*had_bones_to_assign = true;
|
||||
}
|
||||
FOREACH_PCHAN_SELECTED_IN_OBJECT_END;
|
||||
|
||||
WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob);
|
||||
|
||||
bArmature *arm = static_cast<bArmature *>(ob->data);
|
||||
DEG_id_tag_update(&arm->id, ID_RECALC_SELECT); /* Recreate the draw buffers. */
|
||||
}
|
||||
|
||||
static void bone_collection_assign_editbones(bContext *C,
|
||||
Object *ob,
|
||||
BoneCollection *bcoll,
|
||||
assign_ebone_func assign_func,
|
||||
bool *made_any_changes,
|
||||
bool *had_bones_to_assign)
|
||||
{
|
||||
bArmature *arm = static_cast<bArmature *>(ob->data);
|
||||
ED_armature_edit_sync_selection(arm->edbo);
|
||||
|
||||
LISTBASE_FOREACH (EditBone *, ebone, arm->edbo) {
|
||||
if (!EBONE_EDITABLE(ebone)) {
|
||||
continue;
|
||||
}
|
||||
*made_any_changes |= assign_func(bcoll, ebone);
|
||||
*had_bones_to_assign = true;
|
||||
}
|
||||
|
||||
ED_armature_edit_sync_selection(arm->edbo);
|
||||
WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, ob);
|
||||
DEG_id_tag_update(&ob->id, ID_RECALC_COPY_ON_WRITE);
|
||||
}
|
||||
|
||||
/* Returns whether the current mode is actually supported. */
|
||||
static bool bone_collection_assign_mode_specific(bContext *C,
|
||||
Object *ob,
|
||||
BoneCollection *bcoll,
|
||||
assign_bone_func assign_bone_func,
|
||||
assign_ebone_func assign_ebone_func,
|
||||
bool *made_any_changes,
|
||||
bool *had_bones_to_assign)
|
||||
{
|
||||
switch (CTX_data_mode_enum(C)) {
|
||||
case CTX_MODE_POSE: {
|
||||
bone_collection_assign_pchans(
|
||||
C, ob, bcoll, assign_bone_func, made_any_changes, had_bones_to_assign);
|
||||
return true;
|
||||
}
|
||||
|
||||
case CTX_MODE_EDIT_ARMATURE: {
|
||||
uint objects_len = 0;
|
||||
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(
|
||||
CTX_data_scene(C), CTX_data_view_layer(C), CTX_wm_view3d(C), &objects_len);
|
||||
|
||||
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
|
||||
Object *ob = objects[ob_index];
|
||||
bone_collection_assign_editbones(
|
||||
C, ob, bcoll, assign_ebone_func, made_any_changes, had_bones_to_assign);
|
||||
}
|
||||
|
||||
MEM_freeN(objects);
|
||||
ED_outliner_select_sync_from_edit_bone_tag(C);
|
||||
return true;
|
||||
}
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/* Assign selected pchans to the bone collection that the user selects */
|
||||
static int bone_collection_assign_exec(bContext *C, wmOperator *op)
|
||||
{
|
||||
Object *ob = ED_object_context(C);
|
||||
if (ob == nullptr) {
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
BoneCollection *bcoll = get_bonecoll_named_or_active(C, op, ob, CREATE_IF_MISSING);
|
||||
if (bcoll == nullptr) {
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
bool made_any_changes = false;
|
||||
bool had_bones_to_assign = false;
|
||||
const bool mode_is_supported = bone_collection_assign_mode_specific(
|
||||
C,
|
||||
ob,
|
||||
bcoll,
|
||||
ANIM_armature_bonecoll_assign,
|
||||
ANIM_armature_bonecoll_assign_editbone,
|
||||
&made_any_changes,
|
||||
&had_bones_to_assign);
|
||||
|
||||
if (!mode_is_supported) {
|
||||
WM_report(RPT_ERROR, "This operator only works in pose mode and armature edit mode");
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
if (!had_bones_to_assign) {
|
||||
WM_report(RPT_WARNING, "No bones selected, nothing to assign to bone collection");
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
if (!made_any_changes) {
|
||||
WM_report(RPT_WARNING, "All selected bones were already part of this collection");
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
void ARMATURE_OT_collection_assign(wmOperatorType *ot)
|
||||
{
|
||||
/* identifiers */
|
||||
ot->name = "Add Selected Bones to Collection";
|
||||
ot->idname = "ARMATURE_OT_collection_assign";
|
||||
ot->description = "Add selected bones to the chosen bone collection";
|
||||
|
||||
/* api callbacks */
|
||||
// TODO: reinstate the menu?
|
||||
// ot->invoke = bone_collections_menu_invoke;
|
||||
ot->exec = bone_collection_assign_exec;
|
||||
ot->poll = bone_collection_poll;
|
||||
|
||||
/* flags */
|
||||
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
||||
|
||||
/* properties */
|
||||
RNA_def_string(ot->srna,
|
||||
"name",
|
||||
NULL,
|
||||
MAX_NAME,
|
||||
"Bone Collection",
|
||||
"Name of the bone collection to assign this bone to; empty to assign to the "
|
||||
"active bone collection");
|
||||
}
|
||||
|
||||
static int bone_collection_unassign_exec(bContext *C, wmOperator *op)
|
||||
{
|
||||
Object *ob = ED_object_context(C);
|
||||
if (ob == nullptr) {
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
BoneCollection *bcoll = get_bonecoll_named_or_active(C, op, ob, FAIL_IF_MISSING);
|
||||
if (bcoll == nullptr) {
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
bool made_any_changes = false;
|
||||
bool had_bones_to_unassign = false;
|
||||
const bool mode_is_supported = bone_collection_assign_mode_specific(
|
||||
C,
|
||||
ob,
|
||||
bcoll,
|
||||
ANIM_armature_bonecoll_unassign,
|
||||
ANIM_armature_bonecoll_unassign_editbone,
|
||||
&made_any_changes,
|
||||
&had_bones_to_unassign);
|
||||
|
||||
if (!mode_is_supported) {
|
||||
WM_report(RPT_ERROR, "This operator only works in pose mode and armature edit mode");
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
if (!had_bones_to_unassign) {
|
||||
WM_report(RPT_WARNING, "No bones selected, nothing to unassign from bone collection");
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
if (!made_any_changes) {
|
||||
WM_report(RPT_WARNING, "None of the selected bones were assigned to this collection");
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
void ARMATURE_OT_collection_unassign(wmOperatorType *ot)
|
||||
{
|
||||
/* identifiers */
|
||||
ot->name = "Remove Selected from Bone collections";
|
||||
ot->idname = "ARMATURE_OT_collection_unassign";
|
||||
ot->description = "Remove selected bones from the active bone collection";
|
||||
|
||||
/* api callbacks */
|
||||
ot->exec = bone_collection_unassign_exec;
|
||||
ot->poll = bone_collection_poll;
|
||||
|
||||
/* flags */
|
||||
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
||||
|
||||
RNA_def_string(ot->srna,
|
||||
"name",
|
||||
NULL,
|
||||
MAX_NAME,
|
||||
"Bone Collection",
|
||||
"Name of the bone collection to unassign this bone from; empty to unassign from "
|
||||
"the active bone collection");
|
||||
}
|
||||
|
||||
static bool editbone_is_member(const EditBone *ebone, const BoneCollection *bcoll)
|
||||
{
|
||||
LISTBASE_FOREACH (BoneCollectionReference *, ref, &ebone->bone_collections) {
|
||||
if (ref->bcoll == bcoll) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static void bone_collection_select(bContext *C,
|
||||
Object *ob,
|
||||
BoneCollection *bcoll,
|
||||
const bool select)
|
||||
{
|
||||
bArmature *armature = static_cast<bArmature *>(ob->data);
|
||||
const bool is_editmode = armature->edbo != nullptr;
|
||||
|
||||
if (is_editmode) {
|
||||
LISTBASE_FOREACH (EditBone *, ebone, armature->edbo) {
|
||||
if (!EBONE_SELECTABLE(armature, ebone)) {
|
||||
continue;
|
||||
}
|
||||
if (!editbone_is_member(ebone, bcoll)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (select) {
|
||||
ebone->flag |= BONE_SELECTED;
|
||||
}
|
||||
else {
|
||||
ebone->flag &= ~BONE_SELECTED;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
LISTBASE_FOREACH (BoneCollectionMember *, member, &bcoll->bones) {
|
||||
Bone *bone = member->bone;
|
||||
if (!ANIM_bone_is_visible(armature, bone)) {
|
||||
continue;
|
||||
}
|
||||
if (bone->flag & BONE_UNSELECTABLE) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (select) {
|
||||
bone->flag |= BONE_SELECTED;
|
||||
}
|
||||
else {
|
||||
bone->flag &= ~BONE_SELECTED;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DEG_id_tag_update(&armature->id, ID_RECALC_SELECT);
|
||||
WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob);
|
||||
|
||||
if (is_editmode) {
|
||||
ED_outliner_select_sync_from_edit_bone_tag(C);
|
||||
}
|
||||
else {
|
||||
ED_outliner_select_sync_from_pose_bone_tag(C);
|
||||
}
|
||||
}
|
||||
|
||||
static int bone_collection_select_exec(bContext *C, wmOperator *op)
|
||||
{
|
||||
Object *ob = ED_object_context(C);
|
||||
if (ob == nullptr) {
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
BoneCollection *bcoll = get_bonecoll_named_or_active(C, op, ob, FAIL_IF_MISSING);
|
||||
if (bcoll == nullptr) {
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
bone_collection_select(C, ob, bcoll, true);
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
void ARMATURE_OT_collection_select(wmOperatorType *ot)
|
||||
{
|
||||
/* identifiers */
|
||||
ot->name = "Select Bones of Bone Collection";
|
||||
ot->idname = "ARMATURE_OT_collection_select";
|
||||
ot->description = "Select bones in active Bone Collection";
|
||||
|
||||
/* api callbacks */
|
||||
ot->exec = bone_collection_select_exec;
|
||||
ot->poll = bone_collection_poll;
|
||||
|
||||
/* flags */
|
||||
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
||||
|
||||
PropertyRNA *prop = RNA_def_string(
|
||||
ot->srna,
|
||||
"name",
|
||||
NULL,
|
||||
MAX_NAME,
|
||||
"Bone Collection",
|
||||
"Name of the bone collection to select bones from; empty use the active bone collection");
|
||||
RNA_def_property_flag(prop, PROP_HIDDEN);
|
||||
}
|
||||
|
||||
static int bone_collection_deselect_exec(bContext *C, wmOperator *op)
|
||||
{
|
||||
Object *ob = ED_object_context(C);
|
||||
if (ob == nullptr) {
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
BoneCollection *bcoll = get_bonecoll_named_or_active(C, op, ob, FAIL_IF_MISSING);
|
||||
if (bcoll == nullptr) {
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
bone_collection_select(C, ob, bcoll, false);
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
void ARMATURE_OT_collection_deselect(wmOperatorType *ot)
|
||||
{
|
||||
/* identifiers */
|
||||
ot->name = "Deselect Bone Collection";
|
||||
ot->idname = "ARMATURE_OT_collection_deselect";
|
||||
ot->description = "Deselect bones of active Bone Collection";
|
||||
|
||||
/* api callbacks */
|
||||
ot->exec = bone_collection_deselect_exec;
|
||||
ot->poll = bone_collection_poll;
|
||||
|
||||
/* flags */
|
||||
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
||||
|
||||
PropertyRNA *prop = RNA_def_string(
|
||||
ot->srna,
|
||||
"name",
|
||||
NULL,
|
||||
MAX_NAME,
|
||||
"Bone Collection",
|
||||
"Name of the bone collection to deselect bones from; empty use the active bone collection");
|
||||
RNA_def_property_flag(prop, PROP_HIDDEN);
|
||||
}
|
||||
|
||||
/* -------------------------- */
|
||||
|
||||
using assign_func = bool (*)(BoneCollection *, Bone *);
|
||||
|
||||
static int add_or_move_to_collection_exec(bContext *C,
|
||||
wmOperator *op,
|
||||
const assign_func assign_func)
|
||||
{
|
||||
Object *obpose = ED_pose_object_from_context(C);
|
||||
bArmature *arm = static_cast<bArmature *>(obpose->data);
|
||||
const int collection_index = RNA_enum_get(op->ptr, "collection");
|
||||
BoneCollection *target_bcoll;
|
||||
|
||||
if (collection_index < 0) {
|
||||
char new_collection_name[MAX_NAME];
|
||||
RNA_string_get(op->ptr, "new_collection_name", new_collection_name);
|
||||
target_bcoll = ANIM_armature_bonecoll_new(arm, new_collection_name);
|
||||
BLI_assert_msg(target_bcoll,
|
||||
"It should always be possible to create a new bone collection on an armature");
|
||||
ANIM_armature_bonecoll_active_set(arm, target_bcoll);
|
||||
}
|
||||
else {
|
||||
target_bcoll = static_cast<BoneCollection *>(
|
||||
BLI_findlink(&arm->collections, collection_index));
|
||||
if (target_bcoll == nullptr) {
|
||||
WM_reportf(RPT_ERROR,
|
||||
"Bone collection with index %d not found on Armature %s",
|
||||
collection_index,
|
||||
arm->id.name + 2);
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
}
|
||||
|
||||
FOREACH_PCHAN_SELECTED_IN_OBJECT_BEGIN (obpose, pchan) {
|
||||
assign_func(target_bcoll, pchan->bone);
|
||||
}
|
||||
FOREACH_PCHAN_SELECTED_IN_OBJECT_END;
|
||||
|
||||
DEG_id_tag_update(&arm->id, ID_RECALC_SELECT); /* Recreate the draw buffers. */
|
||||
|
||||
WM_event_add_notifier(C, NC_OBJECT | ND_DATA, obpose);
|
||||
WM_event_add_notifier(C, NC_OBJECT | ND_POSE, obpose);
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
static int move_to_collection_exec(bContext *C, wmOperator *op)
|
||||
{
|
||||
return add_or_move_to_collection_exec(C, op, ANIM_armature_bonecoll_assign_and_move);
|
||||
}
|
||||
|
||||
static int assign_to_collection_exec(bContext *C, wmOperator *op)
|
||||
{
|
||||
return add_or_move_to_collection_exec(C, op, ANIM_armature_bonecoll_assign);
|
||||
}
|
||||
|
||||
static bool move_to_collection_poll(bContext *C)
|
||||
{
|
||||
/* TODO: add outliner support.
|
||||
if (CTX_wm_space_outliner(C) != nullptr) {
|
||||
return ED_outliner_collections_editor_poll(C);
|
||||
}
|
||||
*/
|
||||
// TODO: add armature edit mode support.
|
||||
return ED_operator_object_active_local_editable_posemode_exclusive(C);
|
||||
}
|
||||
|
||||
static const EnumPropertyItem *bone_collection_enum_items(bContext *C,
|
||||
PointerRNA * /*ptr*/,
|
||||
PropertyRNA * /*prop*/,
|
||||
bool *r_free)
|
||||
{
|
||||
Object *obpose = ED_pose_object_from_context(C);
|
||||
bArmature *arm = static_cast<bArmature *>(obpose->data);
|
||||
|
||||
EnumPropertyItem *item = nullptr, item_tmp = {0};
|
||||
int totitem = 0;
|
||||
int bcoll_index = 0;
|
||||
|
||||
LISTBASE_FOREACH_INDEX (BoneCollection *, bcoll, &arm->collections, bcoll_index) {
|
||||
item_tmp.identifier = bcoll->name;
|
||||
item_tmp.name = bcoll->name;
|
||||
item_tmp.value = bcoll_index;
|
||||
RNA_enum_item_add(&item, &totitem, &item_tmp);
|
||||
}
|
||||
|
||||
RNA_enum_item_add_separator(&item, &totitem);
|
||||
|
||||
/* New Collection. */
|
||||
item_tmp.identifier = "__NEW__";
|
||||
item_tmp.name = "New Collection";
|
||||
item_tmp.value = -1;
|
||||
RNA_enum_item_add(&item, &totitem, &item_tmp);
|
||||
|
||||
RNA_enum_item_end(&item, &totitem);
|
||||
*r_free = true;
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
static int move_to_collection_invoke(bContext *C, wmOperator *op, const wmEvent * /*event*/)
|
||||
{
|
||||
PropertyRNA *prop = RNA_struct_find_property(op->ptr, "collection");
|
||||
if (RNA_property_is_set(op->ptr, prop)) {
|
||||
const int collection_index = RNA_property_enum_get(op->ptr, prop);
|
||||
if (collection_index < 0) {
|
||||
return WM_operator_props_dialog_popup(C, op, 200);
|
||||
}
|
||||
/* Either call move_to_collection_exec() or assign_to_collection_exec(), depending on which
|
||||
* operator got invoked. */
|
||||
return op->type->exec(C, op);
|
||||
}
|
||||
|
||||
uiPopupMenu *pup = UI_popup_menu_begin(C, op->type->name, ICON_NONE);
|
||||
uiLayout *layout = UI_popup_menu_layout(pup);
|
||||
uiLayoutSetOperatorContext(layout, WM_OP_INVOKE_DEFAULT);
|
||||
uiItemsEnumO(layout, op->idname, "collection");
|
||||
UI_popup_menu_end(C, pup);
|
||||
return OPERATOR_INTERFACE;
|
||||
}
|
||||
|
||||
void ARMATURE_OT_move_to_collection(wmOperatorType *ot)
|
||||
{
|
||||
PropertyRNA *prop;
|
||||
|
||||
/* identifiers */
|
||||
ot->name = "Move to Collection";
|
||||
ot->description = "Move bones to a collection";
|
||||
ot->idname = "ARMATURE_OT_move_to_collection";
|
||||
|
||||
/* api callbacks */
|
||||
ot->exec = move_to_collection_exec;
|
||||
ot->invoke = move_to_collection_invoke;
|
||||
ot->poll = move_to_collection_poll;
|
||||
|
||||
/* Flags don't include OPTYPE_REGISTER, as the redo panel doesn't make much sense for this
|
||||
* operator. The visibility of the RNA properties is determined by the needs of the 'New Catalog'
|
||||
* popup, so that a name can be entered. This means that the redo panel would also only show the
|
||||
* 'Name' property, without any choice for another collection. */
|
||||
ot->flag = OPTYPE_UNDO;
|
||||
|
||||
prop = RNA_def_enum(ot->srna,
|
||||
"collection",
|
||||
rna_enum_dummy_DEFAULT_items,
|
||||
0,
|
||||
"Collection",
|
||||
"The bone collection to move the selected bones to");
|
||||
RNA_def_enum_funcs(prop, bone_collection_enum_items);
|
||||
RNA_def_property_flag(prop, PROP_SKIP_SAVE | PROP_HIDDEN);
|
||||
|
||||
prop = RNA_def_string(ot->srna,
|
||||
"new_collection_name",
|
||||
nullptr,
|
||||
MAX_NAME,
|
||||
"Name",
|
||||
"Name of the newly added bone collection");
|
||||
RNA_def_property_flag(prop, PROP_SKIP_SAVE);
|
||||
ot->prop = prop;
|
||||
}
|
||||
|
||||
void ARMATURE_OT_assign_to_collection(wmOperatorType *ot)
|
||||
{
|
||||
PropertyRNA *prop;
|
||||
|
||||
/* identifiers */
|
||||
ot->name = "Assign to Collection";
|
||||
ot->description = "Assign bones to a collection";
|
||||
ot->idname = "ARMATURE_OT_assign_to_collection";
|
||||
|
||||
/* api callbacks */
|
||||
ot->exec = assign_to_collection_exec;
|
||||
ot->invoke = move_to_collection_invoke;
|
||||
ot->poll = move_to_collection_poll;
|
||||
|
||||
/* Flags don't include OPTYPE_REGISTER, as the redo panel doesn't make much sense for this
|
||||
* operator. The visibility of the RNA properties is determined by the needs of the 'New Catalog'
|
||||
* popup, so that a name can be entered. This means that the redo panel would also only show the
|
||||
* 'Name' property, without any choice for another collection. */
|
||||
ot->flag = OPTYPE_UNDO;
|
||||
|
||||
prop = RNA_def_enum(ot->srna,
|
||||
"collection",
|
||||
rna_enum_dummy_DEFAULT_items,
|
||||
0,
|
||||
"Collection",
|
||||
"The bone collection to move the selected bones to");
|
||||
RNA_def_enum_funcs(prop, bone_collection_enum_items);
|
||||
RNA_def_property_flag(prop, PROP_SKIP_SAVE | PROP_HIDDEN);
|
||||
|
||||
prop = RNA_def_string(ot->srna,
|
||||
"new_collection_name",
|
||||
nullptr,
|
||||
MAX_NAME,
|
||||
"Name",
|
||||
"Name of the newly added bone collection");
|
||||
RNA_def_property_flag(prop, PROP_SKIP_SAVE);
|
||||
ot->prop = prop;
|
||||
}
|
||||
|
||||
/* ********************************************** */
|
||||
@@ -941,31 +941,13 @@ static int armature_bone_layers_invoke(bContext *C, wmOperator *op, const wmEven
|
||||
}
|
||||
|
||||
/* Set the visible layers for the active armature (edit and pose modes) */
|
||||
static int armature_bone_layers_exec(bContext *C, wmOperator *op)
|
||||
static int armature_bone_layers_exec(bContext * /*C*/, wmOperator * /*op*/)
|
||||
{
|
||||
Object *ob = CTX_data_edit_object(C);
|
||||
PointerRNA ptr;
|
||||
/* hardcoded for now - we can only have 32 armature layers, so this should be fine... */
|
||||
bool layers[32];
|
||||
|
||||
/* get the values set in the operator properties */
|
||||
RNA_boolean_get_array(op->ptr, "layers", layers);
|
||||
|
||||
/* set layers of pchans based on the values set in the operator props */
|
||||
CTX_DATA_BEGIN_WITH_ID (C, EditBone *, ebone, selected_editable_bones, bArmature *, arm) {
|
||||
/* get pointer for pchan, and write flags this way */
|
||||
RNA_pointer_create((ID *)arm, &RNA_EditBone, ebone, &ptr);
|
||||
RNA_boolean_set_array(&ptr, "layers", layers);
|
||||
}
|
||||
CTX_DATA_END;
|
||||
|
||||
ED_armature_edit_refresh_layer_used(static_cast<bArmature *>(ob->data));
|
||||
|
||||
/* NOTE: notifier might evolve. */
|
||||
WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob);
|
||||
DEG_id_tag_update((ID *)ob->data, ID_RECALC_PARAMETERS);
|
||||
|
||||
return OPERATOR_FINISHED;
|
||||
// TODO: remove this entire operator, replacing it with a similar one for bone collections.
|
||||
WM_report(
|
||||
RPT_ERROR,
|
||||
"Bone Layers have been converted to Bone Collections. This operator will be removed soon.");
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
void ARMATURE_OT_bone_layers(wmOperatorType *ot)
|
||||
|
||||
@@ -35,6 +35,8 @@
|
||||
#include "UI_interface.hh"
|
||||
#include "UI_resources.hh"
|
||||
|
||||
#include "ANIM_bone_collections.h"
|
||||
|
||||
#include "armature_intern.h"
|
||||
|
||||
/* ********************************************** */
|
||||
|
||||
@@ -43,6 +43,8 @@
|
||||
#include "ED_screen.hh"
|
||||
#include "ED_util.hh"
|
||||
|
||||
#include "ANIM_bone_collections.h"
|
||||
|
||||
#include "armature_intern.h"
|
||||
|
||||
enum ePoseBlendState {
|
||||
|
||||
@@ -46,6 +46,8 @@
|
||||
#include "ED_select_utils.hh"
|
||||
#include "ED_view3d.hh"
|
||||
|
||||
#include "ANIM_bone_collections.h"
|
||||
|
||||
#include "armature_intern.h"
|
||||
|
||||
/* utility macros for storing a temp int in the bone (selection flag) */
|
||||
|
||||
@@ -796,7 +796,14 @@ static int pose_copy_exec(bContext *C, wmOperator *op)
|
||||
|
||||
Object ob_copy = blender::dna::shallow_copy(*ob);
|
||||
ob_copy.adt = nullptr;
|
||||
bArmature arm_copy = *((bArmature *)ob->data);
|
||||
|
||||
/* Copy the armature without using the default copy constructor. This prevents
|
||||
* the compiler from complaining that the `layer`, `layer_used`, and
|
||||
* `layer_protected` fields are DNA_DEPRECATED.
|
||||
*/
|
||||
bArmature arm_copy;
|
||||
memcpy(&arm_copy, ob->data, sizeof(arm_copy));
|
||||
|
||||
arm_copy.adt = nullptr;
|
||||
ob_copy.data = &arm_copy;
|
||||
BLI_addtail(&temp_bmain->objects, &ob_copy);
|
||||
|
||||
@@ -39,11 +39,8 @@ struct wmOperator;
|
||||
#define BONESEL_BONE (1u << 31)
|
||||
#define BONESEL_ANY (BONESEL_TIP | BONESEL_ROOT | BONESEL_BONE)
|
||||
|
||||
/* useful macros */
|
||||
#define EBONE_VISIBLE(arm, ebone) \
|
||||
(CHECK_TYPE_INLINE(arm, bArmature *), \
|
||||
CHECK_TYPE_INLINE(ebone, EditBone *), \
|
||||
(((arm)->layer & (ebone)->layer) && !((ebone)->flag & BONE_HIDDEN_A)))
|
||||
/* useful macros, be sure to #include "ANIM_bone_collections.h". */
|
||||
#define EBONE_VISIBLE(arm, ebone) ANIM_bone_is_visible_editbone(arm, ebone)
|
||||
|
||||
#define EBONE_SELECTABLE(arm, ebone) \
|
||||
(EBONE_VISIBLE(arm, ebone) && !((ebone)->flag & BONE_UNSELECTABLE))
|
||||
@@ -67,6 +64,8 @@ struct wmOperator;
|
||||
EditBone *ED_armature_ebone_add(bArmature *arm, const char *name);
|
||||
EditBone *ED_armature_ebone_add_primitive(Object *obedit_arm, float length, bool view_aligned);
|
||||
|
||||
void ED_armature_ebone_copy(EditBone *dest, const EditBone *source);
|
||||
|
||||
/* `armature_edit.cc` */
|
||||
|
||||
/**
|
||||
@@ -205,12 +204,6 @@ void ED_armature_undosys_type(UndoType *ut);
|
||||
/** Sync selection to parent for connected children. */
|
||||
void ED_armature_edit_sync_selection(ListBase *edbo);
|
||||
void ED_armature_edit_validate_active(bArmature *arm);
|
||||
/**
|
||||
* Update the layers_used variable after bones are moved between layer
|
||||
* \note Used to be done in drawing code in 2.7, but that won't work with
|
||||
* Copy-on-Write, as drawing uses evaluated copies.
|
||||
*/
|
||||
void ED_armature_edit_refresh_layer_used(bArmature *arm);
|
||||
/**
|
||||
* \param clear_connected: When false caller is responsible for keeping the flag in a valid state.
|
||||
*/
|
||||
|
||||
@@ -549,23 +549,6 @@ static void ui_item_array(uiLayout *layout,
|
||||
const int butw = UI_UNIT_X * 0.75;
|
||||
const int buth = UI_UNIT_X * 0.75;
|
||||
|
||||
if (ptr->type == &RNA_Armature) {
|
||||
bArmature *arm = static_cast<bArmature *>(ptr->data);
|
||||
|
||||
layer_used = arm->layer_used;
|
||||
|
||||
if (arm->edbo) {
|
||||
if (arm->act_edbone) {
|
||||
layer_active |= arm->act_edbone->layer;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (arm->act_bone) {
|
||||
layer_active |= arm->act_bone->layer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int b = 0; b < cols; b++) {
|
||||
UI_block_align_begin(block);
|
||||
|
||||
|
||||
@@ -2945,7 +2945,7 @@ static Object *modifier_skin_armature_create(Depsgraph *depsgraph, Main *bmain,
|
||||
Object *arm_ob = BKE_object_add(bmain, scene, view_layer, OB_ARMATURE, nullptr);
|
||||
BKE_object_transform_copy(arm_ob, skin_ob);
|
||||
bArmature *arm = static_cast<bArmature *>(arm_ob->data);
|
||||
ANIM_armature_ensure_first_layer_enabled(arm);
|
||||
ANIM_armature_bonecoll_show_all(arm);
|
||||
arm_ob->dtx |= OB_DRAW_IN_FRONT;
|
||||
arm->drawtype = ARM_LINE;
|
||||
arm->edbo = MEM_cnew<ListBase>("edbo armature");
|
||||
|
||||
@@ -313,7 +313,7 @@ bool ED_object_jump_to_bone(bContext *C,
|
||||
if (reveal_hidden) {
|
||||
/* Unhide the bone. */
|
||||
ebone->flag &= ~BONE_HIDDEN_A;
|
||||
ANIM_armature_ensure_layer_enabled_from_ebone(arm, ebone);
|
||||
ANIM_armature_bonecoll_show_from_ebone(arm, ebone);
|
||||
}
|
||||
|
||||
/* Select it. */
|
||||
@@ -337,7 +337,7 @@ bool ED_object_jump_to_bone(bContext *C,
|
||||
if (reveal_hidden) {
|
||||
/* Unhide the bone. */
|
||||
pchan->bone->flag &= ~BONE_HIDDEN_P;
|
||||
ANIM_armature_ensure_layer_enabled_from_pchan(arm, pchan);
|
||||
ANIM_armature_bonecoll_show_from_pchan(arm, pchan);
|
||||
}
|
||||
|
||||
/* Select it. */
|
||||
|
||||
@@ -44,6 +44,7 @@ set(SRC
|
||||
)
|
||||
|
||||
set(LIB
|
||||
PRIVATE bf::animrig
|
||||
PRIVATE bf::blenlib
|
||||
PRIVATE bf::dna
|
||||
bf_editor_datafiles
|
||||
|
||||
@@ -52,6 +52,8 @@
|
||||
#include "UI_interface.hh"
|
||||
#include "WM_api.hh"
|
||||
|
||||
#include "ANIM_bone_collections.h"
|
||||
|
||||
#include "screen_intern.h"
|
||||
|
||||
const char *screen_context_dir[] = {
|
||||
|
||||
@@ -131,6 +131,7 @@ set(SRC
|
||||
)
|
||||
|
||||
set(LIB
|
||||
PRIVATE bf::animrig
|
||||
bf_blenkernel
|
||||
PRIVATE bf::blenlib
|
||||
PRIVATE bf::dna
|
||||
|
||||
@@ -67,6 +67,8 @@
|
||||
#include "RNA_define.hh"
|
||||
#include "RNA_prototypes.h"
|
||||
|
||||
#include "ANIM_bone_collections.h"
|
||||
|
||||
#include "outliner_intern.hh"
|
||||
#include "tree/tree_display.hh"
|
||||
#include "tree/tree_element_grease_pencil_node.hh"
|
||||
|
||||
@@ -35,6 +35,8 @@
|
||||
#include "WM_api.hh"
|
||||
#include "WM_types.hh"
|
||||
|
||||
#include "ANIM_bone_collections.h"
|
||||
|
||||
#include "tree/tree_element_seq.hh"
|
||||
|
||||
#include "outliner_intern.hh"
|
||||
|
||||
@@ -35,6 +35,8 @@
|
||||
#include "DEG_depsgraph.h"
|
||||
#include "DEG_depsgraph_query.h"
|
||||
|
||||
#include "ANIM_bone_collections.h"
|
||||
|
||||
#include "bmesh.h"
|
||||
|
||||
#include "ED_armature.hh"
|
||||
|
||||
@@ -98,6 +98,8 @@
|
||||
#include "DRW_engine.h"
|
||||
#include "DRW_select_buffer.h"
|
||||
|
||||
#include "ANIM_bone_collections.h"
|
||||
|
||||
#include "view3d_intern.h" /* own include */
|
||||
|
||||
// #include "PIL_time_utildefines.h"
|
||||
|
||||
@@ -37,6 +37,8 @@
|
||||
#include "RNA_access.hh"
|
||||
#include "RNA_prototypes.h"
|
||||
|
||||
#include "ANIM_bone_collections.h"
|
||||
|
||||
#include "transform.hh"
|
||||
#include "transform_orientations.hh"
|
||||
#include "transform_snap.hh"
|
||||
|
||||
@@ -50,6 +50,8 @@
|
||||
#include "RNA_access.hh"
|
||||
#include "RNA_define.hh"
|
||||
|
||||
#include "ANIM_bone_collections.h"
|
||||
|
||||
/* local module include */
|
||||
#include "transform.hh"
|
||||
#include "transform_convert.hh"
|
||||
|
||||
@@ -491,7 +491,7 @@ void ArmatureImporter::create_armature_bones(Main *bmain, std::vector<Object *>
|
||||
|
||||
ED_armature_to_edit(armature);
|
||||
/* Layers are enabled according to imported bone set in create_bone(). */
|
||||
ANIM_armature_disable_all_layers(armature);
|
||||
ANIM_armature_bonecoll_hide_all(armature);
|
||||
|
||||
create_bone(
|
||||
nullptr, node, nullptr, node->getChildNodes().getCount(), nullptr, armature, layer_labels);
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
namespace blender::animrig {
|
||||
class BoneColor;
|
||||
@@ -24,6 +26,7 @@ extern "C" {
|
||||
#endif
|
||||
|
||||
struct AnimData;
|
||||
struct BoneCollection;
|
||||
|
||||
/* this system works on different transformation space levels;
|
||||
*
|
||||
@@ -49,6 +52,11 @@ typedef struct BoneColor {
|
||||
#endif
|
||||
} BoneColor;
|
||||
|
||||
typedef struct Bone_Runtime {
|
||||
/* #BoneCollectionReference */
|
||||
ListBase collections;
|
||||
} Bone_Runtime;
|
||||
|
||||
typedef struct Bone {
|
||||
/** Next/previous elements within this list. */
|
||||
struct Bone *next, *prev;
|
||||
@@ -127,8 +135,22 @@ typedef struct Bone {
|
||||
/** Next/prev bones to use as handle references when calculating bbones (optional). */
|
||||
struct Bone *bbone_prev;
|
||||
struct Bone *bbone_next;
|
||||
|
||||
/* Keep last. */
|
||||
Bone_Runtime runtime;
|
||||
} Bone;
|
||||
|
||||
typedef struct bArmature_Runtime {
|
||||
/**
|
||||
* Index of the active collection, -1 if there is no collection active.
|
||||
*
|
||||
* For UIList support in the user interface. Assigning here does nothing, use
|
||||
* `ANIM_armature_bonecoll_active_set` to set the active bone collection.
|
||||
*/
|
||||
int active_collection_index;
|
||||
uint8_t _pad0[4];
|
||||
} bArmature_Runtime;
|
||||
|
||||
typedef struct bArmature {
|
||||
ID id;
|
||||
struct AnimData *adt;
|
||||
@@ -163,15 +185,67 @@ typedef struct bArmature {
|
||||
short deformflag;
|
||||
short pathflag;
|
||||
|
||||
/* BoneCollection. */
|
||||
ListBase collections;
|
||||
/* Do not directly assign, use `ANIM_armature_bonecoll_active_set` instead. */
|
||||
struct BoneCollection *active_collection;
|
||||
|
||||
/** For UI, to show which layers are there. */
|
||||
unsigned int layer_used;
|
||||
unsigned int layer_used DNA_DEPRECATED;
|
||||
/** For buttons to work, both variables in this order together. */
|
||||
unsigned int layer, layer_protected;
|
||||
unsigned int layer DNA_DEPRECATED, layer_protected DNA_DEPRECATED;
|
||||
|
||||
/** Relative position of the axes on the bone, from head (0.0f) to tail (1.0f). */
|
||||
float axes_position;
|
||||
|
||||
/** Keep last, for consistency with the position of other DNA runtime structures. */
|
||||
struct bArmature_Runtime runtime;
|
||||
} bArmature;
|
||||
|
||||
/**
|
||||
* Collection of Bones within an Armature.
|
||||
*
|
||||
* BoneCollections are owned by their Armature, and cannot be shared between
|
||||
* different armatures.
|
||||
*
|
||||
* Bones can be in more than one collection at a time.
|
||||
*
|
||||
* Selectability and visibility of bones are determined by OR-ing the collection
|
||||
* flags.
|
||||
*/
|
||||
typedef struct BoneCollection {
|
||||
struct BoneCollection *next, *prev;
|
||||
|
||||
/** MAX_NAME. */
|
||||
char name[64];
|
||||
|
||||
/** BoneCollectionMember. */
|
||||
ListBase bones;
|
||||
|
||||
/** eBoneCollection_Flag. */
|
||||
uint8_t flags;
|
||||
uint8_t _pad0[7];
|
||||
|
||||
// TODO: add IDProperties.
|
||||
} BoneCollection;
|
||||
|
||||
/** Membership relation of a bone with a bone collection. */
|
||||
typedef struct BoneCollectionMember {
|
||||
struct BoneCollectionMember *next, *prev;
|
||||
struct Bone *bone;
|
||||
} BoneCollectionMember;
|
||||
|
||||
/**
|
||||
* Membership relation of a bone with its collections.
|
||||
*
|
||||
* This is only bone-runtime data for easy lookups, the actual membership is
|
||||
* stored on the #bArmature in #BoneCollectionMember structs.
|
||||
*/
|
||||
typedef struct BoneCollectionReference {
|
||||
struct BoneCollectionReference *next, *prev;
|
||||
struct BoneCollection *bcoll;
|
||||
} BoneCollectionReference;
|
||||
|
||||
/* armature->flag */
|
||||
/* don't use bit 7, was saved in files to disable stuff */
|
||||
typedef enum eArmature_Flag {
|
||||
@@ -342,6 +416,13 @@ typedef enum eBone_BBoneHandleFlag {
|
||||
|
||||
#define MAXBONENAME 64
|
||||
|
||||
/** #BoneCollection.flag */
|
||||
typedef enum eBoneCollection_Flag {
|
||||
BONE_COLLECTION_VISIBLE = (1 << 0),
|
||||
BONE_COLLECTION_SELECTABLE = (1 << 1), /* Intended to be implemented in the not-so-far future. */
|
||||
} eBoneCollection_Flag;
|
||||
ENUM_OPERATORS(eBoneCollection_Flag, BONE_COLLECTION_SELECTABLE)
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
inline blender::animrig::BoneColor &BoneColor::wrap()
|
||||
|
||||
@@ -22,6 +22,8 @@
|
||||
#include "DNA_object_types.h"
|
||||
#include "DNA_scene_types.h"
|
||||
|
||||
#include "ED_anim_api.hh"
|
||||
|
||||
#include "WM_api.hh"
|
||||
#include "WM_types.hh"
|
||||
|
||||
@@ -68,6 +70,8 @@ constexpr int COLOR_SETS_MAX_THEMED_INDEX = 20;
|
||||
# include "BKE_armature.h"
|
||||
# include "ED_armature.hh"
|
||||
|
||||
# include "ANIM_bone_collections.h"
|
||||
|
||||
# include "DEG_depsgraph.h"
|
||||
# include "DEG_depsgraph_build.h"
|
||||
|
||||
@@ -181,12 +185,63 @@ static void rna_Armature_edit_bone_remove(bArmature *arm,
|
||||
RNA_POINTER_INVALIDATE(ebone_ptr);
|
||||
}
|
||||
|
||||
static void rna_Armature_update_layers(Main * /*bmain*/, Scene * /*scene*/, PointerRNA *ptr)
|
||||
static int rna_BoneCollections_active_index_get(PointerRNA *ptr)
|
||||
{
|
||||
bArmature *arm = (bArmature *)ptr->data;
|
||||
return arm->runtime.active_collection_index;
|
||||
}
|
||||
|
||||
static void rna_BoneCollections_active_index_set(PointerRNA *ptr, const int bone_collection_index)
|
||||
{
|
||||
bArmature *arm = (bArmature *)ptr->data;
|
||||
ANIM_armature_bonecoll_active_index_set(arm, bone_collection_index);
|
||||
|
||||
// TODO: send notifiers?
|
||||
}
|
||||
|
||||
static void rna_BoneCollections_active_index_range(
|
||||
PointerRNA *ptr, int *min, int *max, int * /*softmin*/, int * /*softmax*/)
|
||||
{
|
||||
bArmature *arm = (bArmature *)ptr->data;
|
||||
|
||||
// TODO: Figure out what this function actually is used for, as we may want to protect the first
|
||||
// collection (i.e. the default collection that should remain first).
|
||||
*min = 0;
|
||||
*max = max_ii(0, BLI_listbase_count(&arm->collections) - 1);
|
||||
}
|
||||
|
||||
static void rna_BoneCollection_name_set(PointerRNA *ptr, const char *name)
|
||||
{
|
||||
bArmature *arm = (bArmature *)ptr->owner_id;
|
||||
BoneCollection *bcoll = (BoneCollection *)ptr->data;
|
||||
|
||||
DEG_id_tag_update(&arm->id, ID_RECALC_COPY_ON_WRITE);
|
||||
WM_main_add_notifier(NC_GEOM | ND_DATA, arm);
|
||||
ANIM_armature_bonecoll_name_set(arm, bcoll, name);
|
||||
// TODO: notifiers.
|
||||
}
|
||||
|
||||
/* Bone.collections iterator functions. */
|
||||
|
||||
static void rna_Bone_collections_begin(CollectionPropertyIterator *iter, PointerRNA *ptr)
|
||||
{
|
||||
Bone *bone = (Bone *)ptr->data;
|
||||
ListBase /*BoneCollectionReference*/ bone_collection_refs = bone->runtime.collections;
|
||||
rna_iterator_listbase_begin(iter, &bone_collection_refs, nullptr);
|
||||
}
|
||||
|
||||
static PointerRNA rna_Bone_collections_get(CollectionPropertyIterator *iter)
|
||||
{
|
||||
ListBaseIterator *lb_iter = &iter->internal.listbase;
|
||||
BoneCollectionReference *bcoll_ref = (BoneCollectionReference *)lb_iter->link;
|
||||
return rna_pointer_inherit_refine(&iter->parent, &RNA_BoneCollection, bcoll_ref->bcoll);
|
||||
}
|
||||
|
||||
/* EditBone.collections iterator functions. */
|
||||
|
||||
static void rna_EditBone_collections_begin(CollectionPropertyIterator *iter, PointerRNA *ptr)
|
||||
{
|
||||
EditBone *ebone = (EditBone *)ptr->data;
|
||||
ListBase /*BoneCollectionReference*/ bone_collection_refs = ebone->bone_collections;
|
||||
rna_iterator_listbase_begin(iter, &bone_collection_refs, nullptr);
|
||||
}
|
||||
|
||||
static char *rna_BoneColor_path_posebone(const PointerRNA *ptr)
|
||||
@@ -431,40 +486,6 @@ static IDProperty **rna_EditBone_idprops(PointerRNA *ptr)
|
||||
return &ebone->prop;
|
||||
}
|
||||
|
||||
static void rna_bone_layer_set(int *layer, const bool *values)
|
||||
{
|
||||
int i, tot = 0;
|
||||
|
||||
/* ensure we always have some layer selected */
|
||||
for (i = 0; i < 32; i++) {
|
||||
if (values[i]) {
|
||||
tot++;
|
||||
}
|
||||
}
|
||||
|
||||
if (tot == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0; i < 32; i++) {
|
||||
if (values[i]) {
|
||||
*layer |= (1u << i);
|
||||
}
|
||||
else {
|
||||
*layer &= ~(1u << i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void rna_Bone_layer_set(PointerRNA *ptr, const bool *values)
|
||||
{
|
||||
bArmature *arm = (bArmature *)ptr->owner_id;
|
||||
Bone *bone = (Bone *)ptr->data;
|
||||
|
||||
rna_bone_layer_set(&bone->layer, values);
|
||||
BKE_armature_refresh_layer_used(nullptr, arm);
|
||||
}
|
||||
|
||||
/* TODO: remove the deprecation stubs. */
|
||||
static bool rna_use_inherit_scale_get(char inherit_scale_mode)
|
||||
{
|
||||
@@ -499,32 +520,6 @@ static void rna_Bone_use_inherit_scale_set(PointerRNA *ptr, bool value)
|
||||
rna_use_inherit_scale_set(&((Bone *)ptr->data)->inherit_scale_mode, value);
|
||||
}
|
||||
|
||||
static void rna_Armature_layer_set(PointerRNA *ptr, const bool *values)
|
||||
{
|
||||
bArmature *arm = (bArmature *)ptr->data;
|
||||
int i, tot = 0;
|
||||
|
||||
/* ensure we always have some layer selected */
|
||||
for (i = 0; i < 32; i++) {
|
||||
if (values[i]) {
|
||||
tot++;
|
||||
}
|
||||
}
|
||||
|
||||
if (tot == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0; i < 32; i++) {
|
||||
if (values[i]) {
|
||||
arm->layer |= (1u << i);
|
||||
}
|
||||
else {
|
||||
arm->layer &= ~(1u << i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void rna_EditBone_name_set(PointerRNA *ptr, const char *value)
|
||||
{
|
||||
bArmature *arm = (bArmature *)ptr->owner_id;
|
||||
@@ -553,12 +548,6 @@ static void rna_Bone_name_set(PointerRNA *ptr, const char *value)
|
||||
ED_armature_bone_rename(G_MAIN, arm, oldname, newname);
|
||||
}
|
||||
|
||||
static void rna_EditBone_layer_set(PointerRNA *ptr, const bool values[])
|
||||
{
|
||||
EditBone *data = (EditBone *)(ptr->data);
|
||||
rna_bone_layer_set(&data->layer, values);
|
||||
}
|
||||
|
||||
static void rna_EditBone_connected_check(EditBone *ebone)
|
||||
{
|
||||
if (ebone->parent) {
|
||||
@@ -1094,18 +1083,6 @@ static void rna_def_bone_common(StructRNA *srna, int editbone)
|
||||
}
|
||||
|
||||
/* flags */
|
||||
prop = RNA_def_property(srna, "layers", PROP_BOOLEAN, PROP_LAYER_MEMBER);
|
||||
RNA_def_property_boolean_sdna(prop, nullptr, "layer", 1);
|
||||
RNA_def_property_array(prop, 32);
|
||||
if (editbone) {
|
||||
RNA_def_property_boolean_funcs(prop, nullptr, "rna_EditBone_layer_set");
|
||||
}
|
||||
else {
|
||||
RNA_def_property_boolean_funcs(prop, nullptr, "rna_Bone_layer_set");
|
||||
}
|
||||
RNA_def_property_ui_text(prop, "Layers", "Layers bone exists in");
|
||||
RNA_def_property_update(prop, 0, "rna_Armature_redraw_data");
|
||||
|
||||
prop = RNA_def_property(srna, "use_connect", PROP_BOOLEAN, PROP_NONE);
|
||||
RNA_def_property_boolean_sdna(prop, nullptr, "flag", BONE_CONNECTED);
|
||||
if (editbone) {
|
||||
@@ -1398,6 +1375,21 @@ static void rna_def_bone(BlenderRNA *brna)
|
||||
RNA_def_property_flag(prop, PROP_PTR_NO_OWNERSHIP);
|
||||
RNA_def_property_ui_text(prop, "Children", "Bones which are children of this bone");
|
||||
|
||||
prop = RNA_def_property(srna, "collections", PROP_COLLECTION, PROP_NONE);
|
||||
RNA_def_property_struct_type(prop, "BoneCollection");
|
||||
RNA_def_property_collection_funcs(prop,
|
||||
"rna_Bone_collections_begin",
|
||||
"rna_iterator_listbase_next",
|
||||
"rna_iterator_listbase_end",
|
||||
"rna_Bone_collections_get",
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr);
|
||||
RNA_def_property_flag(prop, PROP_PTR_NO_OWNERSHIP);
|
||||
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
|
||||
RNA_def_property_ui_text(prop, "Collections", "Bone Collections that contain this bone");
|
||||
|
||||
rna_def_bone_common(srna, 0);
|
||||
rna_def_bone_curved_common(srna, false, false);
|
||||
|
||||
@@ -1504,6 +1496,21 @@ static void rna_def_edit_bone(BlenderRNA *brna)
|
||||
RNA_def_struct_ui_text(srna, "Edit Bone", "Edit mode bone in an armature data-block");
|
||||
RNA_def_struct_ui_icon(srna, ICON_BONE_DATA);
|
||||
|
||||
prop = RNA_def_property(srna, "collections", PROP_COLLECTION, PROP_NONE);
|
||||
RNA_def_property_struct_type(prop, "BoneCollection");
|
||||
RNA_def_property_collection_funcs(prop,
|
||||
"rna_EditBone_collections_begin",
|
||||
"rna_iterator_listbase_next",
|
||||
"rna_iterator_listbase_end",
|
||||
"rna_Bone_collections_get",
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr);
|
||||
RNA_def_property_flag(prop, PROP_PTR_NO_OWNERSHIP);
|
||||
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
|
||||
RNA_def_property_ui_text(prop, "Collections", "Bone Collections that contain this bone");
|
||||
|
||||
RNA_define_verify_sdna(false); /* not in sdna */
|
||||
|
||||
prop = RNA_def_property(srna, "parent", PROP_POINTER, PROP_NONE);
|
||||
@@ -1670,6 +1677,66 @@ static void rna_def_armature_edit_bones(BlenderRNA *brna, PropertyRNA *cprop)
|
||||
RNA_def_parameter_clear_flags(parm, PROP_THICK_WRAP, ParameterFlag(0));
|
||||
}
|
||||
|
||||
/** Armature.collections collection-of-bonecollections interface. */
|
||||
static void rna_def_armature_collections(BlenderRNA *brna, PropertyRNA *cprop)
|
||||
{
|
||||
StructRNA *srna;
|
||||
PropertyRNA *prop;
|
||||
|
||||
FunctionRNA *func;
|
||||
PropertyRNA *parm;
|
||||
|
||||
RNA_def_property_srna(cprop, "BoneCollections");
|
||||
srna = RNA_def_struct(brna, "BoneCollections", NULL);
|
||||
RNA_def_struct_sdna(srna, "bArmature");
|
||||
RNA_def_struct_ui_text(
|
||||
srna, "Armature Bone Collections", "The Bone Collections of this Armature");
|
||||
|
||||
prop = RNA_def_property(srna, "active", PROP_POINTER, PROP_NONE);
|
||||
RNA_def_property_struct_type(prop, "BoneCollection");
|
||||
RNA_def_property_pointer_sdna(prop, NULL, "active_collection");
|
||||
RNA_def_property_flag(prop, PROP_EDITABLE);
|
||||
RNA_def_property_ui_text(prop, "Active Collection", "Armature's active bone collection");
|
||||
|
||||
prop = RNA_def_property(srna, "active_index", PROP_INT, PROP_NONE);
|
||||
RNA_def_property_int_sdna(prop, NULL, "runtime.active_collection_index");
|
||||
RNA_def_property_flag(prop, PROP_EDITABLE);
|
||||
RNA_def_property_ui_text(prop,
|
||||
"Active Collection Index",
|
||||
"The index of the Armature's active bone collection; -1 when there "
|
||||
"is no active collection");
|
||||
RNA_def_property_int_funcs(prop,
|
||||
"rna_BoneCollections_active_index_get",
|
||||
"rna_BoneCollections_active_index_set",
|
||||
"rna_BoneCollections_active_index_range");
|
||||
|
||||
/* Armature.collections.new(...) */
|
||||
func = RNA_def_function(srna, "new", "ANIM_armature_bonecoll_new");
|
||||
RNA_def_function_ui_description(func, "Add a new empty bone collection to the armature");
|
||||
parm = RNA_def_string(func,
|
||||
"name",
|
||||
NULL,
|
||||
0,
|
||||
"Name",
|
||||
"Name of the new collection. Blender will ensure it is unique within the "
|
||||
"collections of the Armature");
|
||||
RNA_def_parameter_flags(parm, PropertyFlag(0), PARM_REQUIRED);
|
||||
/* Return value. */
|
||||
parm = RNA_def_pointer(
|
||||
func, "bonecollection", "BoneCollection", "", "Newly created bone collection");
|
||||
RNA_def_function_return(func, parm);
|
||||
|
||||
/* Armature.collections.remove(...) */
|
||||
func = RNA_def_function(srna, "remove", "ANIM_armature_bonecoll_remove");
|
||||
RNA_def_function_ui_description(func, "Remove the bone collection from the armature");
|
||||
parm = RNA_def_pointer(func,
|
||||
"bone_collection",
|
||||
"BoneCollection",
|
||||
"Bone Collection",
|
||||
"The bone collection to remove");
|
||||
RNA_def_parameter_flags(parm, PropertyFlag(0), PARM_REQUIRED);
|
||||
}
|
||||
|
||||
static void rna_def_armature(BlenderRNA *brna)
|
||||
{
|
||||
StructRNA *srna;
|
||||
@@ -1753,6 +1820,12 @@ static void rna_def_armature(BlenderRNA *brna)
|
||||
RNA_def_property_ui_text(prop, "Edit Bones", "");
|
||||
rna_def_armature_edit_bones(brna, prop);
|
||||
|
||||
prop = RNA_def_property(srna, "collections", PROP_COLLECTION, PROP_NONE);
|
||||
RNA_def_property_collection_sdna(prop, NULL, "collections", NULL);
|
||||
RNA_def_property_struct_type(prop, "BoneCollection");
|
||||
RNA_def_property_ui_text(prop, "Bone Collections", "");
|
||||
rna_def_armature_collections(brna, prop);
|
||||
|
||||
/* Enum values */
|
||||
prop = RNA_def_property(srna, "pose_position", PROP_ENUM, PROP_NONE);
|
||||
RNA_def_property_enum_bitflag_sdna(prop, nullptr, "flag");
|
||||
@@ -1769,26 +1842,6 @@ static void rna_def_armature(BlenderRNA *brna)
|
||||
RNA_def_property_update(prop, 0, "rna_Armature_redraw_data");
|
||||
RNA_def_property_flag(prop, PROP_LIB_EXCEPTION);
|
||||
|
||||
/* Boolean values */
|
||||
/* layer */
|
||||
prop = RNA_def_property(srna, "layers", PROP_BOOLEAN, PROP_LAYER_MEMBER);
|
||||
RNA_def_property_boolean_sdna(prop, nullptr, "layer", 1);
|
||||
RNA_def_property_array(prop, 32);
|
||||
RNA_def_property_ui_text(prop, "Visible Layers", "Armature layer visibility");
|
||||
RNA_def_property_boolean_funcs(prop, nullptr, "rna_Armature_layer_set");
|
||||
RNA_def_property_update(prop, NC_OBJECT | ND_POSE, "rna_Armature_update_layers");
|
||||
RNA_def_property_flag(prop, PROP_LIB_EXCEPTION);
|
||||
|
||||
/* layer protection */
|
||||
prop = RNA_def_property(srna, "layers_protected", PROP_BOOLEAN, PROP_LAYER);
|
||||
RNA_def_property_boolean_sdna(prop, nullptr, "layer_protected", 1);
|
||||
RNA_def_property_array(prop, 32);
|
||||
RNA_def_property_ui_text(prop,
|
||||
"Layer Override Protection",
|
||||
"Protected layers in overridden instances are restored to "
|
||||
"their original settings on file reload and undo");
|
||||
RNA_def_property_update(prop, 0, "rna_Armature_redraw_data");
|
||||
|
||||
/* flag */
|
||||
prop = RNA_def_property(srna, "show_axes", PROP_BOOLEAN, PROP_NONE);
|
||||
RNA_def_property_boolean_sdna(prop, nullptr, "flag", ARM_DRAWAXES);
|
||||
@@ -1852,9 +1905,37 @@ static void rna_def_armature(BlenderRNA *brna)
|
||||
RNA_define_lib_overridable(false);
|
||||
}
|
||||
|
||||
static void rna_def_bonecollection(BlenderRNA *brna)
|
||||
{
|
||||
StructRNA *srna;
|
||||
PropertyRNA *prop;
|
||||
|
||||
srna = RNA_def_struct(brna, "BoneCollection", NULL);
|
||||
RNA_def_struct_ui_text(srna, "BoneCollection", "Bone collection in an Armature data-block");
|
||||
// RNA_def_struct_path_func(srna, "rna_BoneCollection_path");
|
||||
// RNA_def_struct_idprops_func(srna, "rna_BoneCollection_idprops");
|
||||
|
||||
prop = RNA_def_property(srna, "name", PROP_STRING, PROP_NONE);
|
||||
RNA_def_property_string_sdna(prop, NULL, "name");
|
||||
RNA_def_property_ui_text(prop, "Name", "Unique within the Armature");
|
||||
RNA_def_struct_name_property(srna, prop);
|
||||
RNA_def_property_string_funcs(prop, NULL, NULL, "rna_BoneCollection_name_set");
|
||||
// RNA_def_property_update(prop, 0, "rna_Bone_update_renamed");
|
||||
|
||||
prop = RNA_def_property(srna, "is_visible", PROP_BOOLEAN, PROP_NONE);
|
||||
RNA_def_property_boolean_sdna(prop, nullptr, "flags", BONE_COLLECTION_VISIBLE);
|
||||
RNA_def_property_ui_text(
|
||||
prop, "Visible", "Bones in this collection will be visible in pose/object mode");
|
||||
RNA_def_property_update(prop, NC_OBJECT | ND_POSE, nullptr);
|
||||
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
|
||||
|
||||
RNA_api_bonecollection(srna);
|
||||
}
|
||||
|
||||
void RNA_def_armature(BlenderRNA *brna)
|
||||
{
|
||||
rna_def_bonecolor(brna);
|
||||
rna_def_bonecollection(brna);
|
||||
rna_def_armature(brna);
|
||||
rna_def_bone(brna);
|
||||
rna_def_edit_bone(brna);
|
||||
|
||||
@@ -96,6 +96,81 @@ static void rna_Bone_AxisRollFromMatrix(const float matrix[9],
|
||||
mat3_to_vec_roll(mat, r_axis, r_roll);
|
||||
}
|
||||
}
|
||||
|
||||
using bonecoll_assign_func_bone = bool (*)(BoneCollection *, Bone *);
|
||||
using bonecoll_assign_func_ebone = bool (*)(BoneCollection *, EditBone *);
|
||||
|
||||
static bool rna_BoneCollection_assign_abstract(BoneCollection *bcoll,
|
||||
bContext *C,
|
||||
ReportList *reports,
|
||||
PointerRNA *bone_ptr,
|
||||
bonecoll_assign_func_bone assign_bone,
|
||||
bonecoll_assign_func_ebone assign_ebone)
|
||||
|
||||
{
|
||||
if (RNA_pointer_is_null(bone_ptr)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (RNA_struct_is_a(bone_ptr->type, &RNA_PoseBone)) {
|
||||
bPoseChannel *pchan = static_cast<bPoseChannel *>(bone_ptr->data);
|
||||
const bool made_any_change = assign_bone(bcoll, pchan->bone);
|
||||
if (made_any_change) {
|
||||
WM_event_add_notifier(C, NC_OBJECT | ND_POSE, nullptr);
|
||||
}
|
||||
return made_any_change;
|
||||
}
|
||||
|
||||
if (RNA_struct_is_a(bone_ptr->type, &RNA_Bone)) {
|
||||
Bone *bone = static_cast<Bone *>(bone_ptr->data);
|
||||
const bool made_any_change = assign_bone(bcoll, bone);
|
||||
if (made_any_change) {
|
||||
WM_event_add_notifier(C, NC_OBJECT | ND_POSE, nullptr);
|
||||
}
|
||||
return made_any_change;
|
||||
}
|
||||
|
||||
if (RNA_struct_is_a(bone_ptr->type, &RNA_EditBone)) {
|
||||
EditBone *ebone = static_cast<EditBone *>(bone_ptr->data);
|
||||
const bool made_any_change = assign_ebone(bcoll, ebone);
|
||||
if (made_any_change) {
|
||||
WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, nullptr);
|
||||
}
|
||||
return made_any_change;
|
||||
}
|
||||
BKE_reportf(reports,
|
||||
RPT_ERROR,
|
||||
"%s is not supported, pass a Bone, PoseBone, or EditBone",
|
||||
RNA_struct_identifier(bone_ptr->type));
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool rna_BoneCollection_assign(BoneCollection *bcoll,
|
||||
bContext *C,
|
||||
ReportList *reports,
|
||||
PointerRNA *bone_ptr)
|
||||
{
|
||||
return rna_BoneCollection_assign_abstract(bcoll,
|
||||
C,
|
||||
reports,
|
||||
bone_ptr,
|
||||
ANIM_armature_bonecoll_assign,
|
||||
ANIM_armature_bonecoll_assign_editbone);
|
||||
}
|
||||
|
||||
static bool rna_BoneCollection_unassign(BoneCollection *bcoll,
|
||||
bContext *C,
|
||||
ReportList *reports,
|
||||
PointerRNA *bone_ptr)
|
||||
{
|
||||
return rna_BoneCollection_assign_abstract(bcoll,
|
||||
C,
|
||||
reports,
|
||||
bone_ptr,
|
||||
ANIM_armature_bonecoll_unassign,
|
||||
ANIM_armature_bonecoll_unassign_editbone);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
void RNA_api_armature_edit_bone(StructRNA *srna)
|
||||
@@ -203,4 +278,48 @@ void RNA_api_bone(StructRNA *srna)
|
||||
RNA_def_function_output(func, parm);
|
||||
}
|
||||
|
||||
void RNA_api_bonecollection(StructRNA *srna)
|
||||
{
|
||||
PropertyRNA *parm;
|
||||
FunctionRNA *func;
|
||||
|
||||
func = RNA_def_function(srna, "assign", "rna_BoneCollection_assign");
|
||||
RNA_def_function_flag(func, FUNC_USE_CONTEXT | FUNC_USE_REPORTS);
|
||||
RNA_def_function_ui_description(func, "Assign the given bone to this collection");
|
||||
parm = RNA_def_pointer(
|
||||
func,
|
||||
"bone",
|
||||
"AnyType",
|
||||
"",
|
||||
"Bone to assign to this collection. This must be a Bone, PoseBone, or EditBone");
|
||||
RNA_def_parameter_flags(parm, PropertyFlag(0), PARM_REQUIRED | PARM_RNAPTR);
|
||||
/* return value */
|
||||
parm = RNA_def_boolean(func,
|
||||
"assigned",
|
||||
false,
|
||||
"Assigned",
|
||||
"Whether the bone was actually assigned; will be false if the bone was "
|
||||
"already member of the collection");
|
||||
RNA_def_function_return(func, parm);
|
||||
|
||||
func = RNA_def_function(srna, "unassign", "rna_BoneCollection_unassign");
|
||||
RNA_def_function_flag(func, FUNC_USE_CONTEXT | FUNC_USE_REPORTS);
|
||||
RNA_def_function_ui_description(func, "Remove the given bone from this collection");
|
||||
parm = RNA_def_pointer(
|
||||
func,
|
||||
"bone",
|
||||
"AnyType",
|
||||
"",
|
||||
"Bone to remove from this collection. This must be a Bone, PoseBone, or EditBone");
|
||||
RNA_def_parameter_flags(parm, PropertyFlag(0), PARM_REQUIRED | PARM_RNAPTR);
|
||||
/* return value */
|
||||
parm = RNA_def_boolean(func,
|
||||
"assigned",
|
||||
false,
|
||||
"Unassigned",
|
||||
"Whether the bone was actually removed; will be false if the bone was "
|
||||
"not a member of the collection to begin with");
|
||||
RNA_def_function_return(func, parm);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -425,6 +425,7 @@ void RNA_api_action(StructRNA *srna);
|
||||
void RNA_api_animdata(struct StructRNA *srna);
|
||||
void RNA_api_armature_edit_bone(StructRNA *srna);
|
||||
void RNA_api_bone(StructRNA *srna);
|
||||
void RNA_api_bonecollection(StructRNA *srna);
|
||||
void RNA_api_camera(StructRNA *srna);
|
||||
void RNA_api_curve(StructRNA *srna);
|
||||
void RNA_api_curve_nurb(StructRNA *srna);
|
||||
|
||||
Reference in New Issue
Block a user