Anim: hierarchical visibility for bone collections #116784
|
@ -214,8 +214,33 @@ void ANIM_armature_bonecoll_name_set(struct bArmature *armature,
|
|||
struct BoneCollection *bcoll,
|
||||
const char *name);
|
||||
|
||||
void ANIM_bonecoll_show(struct BoneCollection *bcoll);
|
||||
void ANIM_bonecoll_hide(struct BoneCollection *bcoll);
|
||||
/**
|
||||
* Show this bone collection.
|
||||
*
|
||||
* This marks the bone collection as 'visible'. Whether it is effectively
|
||||
* visible also depends on the visibility state of its ancestors. */
|
||||
void ANIM_bonecoll_show(bArmature *armature, BoneCollection *bcoll);
|
||||
|
||||
/**
|
||||
* Hide this bone collection.
|
||||
*
|
||||
* This marks the bone collection as 'hidden'. This also effectively hides its descendants,
|
||||
dr.sybren marked this conversation as resolved
Outdated
|
||||
* regardless of their visibility state. */
|
||||
void ANIM_bonecoll_hide(bArmature *armature, BoneCollection *bcoll);
|
||||
|
||||
/**
|
||||
* Show or hide this bone collection.
|
||||
*
|
||||
* Calling this with a hard-coded `is_visible` parameter is equivalent to
|
||||
dr.sybren marked this conversation as resolved
Outdated
Nathan Vegdahl
commented
Nit: IMO "Do not use..." gives the impression that the results will be incorrect if you do so. It's fairly obvious that that's not the case here, but it did give me minor cognitive dissonance for a moment. Maybe something like this would be better: "Calling this with a hard-coded Nit: IMO "Do not use..." gives the impression that the results will be incorrect if you do so. It's fairly obvious that that's not the case here, but it did give me minor cognitive dissonance for a moment.
Maybe something like this would be better:
"Calling this with a hard-coded `is_visible` parameter is equivalent to calling the dedicated show/hide functions. Prefer the dedicated functions for clarity."
|
||||
* calling the dedicated show/hide functions. Prefer the dedicated functions for
|
||||
* clarity.
|
||||
*
|
||||
* \see ANIM_bonecoll_show
|
||||
* \see ANIM_bonecoll_hide
|
||||
*/
|
||||
void ANIM_armature_bonecoll_is_visible_set(bArmature *armature,
|
||||
BoneCollection *bcoll,
|
||||
bool is_visible);
|
||||
|
||||
/**
|
||||
* Assign the bone to the bone collection.
|
||||
|
|
|
@ -44,11 +44,16 @@ namespace {
|
|||
|
||||
/** Default flags for new bone collections. */
|
||||
constexpr eBoneCollection_Flag default_flags = BONE_COLLECTION_VISIBLE |
|
||||
BONE_COLLECTION_SELECTABLE;
|
||||
BONE_COLLECTION_SELECTABLE |
|
||||
BONE_COLLECTION_ANCESTORS_VISIBLE;
|
||||
constexpr auto bonecoll_default_name = "Bones";
|
||||
|
||||
} // namespace
|
||||
|
||||
static void ancestors_visible_update(bArmature *armature,
|
||||
const BoneCollection *parent_bcoll,
|
||||
BoneCollection *bcoll);
|
||||
|
||||
BoneCollection *ANIM_bonecoll_new(const char *name)
|
||||
{
|
||||
if (name == nullptr || name[0] == '\0') {
|
||||
|
@ -98,6 +103,11 @@ void ANIM_armature_runtime_refresh(bArmature *armature)
|
|||
ANIM_armature_runtime_free(armature);
|
||||
ANIM_armature_bonecoll_active_runtime_refresh(armature);
|
||||
|
||||
/* Make sure the BONE_COLLECTION_ANCESTORS_VISIBLE flags are set correctly. */
|
||||
for (BoneCollection *bcoll : armature->collections_roots()) {
|
||||
ancestors_visible_update(armature, nullptr, bcoll);
|
||||
}
|
||||
|
||||
/* Construct the bone-to-collections mapping. */
|
||||
for (BoneCollection *bcoll : armature->collections_span()) {
|
||||
add_reverse_pointers(bcoll);
|
||||
|
@ -182,6 +192,8 @@ static void bonecoll_insert_as_root(bArmature *armature, BoneCollection *bcoll,
|
|||
|
||||
bonecoll_insert_at_index(armature, bcoll, at_index);
|
||||
armature->collection_root_count++;
|
||||
|
||||
ancestors_visible_update(armature, nullptr, bcoll);
|
||||
}
|
||||
|
||||
static int bonecoll_insert_as_child(bArmature *armature,
|
||||
|
@ -201,6 +213,8 @@ static int bonecoll_insert_as_child(bArmature *armature,
|
|||
bonecoll_insert_at_index(armature, bcoll, insert_at_index);
|
||||
parent->child_count++;
|
||||
|
||||
ancestors_visible_update(armature, parent, bcoll);
|
||||
|
||||
return insert_at_index;
|
||||
}
|
||||
|
||||
|
@ -707,14 +721,70 @@ int ANIM_armature_bonecoll_get_index_by_name(bArmature *armature, const char *na
|
|||
return -1;
|
||||
}
|
||||
|
||||
void ANIM_bonecoll_show(BoneCollection *bcoll)
|
||||
/* Clear BONE_COLLECTION_ANCESTORS_VISIBLE on all decendents of this bone collection. */
|
||||
static void ancestors_visible_descendants_clear(bArmature *armature, BoneCollection *parent_bcoll)
|
||||
{
|
||||
bcoll->flags |= BONE_COLLECTION_VISIBLE;
|
||||
for (BoneCollection *bcoll : armature->collection_children(parent_bcoll)) {
|
||||
bcoll->flags &= ~BONE_COLLECTION_ANCESTORS_VISIBLE;
|
||||
ancestors_visible_descendants_clear(armature, bcoll);
|
||||
}
|
||||
}
|
||||
|
||||
void ANIM_bonecoll_hide(BoneCollection *bcoll)
|
||||
/* Set or clear BONE_COLLECTION_ANCESTORS_VISIBLE on all decendents of this bone collection. */
|
||||
static void ancestors_visible_descendants_update(bArmature *armature, BoneCollection *parent_bcoll)
|
||||
{
|
||||
if (!parent_bcoll->is_visible_effectively()) {
|
||||
/* If this bone collection is not visible itself, or any of its ancestors are
|
||||
* invisible, all descendants have an invisible ancestor. */
|
||||
ancestors_visible_descendants_clear(armature, parent_bcoll);
|
||||
return;
|
||||
}
|
||||
|
||||
/* parent_bcoll is visible, and so are its ancestors. This means that all direct children have
|
||||
* visible ancestors. The grandchildren depend on the children's visibility as well, hence the
|
||||
* recursion. */
|
||||
for (BoneCollection *bcoll : armature->collection_children(parent_bcoll)) {
|
||||
bcoll->flags |= BONE_COLLECTION_ANCESTORS_VISIBLE;
|
||||
ancestors_visible_descendants_update(armature, bcoll);
|
||||
}
|
||||
}
|
||||
|
||||
/* Set/clear BONE_COLLECTION_ANCESTORS_VISIBLE on this bone collection and all its decendents. */
|
||||
minor, but this could also read "Set or clear" just like the function above minor, but this could also read "Set or clear" just like the function above
Sybren A. Stüvel
commented
I shortened it, as with "Set or clear" it will rewrap the line and put the closing I shortened it, as with "Set or clear" it will rewrap the line and put the closing `*/` on a line by itself. Now it juuuuust fits.
|
||||
static void ancestors_visible_update(bArmature *armature,
|
||||
nathanvegdahl marked this conversation as resolved
Nathan Vegdahl
commented
Just noting a possible alternative, rather than actually suggesting a change per se: I was waffling back-and-forth on how I feel about the redundancy between this function and I believe if we want to we could reduce both to a single function. Basically, we would effectively move the logic of That would make this function slower due to the additional linear scan at the initial call (but not subsequent recursive calls), and also because it would always update I don't feel strongly either way, so I'm happy with the code as-is. But just wanted to mention the alternative. Just noting a possible alternative, rather than actually suggesting a change per se:
I was waffling back-and-forth on how I feel about the redundancy between this function and `ancestors_visible_decendants_update()` above.
I believe if we want to we could reduce both to a single function. Basically, we would effectively move the logic of `ancestors_visible_decendants_update()` into this function. Then at the top of this function, if (and only if) `parent_bcoll` is null we scan to find its parent (leaving it null if `bcoll` is a root).
That would make this function slower due to the additional linear scan at the initial call (but not subsequent recursive calls), and also because it would always update `bcoll` itself even when not strictly needed. But it would also reduce the API surface. So it's a trade-off.
I don't feel strongly either way, so I'm happy with the code as-is. But just wanted to mention the alternative.
Sybren A. Stüvel
commented
I've been pondering the same thing, and to me it was easier to comprehend what was going on with separate "update this bcoll and its descendents" vs. "make sure the descendents are in sync with their parent" functions. Since these are I've been pondering the same thing, and to me it was easier to comprehend what was going on with separate "update this bcoll and its descendents" vs. "make sure the descendents are in sync with their parent" functions.
Since these are `static` functions, and thus not part of any public API, I think I'd like to keep them smaller and more specific.
|
||||
const BoneCollection *parent_bcoll,
|
||||
BoneCollection *bcoll)
|
||||
{
|
||||
if (parent_bcoll == nullptr || parent_bcoll->is_visible_effectively()) {
|
||||
bcoll->flags |= BONE_COLLECTION_ANCESTORS_VISIBLE;
|
||||
}
|
||||
else {
|
||||
bcoll->flags &= ~BONE_COLLECTION_ANCESTORS_VISIBLE;
|
||||
}
|
||||
ancestors_visible_descendants_update(armature, bcoll);
|
||||
}
|
||||
|
||||
void ANIM_bonecoll_show(bArmature *armature, BoneCollection *bcoll)
|
||||
{
|
||||
bcoll->flags |= BONE_COLLECTION_VISIBLE;
|
||||
ancestors_visible_descendants_update(armature, bcoll);
|
||||
}
|
||||
|
||||
void ANIM_bonecoll_hide(bArmature *armature, BoneCollection *bcoll)
|
||||
{
|
||||
bcoll->flags &= ~BONE_COLLECTION_VISIBLE;
|
||||
ancestors_visible_descendants_update(armature, bcoll);
|
||||
}
|
||||
|
||||
void ANIM_armature_bonecoll_is_visible_set(bArmature *armature,
|
||||
BoneCollection *bcoll,
|
||||
const bool is_visible)
|
||||
{
|
||||
if (is_visible) {
|
||||
ANIM_bonecoll_show(armature, bcoll);
|
||||
}
|
||||
else {
|
||||
ANIM_bonecoll_hide(armature, bcoll);
|
||||
}
|
||||
}
|
||||
|
||||
/* Store the bone's membership on the collection. */
|
||||
|
@ -859,7 +929,7 @@ static bool any_bone_collection_visible(const ListBase /*BoneCollectionRef*/ *co
|
|||
|
||||
LISTBASE_FOREACH (const BoneCollectionReference *, bcoll_ref, collection_refs) {
|
||||
const BoneCollection *bcoll = bcoll_ref->bcoll;
|
||||
if (bcoll->flags & BONE_COLLECTION_VISIBLE) {
|
||||
if (bcoll->is_visible_effectively()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -881,14 +951,14 @@ bool ANIM_bonecoll_is_visible_editbone(const bArmature * /*armature*/, const Edi
|
|||
void ANIM_armature_bonecoll_show_all(bArmature *armature)
|
||||
{
|
||||
for (BoneCollection *bcoll : armature->collections_span()) {
|
||||
ANIM_bonecoll_show(bcoll);
|
||||
ANIM_bonecoll_show(armature, bcoll);
|
||||
}
|
||||
}
|
||||
|
||||
void ANIM_armature_bonecoll_hide_all(bArmature *armature)
|
||||
{
|
||||
for (BoneCollection *bcoll : armature->collections_span()) {
|
||||
ANIM_bonecoll_hide(bcoll);
|
||||
ANIM_bonecoll_hide(armature, bcoll);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1145,6 +1215,9 @@ int armature_bonecoll_move_to_parent(bArmature *armature,
|
|||
armature->collection_root_count = armature_root.child_count;
|
||||
BLI_assert(armature_root.child_index == 0);
|
||||
|
||||
/* Since the parent changed, the effective visibility might change too. */
|
||||
ancestors_visible_update(armature, to_parent, armature->collection_array[to_bcoll_index]);
|
||||
|
||||
return to_bcoll_index;
|
||||
}
|
||||
|
||||
|
|
|
@ -22,7 +22,9 @@ TEST(ANIM_bone_collections, bonecoll_new_free)
|
|||
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);
|
||||
EXPECT_EQ(BONE_COLLECTION_VISIBLE | BONE_COLLECTION_SELECTABLE |
|
||||
BONE_COLLECTION_ANCESTORS_VISIBLE,
|
||||
bcoll->flags);
|
||||
ANIM_bonecoll_free(bcoll);
|
||||
}
|
||||
|
||||
|
@ -453,6 +455,71 @@ TEST_F(ANIM_armature_bone_collections, find_parent_index)
|
|||
EXPECT_EQ(2, armature_bonecoll_find_parent_index(&arm, 5));
|
||||
}
|
||||
|
||||
TEST_F(ANIM_armature_bone_collections, collection_hierarchy_visibility)
|
||||
{
|
||||
/* Set up a small hierarchy. */
|
||||
BoneCollection *bcoll_root0 = ANIM_armature_bonecoll_new(&arm, "root0");
|
||||
BoneCollection *bcoll_root1 = ANIM_armature_bonecoll_new(&arm, "root1");
|
||||
const int root0_index = armature_bonecoll_find_index(&arm, bcoll_root0);
|
||||
BoneCollection *bcoll_r0_child0 = ANIM_armature_bonecoll_new(&arm, "r0_child0", root0_index);
|
||||
BoneCollection *bcoll_r0_child1 = ANIM_armature_bonecoll_new(&arm, "r0_child1", root0_index);
|
||||
const int child0_index = armature_bonecoll_find_index(&arm, bcoll_r0_child0);
|
||||
BoneCollection *bcoll_c0_child0 = ANIM_armature_bonecoll_new(&arm, "c0_child0", child0_index);
|
||||
dr.sybren marked this conversation as resolved
Outdated
Nathan Vegdahl
commented
Not sure if this is a naming mistake or a choice-of-parent mistake. With the name Not sure if this is a naming mistake or a choice-of-parent mistake. With the name `c1_child0`, I would expect its parent to be `r0_child1`, but in fact the parent is being set to `r0_child0`.
|
||||
|
||||
/* Initially, all bone collections should be marked as visible. */
|
||||
EXPECT_TRUE(bcoll_root0->flags & BONE_COLLECTION_VISIBLE);
|
||||
EXPECT_TRUE(bcoll_root1->flags & BONE_COLLECTION_VISIBLE);
|
||||
EXPECT_TRUE(bcoll_r0_child0->flags & BONE_COLLECTION_VISIBLE);
|
||||
EXPECT_TRUE(bcoll_r0_child1->flags & BONE_COLLECTION_VISIBLE);
|
||||
EXPECT_TRUE(bcoll_c0_child0->flags & BONE_COLLECTION_VISIBLE);
|
||||
|
||||
/* Initially, all bone collections should have visible ancestors. */
|
||||
EXPECT_TRUE(bcoll_root0->flags & BONE_COLLECTION_ANCESTORS_VISIBLE);
|
||||
EXPECT_TRUE(bcoll_root1->flags & BONE_COLLECTION_ANCESTORS_VISIBLE);
|
||||
EXPECT_TRUE(bcoll_r0_child0->flags & BONE_COLLECTION_ANCESTORS_VISIBLE);
|
||||
EXPECT_TRUE(bcoll_r0_child1->flags & BONE_COLLECTION_ANCESTORS_VISIBLE);
|
||||
EXPECT_TRUE(bcoll_c0_child0->flags & BONE_COLLECTION_ANCESTORS_VISIBLE);
|
||||
|
||||
/* Mark root_0 as invisible, this should also update its children. */
|
||||
ANIM_bonecoll_hide(&arm, bcoll_root0);
|
||||
|
||||
EXPECT_FALSE(bcoll_root0->flags & BONE_COLLECTION_VISIBLE);
|
||||
EXPECT_TRUE(bcoll_root1->flags & BONE_COLLECTION_VISIBLE);
|
||||
EXPECT_TRUE(bcoll_r0_child0->flags & BONE_COLLECTION_VISIBLE);
|
||||
EXPECT_TRUE(bcoll_r0_child1->flags & BONE_COLLECTION_VISIBLE);
|
||||
EXPECT_TRUE(bcoll_c0_child0->flags & BONE_COLLECTION_VISIBLE);
|
||||
|
||||
EXPECT_TRUE(bcoll_root0->flags & BONE_COLLECTION_ANCESTORS_VISIBLE);
|
||||
EXPECT_TRUE(bcoll_root1->flags & BONE_COLLECTION_ANCESTORS_VISIBLE);
|
||||
EXPECT_FALSE(bcoll_r0_child0->flags & BONE_COLLECTION_ANCESTORS_VISIBLE);
|
||||
EXPECT_FALSE(bcoll_r0_child1->flags & BONE_COLLECTION_ANCESTORS_VISIBLE);
|
||||
EXPECT_FALSE(bcoll_c0_child0->flags & BONE_COLLECTION_ANCESTORS_VISIBLE);
|
||||
|
||||
/* Move r0_child0 to root1, that should change its BONE_COLLECTION_ANCESTORS_VISIBLE */
|
||||
const int root1_index = armature_bonecoll_find_index(&arm, bcoll_root1);
|
||||
armature_bonecoll_move_to_parent(&arm, child0_index, 0, root0_index, root1_index);
|
||||
|
||||
EXPECT_FALSE(bcoll_root0->flags & BONE_COLLECTION_VISIBLE);
|
||||
EXPECT_TRUE(bcoll_root1->flags & BONE_COLLECTION_VISIBLE);
|
||||
EXPECT_TRUE(bcoll_r0_child0->flags & BONE_COLLECTION_VISIBLE);
|
||||
EXPECT_TRUE(bcoll_r0_child1->flags & BONE_COLLECTION_VISIBLE);
|
||||
EXPECT_TRUE(bcoll_c0_child0->flags & BONE_COLLECTION_VISIBLE);
|
||||
|
||||
EXPECT_TRUE(bcoll_root0->flags & BONE_COLLECTION_ANCESTORS_VISIBLE);
|
||||
EXPECT_TRUE(bcoll_root1->flags & BONE_COLLECTION_ANCESTORS_VISIBLE);
|
||||
EXPECT_TRUE(bcoll_r0_child0->flags & BONE_COLLECTION_ANCESTORS_VISIBLE)
|
||||
<< "The child that was moved to a visible root should be affected";
|
||||
EXPECT_FALSE(bcoll_r0_child1->flags & BONE_COLLECTION_ANCESTORS_VISIBLE)
|
||||
<< "The child that wasn't moved should not be affected.";
|
||||
EXPECT_TRUE(bcoll_c0_child0->flags & BONE_COLLECTION_ANCESTORS_VISIBLE)
|
||||
<< "The grandchild that was indirectly moved to a visible root should be affected";
|
||||
|
||||
/* Add a new child to root0, it should have the right flags. */
|
||||
BoneCollection *bcoll_r0_child2 = ANIM_armature_bonecoll_new(&arm, "r0_child2", root0_index);
|
||||
EXPECT_TRUE(bcoll_r0_child2->flags & BONE_COLLECTION_VISIBLE);
|
||||
EXPECT_FALSE(bcoll_r0_child2->flags & BONE_COLLECTION_ANCESTORS_VISIBLE);
|
||||
}
|
||||
|
||||
TEST_F(ANIM_armature_bone_collections, bones_assign_unassign)
|
||||
{
|
||||
BoneCollection *bcoll = ANIM_armature_bonecoll_new(&arm, "collection");
|
||||
|
|
|
@ -3149,4 +3149,37 @@ blender::Span<BoneCollection *> bArmature::collections_span()
|
|||
return blender::Span(collection_array, collection_array_num);
|
||||
}
|
||||
|
||||
blender::Span<const BoneCollection *> bArmature::collections_roots() const
|
||||
{
|
||||
return blender::Span(collection_array, collection_root_count);
|
||||
}
|
||||
blender::Span<BoneCollection *> bArmature::collections_roots()
|
||||
{
|
||||
return blender::Span(collection_array, collection_root_count);
|
||||
}
|
||||
|
||||
blender::Span<const BoneCollection *> bArmature::collection_children(
|
||||
const BoneCollection *parent) const
|
||||
{
|
||||
return blender::Span(&collection_array[parent->child_index], parent->child_count);
|
||||
}
|
||||
|
||||
blender::Span<BoneCollection *> bArmature::collection_children(BoneCollection *parent)
|
||||
{
|
||||
return blender::Span(&collection_array[parent->child_index], parent->child_count);
|
||||
}
|
||||
|
||||
bool BoneCollection::is_visible() const
|
||||
{
|
||||
return this->flags & BONE_COLLECTION_VISIBLE;
|
||||
}
|
||||
bool BoneCollection::is_visible_ancestors() const
|
||||
{
|
||||
return this->flags & BONE_COLLECTION_ANCESTORS_VISIBLE;
|
||||
}
|
||||
bool BoneCollection::is_visible_effectively() const
|
||||
{
|
||||
return this->is_visible() && this->is_visible_ancestors();
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
|
|
@ -183,7 +183,7 @@ static void version_bonelayers_to_bonecollections(Main *bmain)
|
|||
layermask_collection.append(std::make_pair(layer_mask, bcoll));
|
||||
|
||||
if ((arm->layer & layer_mask) == 0) {
|
||||
ANIM_bonecoll_hide(bcoll);
|
||||
ANIM_bonecoll_hide(arm, bcoll);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -223,7 +223,7 @@ static void version_bonegroups_to_bonecollections(Main *bmain)
|
|||
* groups did not have any impact on this. To retain the behavior, that
|
||||
* hiding all layers a bone is on hides the bone, the
|
||||
* bone-group-collections should be created hidden. */
|
||||
ANIM_bonecoll_hide(bcoll);
|
||||
ANIM_bonecoll_hide(arm, bcoll);
|
||||
}
|
||||
|
||||
/* Assign the bones to their bone group based collection. */
|
||||
|
|
|
@ -105,7 +105,7 @@ class BoneCollectionDropTarget : public TreeViewItemDropTarget {
|
|||
return false;
|
||||
}
|
||||
|
||||
/* Do not allow dropping onto its own decendants. */
|
||||
/* Do not allow dropping onto its own descendants. */
|
||||
if (armature_bonecoll_is_decendent_of(
|
||||
drag_arm_bcoll->armature, drag_arm_bcoll->bcoll_index, drop_bonecoll_.bcoll_index))
|
||||
{
|
||||
|
@ -221,14 +221,12 @@ class BoneCollectionItem : public AbstractTreeViewItem {
|
|||
|
||||
/* Visibility eyecon. */
|
||||
{
|
||||
const bool is_visible = bone_collection_.flags & BONE_COLLECTION_VISIBLE;
|
||||
uiLayout *visibility_sub = uiLayoutRow(sub, true);
|
||||
uiLayoutSetActive(visibility_sub, bone_collection_.is_visible_ancestors());
|
||||
|
||||
const int icon = bone_collection_.is_visible() ? ICON_HIDE_OFF : ICON_HIDE_ON;
|
||||
PointerRNA bcoll_ptr = rna_pointer();
|
||||
uiItemR(sub,
|
||||
&bcoll_ptr,
|
||||
"is_visible",
|
||||
UI_ITEM_R_ICON_ONLY,
|
||||
"",
|
||||
is_visible ? ICON_HIDE_OFF : ICON_HIDE_ON);
|
||||
uiItemR(visibility_sub, &bcoll_ptr, "is_visible", UI_ITEM_R_ICON_ONLY, "", icon);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -226,10 +226,10 @@ static void bc_add_armature_collections(COLLADAFW::Node *node,
|
|||
for (const std::string &name : collection_names) {
|
||||
BoneCollection *bcoll = ANIM_armature_bonecoll_new(arm, name.c_str());
|
||||
if (visible_names_set.find(name) == visible_names_set.end()) {
|
||||
ANIM_bonecoll_hide(bcoll);
|
||||
ANIM_bonecoll_hide(arm, bcoll);
|
||||
}
|
||||
else {
|
||||
ANIM_bonecoll_show(bcoll);
|
||||
ANIM_bonecoll_show(arm, bcoll);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -226,6 +226,14 @@ typedef struct bArmature {
|
|||
/* Collection array access for convenient for-loop iteration. */
|
||||
blender::Span<const BoneCollection *> collections_span() const;
|
||||
blender::Span<BoneCollection *> collections_span();
|
||||
|
||||
/* Span of all root collections. */
|
||||
blender::Span<const BoneCollection *> collections_roots() const;
|
||||
blender::Span<BoneCollection *> collections_roots();
|
||||
|
||||
/* Return the span of children of the given bone collection. */
|
||||
blender::Span<const BoneCollection *> collection_children(const BoneCollection *parent) const;
|
||||
blender::Span<BoneCollection *> collection_children(BoneCollection *parent);
|
||||
#endif
|
||||
} bArmature;
|
||||
|
||||
|
@ -264,6 +272,35 @@ typedef struct BoneCollection {
|
|||
|
||||
/** Custom properties. */
|
||||
struct IDProperty *prop;
|
||||
|
||||
#ifdef __cplusplus
|
||||
/**
|
||||
* Return whether this collection is marked as 'visible'.
|
||||
*
|
||||
* Note that its effective visibility depends on the visibility of its ancestors as well.
|
||||
*
|
||||
* \see is_visible_effectively
|
||||
* \see ANIM_bonecoll_show
|
||||
* \see ANIM_bonecoll_hide
|
||||
*/
|
||||
bool is_visible() const;
|
||||
|
||||
/**
|
||||
* Return whether this collection's ancestors are visible or not.
|
||||
*
|
||||
* \see is_visible_effectively
|
||||
*/
|
||||
bool is_visible_ancestors() const;
|
||||
|
||||
/**
|
||||
* Return whether this collection is effectively visible.
|
||||
*
|
||||
* \return true when this collection and all its ancestors are visible.
|
||||
*
|
||||
* \see is_visible
|
||||
dr.sybren marked this conversation as resolved
Outdated
|
||||
*/
|
||||
bool is_visible_effectively() const;
|
||||
I usually went for e.g. There is also another way of doing it, where I usually went for e.g. `is_visible_in_hierarchy` in such cases.
"effectively" doesn't tell me much tbh
There is also another way of doing it, where `is_visible` returns the effective visibility
and you have another function `get_collection_visibility` (or similar) to get the actual flag
Sybren A. Stüvel
commented
This is something I've been struggling with indeed, as there are now three "visibilities":
I don't think there's names without any downside; I'll keep the naming as is, and double-check the documentation to ensure things are as clearly described as possible. This is something I've been struggling with indeed, as there are now three "visibilities":
1. the flag
2. the effective visibility
3. whether the bone collection can be seen in the tree view (i.e. its parents are expanded)
I don't think there's names without any downside; `is_visible_in_hierarchy` to me feels more like the 3rd option, because "the hierarchy" conjures the image of the tree structure.
I'll keep the naming as is, and double-check the documentation to ensure things are as clearly described as possible.
hm, imo the bone collection shouldn't need to know if its expanded or not. hm, imo the bone collection shouldn't need to know if its expanded or not.
but this is an issue of blender in general where the core datastructure and the GUI are too closely connected.
|
||||
#endif
|
||||
} BoneCollection;
|
||||
|
||||
/** Membership relation of a bone with a bone collection. */
|
||||
|
@ -461,11 +498,19 @@ typedef enum eBone_BBoneHandleFlag {
|
|||
|
||||
/** #BoneCollection.flag */
|
||||
typedef enum eBoneCollection_Flag {
|
||||
BONE_COLLECTION_VISIBLE = (1 << 0),
|
||||
BONE_COLLECTION_VISIBLE = (1 << 0), /* Visibility flag of this particular collection. */
|
||||
BONE_COLLECTION_SELECTABLE = (1 << 1), /* Intended to be implemented in the not-so-far future. */
|
||||
BONE_COLLECTION_OVERRIDE_LIBRARY_LOCAL = (1 << 2), /* Added by a local library override. */
|
||||
|
||||
/**
|
||||
* Set when all ancestors are visible.
|
||||
*
|
||||
* This would actually be a runtime flag, but bone collections don't have a
|
||||
* runtime struct yet, and the addition of one more flag doesn't seem worth
|
||||
* the effort. */
|
||||
BONE_COLLECTION_ANCESTORS_VISIBLE = (1 << 3),
|
||||
} eBoneCollection_Flag;
|
||||
ENUM_OPERATORS(eBoneCollection_Flag, BONE_COLLECTION_OVERRIDE_LIBRARY_LOCAL)
|
||||
ENUM_OPERATORS(eBoneCollection_Flag, BONE_COLLECTION_ANCESTORS_VISIBLE)
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
|
|
|
@ -345,6 +345,23 @@ static void rna_BoneCollection_name_set(PointerRNA *ptr, const char *name)
|
|||
WM_main_add_notifier(NC_OBJECT | ND_BONE_COLLECTION, &arm->id);
|
||||
}
|
||||
|
||||
static void rna_BoneCollection_is_visible_set(PointerRNA *ptr, const bool is_visible)
|
||||
{
|
||||
bArmature *arm = (bArmature *)ptr->owner_id;
|
||||
BoneCollection *bcoll = (BoneCollection *)ptr->data;
|
||||
|
||||
ANIM_armature_bonecoll_is_visible_set(arm, bcoll, is_visible);
|
||||
|
||||
WM_main_add_notifier(NC_OBJECT | ND_BONE_COLLECTION, &arm->id);
|
||||
WM_main_add_notifier(NC_OBJECT | ND_POSE, &arm->id);
|
||||
}
|
||||
|
||||
static bool rna_BoneCollection_is_visible_effectively_get(PointerRNA *ptr)
|
||||
{
|
||||
const BoneCollection *bcoll = (BoneCollection *)ptr->data;
|
||||
return bcoll->is_visible_effectively();
|
||||
}
|
||||
|
||||
static char *rna_BoneCollection_path(const PointerRNA *ptr)
|
||||
{
|
||||
const BoneCollection *bcoll = (const BoneCollection *)ptr->data;
|
||||
|
@ -2216,7 +2233,24 @@ static void rna_def_bonecollection(BlenderRNA *brna)
|
|||
prop, "Visible", "Bones in this collection will be visible in pose/object mode");
|
||||
RNA_def_property_flag(prop, PROP_LIB_EXCEPTION);
|
||||
RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
|
||||
RNA_def_property_update(prop, NC_OBJECT | ND_POSE, nullptr);
|
||||
RNA_def_property_boolean_funcs(prop, nullptr, "rna_BoneCollection_is_visible_set");
|
||||
|
||||
prop = RNA_def_property(srna, "is_visible_ancestors", PROP_BOOLEAN, PROP_NONE);
|
||||
RNA_def_property_boolean_sdna(prop, nullptr, "flags", BONE_COLLECTION_ANCESTORS_VISIBLE);
|
||||
RNA_def_property_ui_text(prop,
|
||||
"Ancestors Effectively Visible",
|
||||
"True when all of the ancestors of this bone collection are marked as "
|
||||
"visible; always True for root bone collections");
|
||||
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
|
||||
|
||||
prop = RNA_def_property(srna, "is_visible_effectively", PROP_BOOLEAN, PROP_NONE);
|
||||
RNA_def_property_boolean_funcs(prop, "rna_BoneCollection_is_visible_effectively_get", nullptr);
|
||||
RNA_def_property_ui_text(
|
||||
prop,
|
||||
"Effective Visibility",
|
||||
"Whether this bone collection is effectively visible in the viewport. This is True when "
|
||||
"this bone collection and all of its ancestors are visible");
|
||||
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
|
||||
|
||||
prop = RNA_def_property(srna, "is_local_override", PROP_BOOLEAN, PROP_NONE);
|
||||
RNA_def_property_boolean_sdna(prop, nullptr, "flags", BONE_COLLECTION_OVERRIDE_LIBRARY_LOCAL);
|
||||
|
|
Spelling, throughout this PR: decendant -> descendant
(Don't feel bad: I almost didn't catch it myself, ha ha.)