Anim: bone collections, add 'solo' flag
Add the 'solo' flag to bone collections, effectively adding another layer of visibility controls. If there is _any_ bone collection with this flag enabled, only this collection (and others with this flag enabled) will be visible. In RNA, the following properties are exposed: - `bone_collection.is_solo`: writable property to manage the solo flag. - `armature.is_solo_active`: read-only property that is `True` when any bone collection has `is_solo = True`. The RNA property `bone_collection.is_visible_effectively` now also takes the solo flag into account. Pull Request: https://projects.blender.org/blender/blender/pulls/117414
This commit is contained in:
@@ -239,6 +239,23 @@ void ANIM_armature_bonecoll_is_visible_set(bArmature *armature,
|
||||
BoneCollection *bcoll,
|
||||
bool is_visible);
|
||||
|
||||
/**
|
||||
* Set or clear this bone collection's solo flag.
|
||||
*/
|
||||
void ANIM_armature_bonecoll_solo_set(bArmature *armature, BoneCollection *bcoll, bool is_solo);
|
||||
|
||||
/**
|
||||
* Refresh the ARM_BCOLL_SOLO_ACTIVE flag.
|
||||
*/
|
||||
void ANIM_armature_refresh_solo_active(bArmature *armature);
|
||||
|
||||
/**
|
||||
* Determine whether this bone collection is visible, taking into account the visibility of its
|
||||
* ancestors and the "solo" flags that are in use.
|
||||
*/
|
||||
bool ANIM_armature_bonecoll_is_visible_effectively(const bArmature *armature,
|
||||
const BoneCollection *bcoll);
|
||||
|
||||
/**
|
||||
* Assign the bone to the bone collection.
|
||||
*
|
||||
|
||||
@@ -792,6 +792,54 @@ void ANIM_armature_bonecoll_is_visible_set(bArmature *armature,
|
||||
}
|
||||
}
|
||||
|
||||
void ANIM_armature_bonecoll_solo_set(bArmature *armature,
|
||||
BoneCollection *bcoll,
|
||||
const bool is_solo)
|
||||
{
|
||||
if (is_solo) {
|
||||
/* Enabling solo is simple. */
|
||||
bcoll->flags |= BONE_COLLECTION_SOLO;
|
||||
armature->flag |= ARM_BCOLL_SOLO_ACTIVE;
|
||||
return;
|
||||
}
|
||||
|
||||
/* Disabling is harder, as the armature flag can only be disabled when there
|
||||
* are no more bone collections with the SOLO flag set. */
|
||||
bcoll->flags &= ~BONE_COLLECTION_SOLO;
|
||||
ANIM_armature_refresh_solo_active(armature);
|
||||
}
|
||||
|
||||
void ANIM_armature_refresh_solo_active(bArmature *armature)
|
||||
{
|
||||
bool any_bcoll_solo = false;
|
||||
for (const BoneCollection *bcoll : armature->collections_span()) {
|
||||
if (bcoll->flags & BONE_COLLECTION_SOLO) {
|
||||
any_bcoll_solo = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (any_bcoll_solo) {
|
||||
armature->flag |= ARM_BCOLL_SOLO_ACTIVE;
|
||||
}
|
||||
else {
|
||||
armature->flag &= ~ARM_BCOLL_SOLO_ACTIVE;
|
||||
}
|
||||
}
|
||||
|
||||
bool ANIM_armature_bonecoll_is_visible_effectively(const bArmature *armature,
|
||||
const BoneCollection *bcoll)
|
||||
{
|
||||
const bool is_solo_active = armature->flag & ARM_BCOLL_SOLO_ACTIVE;
|
||||
|
||||
if (is_solo_active) {
|
||||
/* If soloing is active, nothing in the hierarchy matters except the solo flag. */
|
||||
return bcoll->is_solo();
|
||||
}
|
||||
|
||||
return bcoll->is_visible_with_ancestors();
|
||||
}
|
||||
|
||||
/* Store the bone's membership on the collection. */
|
||||
static void add_membership(BoneCollection *bcoll, Bone *bone)
|
||||
{
|
||||
@@ -925,7 +973,8 @@ void ANIM_armature_bonecoll_reconstruct(bArmature *armature)
|
||||
});
|
||||
}
|
||||
|
||||
static bool any_bone_collection_visible(const ListBase /*BoneCollectionRef*/ *collection_refs)
|
||||
static bool any_bone_collection_visible(const bArmature *armature,
|
||||
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)) {
|
||||
@@ -934,23 +983,20 @@ 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->is_visible_with_ancestors()) {
|
||||
if (ANIM_armature_bonecoll_is_visible_effectively(armature, bcoll)) {
|
||||
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_bone_in_visible_collection(const bArmature * /*armature*/, const Bone *bone)
|
||||
bool ANIM_bone_in_visible_collection(const bArmature *armature, const Bone *bone)
|
||||
{
|
||||
return any_bone_collection_visible(&bone->runtime.collections);
|
||||
return any_bone_collection_visible(armature, &bone->runtime.collections);
|
||||
}
|
||||
bool ANIM_bonecoll_is_visible_editbone(const bArmature * /*armature*/, const EditBone *ebone)
|
||||
bool ANIM_bonecoll_is_visible_editbone(const bArmature *armature, const EditBone *ebone)
|
||||
{
|
||||
return any_bone_collection_visible(&ebone->bone_collections);
|
||||
return any_bone_collection_visible(armature, &ebone->bone_collections);
|
||||
}
|
||||
|
||||
void ANIM_armature_bonecoll_show_all(bArmature *armature)
|
||||
|
||||
@@ -1497,6 +1497,35 @@ TEST_F(ANIM_armature_bone_collections_testlist, child_number_set__siblings)
|
||||
EXPECT_TRUE(expect_bcolls({"root", "child1", "child2", "child0", "child1_0"}));
|
||||
}
|
||||
|
||||
TEST_F(ANIM_armature_bone_collections_testlist, bone_collection_solo)
|
||||
{
|
||||
EXPECT_FALSE(arm.flag & ARM_BCOLL_SOLO_ACTIVE) << "By default no solo'ing should be active";
|
||||
|
||||
/* Enable solo. */
|
||||
EXPECT_FALSE(child1->flags & BONE_COLLECTION_SOLO);
|
||||
ANIM_armature_bonecoll_solo_set(&arm, child1, true);
|
||||
EXPECT_TRUE(child1->flags & BONE_COLLECTION_SOLO);
|
||||
EXPECT_TRUE(arm.flag & ARM_BCOLL_SOLO_ACTIVE);
|
||||
|
||||
/* Enable solo on another bone collection. */
|
||||
EXPECT_FALSE(child1_0->flags & BONE_COLLECTION_SOLO);
|
||||
ANIM_armature_bonecoll_solo_set(&arm, child1_0, true);
|
||||
EXPECT_TRUE(child1_0->flags & BONE_COLLECTION_SOLO);
|
||||
EXPECT_TRUE(arm.flag & ARM_BCOLL_SOLO_ACTIVE);
|
||||
|
||||
/* Disable the first solo flag. */
|
||||
EXPECT_TRUE(child1->flags & BONE_COLLECTION_SOLO);
|
||||
ANIM_armature_bonecoll_solo_set(&arm, child1, false);
|
||||
EXPECT_FALSE(child1->flags & BONE_COLLECTION_SOLO);
|
||||
EXPECT_TRUE(arm.flag & ARM_BCOLL_SOLO_ACTIVE);
|
||||
|
||||
/* Disable the second solo flag. This should also disable the ARM_BCOLL_SOLO_ACTIVE flag. */
|
||||
EXPECT_TRUE(child1_0->flags & BONE_COLLECTION_SOLO);
|
||||
ANIM_armature_bonecoll_solo_set(&arm, child1_0, false);
|
||||
EXPECT_FALSE(child1_0->flags & BONE_COLLECTION_SOLO);
|
||||
EXPECT_FALSE(arm.flag & ARM_BCOLL_SOLO_ACTIVE);
|
||||
}
|
||||
|
||||
class ANIM_armature_bone_collections_liboverrides
|
||||
: public ANIM_armature_bone_collections_testlist {
|
||||
protected:
|
||||
|
||||
@@ -3186,5 +3186,9 @@ bool BoneCollection::is_visible_with_ancestors() const
|
||||
{
|
||||
return this->is_visible() && this->is_visible_ancestors();
|
||||
}
|
||||
bool BoneCollection::is_solo() const
|
||||
{
|
||||
return this->flags & BONE_COLLECTION_SOLO;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
@@ -4888,7 +4888,7 @@ void blo_do_versions_280(FileData *fd, Library * /*lib*/, Main *bmain)
|
||||
}
|
||||
|
||||
LISTBASE_FOREACH (bArmature *, arm, &bmain->armatures) {
|
||||
arm->flag &= ~(ARM_FLAG_UNUSED_1 | ARM_DRAW_RELATION_FROM_HEAD | ARM_FLAG_UNUSED_6 |
|
||||
arm->flag &= ~(ARM_FLAG_UNUSED_1 | ARM_DRAW_RELATION_FROM_HEAD | ARM_BCOLL_SOLO_ACTIVE |
|
||||
ARM_FLAG_UNUSED_7 | ARM_FLAG_UNUSED_12);
|
||||
}
|
||||
|
||||
@@ -5367,7 +5367,7 @@ void blo_do_versions_280(FileData *fd, Library * /*lib*/, Main *bmain)
|
||||
}
|
||||
|
||||
LISTBASE_FOREACH (bArmature *, arm, &bmain->armatures) {
|
||||
arm->flag &= ~(ARM_FLAG_UNUSED_6);
|
||||
arm->flag &= ~(ARM_BCOLL_SOLO_ACTIVE);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -236,13 +236,22 @@ class BoneCollectionItem : public AbstractTreeViewItem {
|
||||
|
||||
/* Visibility eye icon. */
|
||||
{
|
||||
const bool is_solo_active = armature_.flag & ARM_BCOLL_SOLO_ACTIVE;
|
||||
uiLayout *visibility_sub = uiLayoutRow(sub, true);
|
||||
uiLayoutSetActive(visibility_sub, bone_collection_.is_visible_ancestors());
|
||||
uiLayoutSetActive(visibility_sub,
|
||||
!is_solo_active && bone_collection_.is_visible_ancestors());
|
||||
|
||||
const int icon = bone_collection_.is_visible() ? ICON_HIDE_OFF : ICON_HIDE_ON;
|
||||
PointerRNA bcoll_ptr = rna_pointer();
|
||||
uiItemR(visibility_sub, &bcoll_ptr, "is_visible", UI_ITEM_R_ICON_ONLY, "", icon);
|
||||
}
|
||||
|
||||
/* Solo icon. */
|
||||
{
|
||||
const int icon = bone_collection_.is_solo() ? ICON_SOLO_ON : ICON_SOLO_OFF;
|
||||
PointerRNA bcoll_ptr = rna_pointer();
|
||||
uiItemR(sub, &bcoll_ptr, "is_solo", UI_ITEM_R_ICON_ONLY, "", icon);
|
||||
}
|
||||
}
|
||||
|
||||
void build_context_menu(bContext &C, uiLayout &column) const override
|
||||
|
||||
@@ -301,6 +301,11 @@ typedef struct BoneCollection {
|
||||
* \see is_visible
|
||||
*/
|
||||
bool is_visible_with_ancestors() const;
|
||||
|
||||
/**
|
||||
* Return whether this collection is marked as 'solo'.
|
||||
*/
|
||||
bool is_solo() const;
|
||||
#endif
|
||||
} BoneCollection;
|
||||
|
||||
@@ -334,8 +339,14 @@ typedef enum eArmature_Flag {
|
||||
* from the tail, set = drawn from the head). Only controls the parent side of
|
||||
* the line; the child side is always drawn to the head of the bone. */
|
||||
ARM_DRAW_RELATION_FROM_HEAD = (1 << 5), /* Cleared in versioning of pre-2.80 files. */
|
||||
ARM_FLAG_UNUSED_6 = (1 << 6), /* cleared */
|
||||
ARM_FLAG_UNUSED_7 = (1 << 7), /* cleared */
|
||||
/**
|
||||
* Whether any bone collection is marked with the 'solo' flag.
|
||||
* When this is the case, bone collection visibility flags don't matter any more, and only ones
|
||||
* that have their 'solo' flag set will be visible.
|
||||
*
|
||||
* \see eBoneCollection_Flag::BONE_COLLECTION_SOLO */
|
||||
ARM_BCOLL_SOLO_ACTIVE = (1 << 6), /* Cleared in versioning of pre-2.80 files. */
|
||||
ARM_FLAG_UNUSED_7 = (1 << 7), /* cleared */
|
||||
ARM_MIRROR_EDIT = (1 << 8),
|
||||
ARM_FLAG_UNUSED_9 = (1 << 9),
|
||||
/** Made option negative, for backwards compatibility. */
|
||||
@@ -510,8 +521,21 @@ typedef enum eBoneCollection_Flag {
|
||||
* runtime struct yet, and the addition of one more flag doesn't seem worth
|
||||
* the effort. */
|
||||
BONE_COLLECTION_ANCESTORS_VISIBLE = (1 << 3),
|
||||
|
||||
/**
|
||||
* Whether this bone collection is marked as 'solo'.
|
||||
*
|
||||
* If no bone collections have this flag set, visibility is determined by
|
||||
* BONE_COLLECTION_VISIBLE.
|
||||
*
|
||||
* If there is any bone collection with the BONE_COLLECTION_SOLO flag enabled, all bone
|
||||
* collections are effectively hidden, except other collections with this flag enabled.
|
||||
*
|
||||
* \see eArmature_Flag::ARM_BCOLL_SOLO_ACTIVE
|
||||
*/
|
||||
BONE_COLLECTION_SOLO = (1 << 4),
|
||||
} eBoneCollection_Flag;
|
||||
ENUM_OPERATORS(eBoneCollection_Flag, BONE_COLLECTION_ANCESTORS_VISIBLE)
|
||||
ENUM_OPERATORS(eBoneCollection_Flag, BONE_COLLECTION_SOLO)
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
|
||||
@@ -389,8 +389,20 @@ static void rna_BoneCollection_is_visible_set(PointerRNA *ptr, const bool is_vis
|
||||
|
||||
static bool rna_BoneCollection_is_visible_effectively_get(PointerRNA *ptr)
|
||||
{
|
||||
const bArmature *arm = (bArmature *)ptr->owner_id;
|
||||
const BoneCollection *bcoll = (BoneCollection *)ptr->data;
|
||||
return bcoll->is_visible_with_ancestors();
|
||||
return ANIM_armature_bonecoll_is_visible_effectively(arm, bcoll);
|
||||
}
|
||||
|
||||
static void rna_BoneCollection_is_solo_set(PointerRNA *ptr, const bool is_solo)
|
||||
{
|
||||
bArmature *arm = (bArmature *)ptr->owner_id;
|
||||
BoneCollection *bcoll = (BoneCollection *)ptr->data;
|
||||
|
||||
ANIM_armature_bonecoll_solo_set(arm, bcoll, is_solo);
|
||||
|
||||
WM_main_add_notifier(NC_OBJECT | ND_BONE_COLLECTION, &arm->id);
|
||||
WM_main_add_notifier(NC_OBJECT | ND_POSE, &arm->id);
|
||||
}
|
||||
|
||||
static char *rna_BoneCollection_path(const PointerRNA *ptr)
|
||||
@@ -2018,6 +2030,14 @@ static void rna_def_armature_collections(BlenderRNA *brna, PropertyRNA *cprop)
|
||||
"is no active collection");
|
||||
RNA_def_property_string_funcs(prop, nullptr, nullptr, "rna_BoneCollections_active_name_set");
|
||||
|
||||
prop = RNA_def_property(srna, "is_solo_active", PROP_BOOLEAN, PROP_NONE);
|
||||
RNA_def_property_boolean_sdna(prop, nullptr, "flag", ARM_BCOLL_SOLO_ACTIVE);
|
||||
RNA_def_property_ui_text(
|
||||
prop,
|
||||
"Solo Active",
|
||||
"Read-ony flag that indicates there is at least one bone collection marked as 'solo'");
|
||||
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
|
||||
|
||||
/* Armature.collections.new(...) */
|
||||
func = RNA_def_function(srna, "new", "rna_BoneCollections_new");
|
||||
RNA_def_function_ui_description(func, "Add a new empty bone collection to the armature");
|
||||
@@ -2311,9 +2331,17 @@ static void rna_def_bonecollection(BlenderRNA *brna)
|
||||
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");
|
||||
"this bone collection and all of its ancestors are visible, or when it is marked as 'solo'");
|
||||
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
|
||||
|
||||
prop = RNA_def_property(srna, "is_solo", PROP_BOOLEAN, PROP_NONE);
|
||||
RNA_def_property_boolean_sdna(prop, nullptr, "flags", BONE_COLLECTION_SOLO);
|
||||
RNA_def_property_ui_text(
|
||||
prop, "Solo", "Show only this bone collection, and others also marked as 'solo'");
|
||||
RNA_def_property_flag(prop, PROP_LIB_EXCEPTION);
|
||||
RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
|
||||
RNA_def_property_boolean_funcs(prop, nullptr, "rna_BoneCollection_is_solo_set");
|
||||
|
||||
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);
|
||||
RNA_def_property_ui_text(
|
||||
|
||||
Reference in New Issue
Block a user