Blender V4.3
animrig/intern/bone_collections.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
9#include "BLI_listbase.h"
10#include "BLI_map.hh"
11#include "BLI_string.h"
12#include "BLI_string_utf8.h"
13#include "BLI_string_utils.hh"
14#include "BLI_utildefines.h"
15
16#include "BLT_translation.hh"
17
18#include "DNA_armature_types.h"
19
20#include "MEM_guardedalloc.h"
21
22#include "BKE_animsys.h"
23#include "BKE_idprop.hh"
24#include "BKE_lib_id.hh"
25#include "BKE_lib_override.hh"
26
27#include "ANIM_armature_iter.hh"
29
31
32#include <cstring>
33#include <string>
34
35using std::strcmp;
36
37using namespace blender::animrig;
38
39namespace {
40
42constexpr eBoneCollection_Flag default_flags = BONE_COLLECTION_VISIBLE |
45constexpr auto bonecoll_default_name = "Bones";
46
47} // namespace
48
49static void ancestors_visible_update(bArmature *armature,
50 const BoneCollection *parent_bcoll,
51 BoneCollection *bcoll);
52
54{
55 if (name == nullptr || name[0] == '\0') {
56 /* Use a default name if no name was given. */
57 name = DATA_(bonecoll_default_name);
58 }
59
60 /* NOTE: the collection name may change after the collection is added to an
61 * armature, to ensure it is unique within the armature. */
62 BoneCollection *bcoll = MEM_cnew<BoneCollection>(__func__);
63
64 STRNCPY_UTF8(bcoll->name, name);
65 bcoll->flags = default_flags;
66
67 bcoll->prop = nullptr;
68
69 return bcoll;
70}
71
72void ANIM_bonecoll_free(BoneCollection *bcoll, const bool do_id_user_count)
73{
75 "bone collection still has bones assigned to it, will cause dangling pointers in "
76 "bone runtime data");
77 if (bcoll->prop) {
78 IDP_FreeProperty_ex(bcoll->prop, do_id_user_count);
79 }
80 MEM_delete(bcoll);
81}
82
89{
90 LISTBASE_FOREACH (BoneCollectionMember *, member, &bcoll->bones) {
91 BoneCollectionReference *ref = MEM_cnew<BoneCollectionReference>(__func__);
92 ref->bcoll = bcoll;
93 BLI_addtail(&member->bone->runtime.collections, ref);
94 }
95}
96
98{
101
102 /* Make sure the BONE_COLLECTION_ANCESTORS_VISIBLE flags are set correctly. */
103 for (BoneCollection *bcoll : armature->collections_roots()) {
104 ancestors_visible_update(armature, nullptr, bcoll);
105 }
106
107 /* Construct the bone-to-collections mapping. */
108 for (BoneCollection *bcoll : armature->collections_span()) {
110 }
111}
112
114{
115 /* Free the bone-to-its-collections mapping. */
117 [&](Bone *bone) { BLI_freelistN(&bone->runtime.collections); });
118}
119
126{
127 struct DupNameCheckData {
128 bArmature *arm;
129 BoneCollection *bcoll;
130 };
131
132 /* Cannot capture armature & bcoll by reference in the lambda, as that would change its signature
133 * and no longer be compatible with BLI_uniquename_cb(). */
134 auto bonecoll_name_is_duplicate = [](void *arg, const char *name) -> bool {
135 DupNameCheckData *data = static_cast<DupNameCheckData *>(arg);
136 for (BoneCollection *bcoll : data->arm->collections_span()) {
137 if (bcoll != data->bcoll && STREQ(bcoll->name, name)) {
138 return true;
139 }
140 }
141 return false;
142 };
143
144 DupNameCheckData check_data = {armature, bcoll};
145 BLI_uniquename_cb(bonecoll_name_is_duplicate,
146 &check_data,
147 DATA_(bonecoll_default_name),
148 '.',
149 bcoll->name,
150 sizeof(bcoll->name));
151}
152
160static void bonecoll_insert_at_index(bArmature *armature, BoneCollection *bcoll, const int index)
161{
162 BLI_assert(index <= armature->collection_array_num);
163
165 armature->collection_array,
166 sizeof(BoneCollection *) * (armature->collection_array_num + 1),
167 __func__);
168
169 /* To keep the memory consistent, insert the new element at the end of the
170 * now-grown array, then rotate it into place. */
171 armature->collection_array[armature->collection_array_num] = bcoll;
172 armature->collection_array_num++;
173
174 const int rotate_count = armature->collection_array_num - index - 1;
175 internal::bonecolls_rotate_block(armature, index, rotate_count, +1);
176
177 if (armature->runtime.active_collection_index >= index) {
179 armature->runtime.active_collection_index + 1);
180 }
181}
182
183static void bonecoll_insert_as_root(bArmature *armature, BoneCollection *bcoll, int at_index)
184{
185 BLI_assert(at_index >= -1);
186 BLI_assert(at_index <= armature->collection_root_count);
187 if (at_index < 0) {
188 at_index = armature->collection_root_count;
189 }
190
191 bonecoll_insert_at_index(armature, bcoll, at_index);
192 armature->collection_root_count++;
193
194 ancestors_visible_update(armature, nullptr, bcoll);
195}
196
198 BoneCollection *bcoll,
199 const int parent_index)
200{
201 BLI_assert_msg(parent_index >= 0, "Armature bone collection index should be 0 or larger");
202 BLI_assert_msg(parent_index < armature->collection_array_num,
203 "Parent bone collection index should not point beyond the end of the array");
204
205 BoneCollection *parent = armature->collection_array[parent_index];
206 if (parent->child_index == 0) {
207 /* This parent doesn't have any children yet, so place them at the end of the array. */
208 parent->child_index = armature->collection_array_num;
209 }
210 const int insert_at_index = parent->child_index + parent->child_count;
211 bonecoll_insert_at_index(armature, bcoll, insert_at_index);
212 parent->child_count++;
213
214 ancestors_visible_update(armature, parent, bcoll);
215
216 return insert_at_index;
217}
218
220 const char *name,
221 const int parent_index)
222{
223 BoneCollection *bcoll = ANIM_bonecoll_new(name);
224
225 if (!ID_IS_LINKED(&armature->id) && ID_IS_OVERRIDE_LIBRARY(&armature->id)) {
226 /* Mark this bone collection as local override, so that certain operations can be allowed. */
228 }
229
230 bonecoll_ensure_name_unique(armature, bcoll);
231
232 if (parent_index < 0) {
233 bonecoll_insert_as_root(armature, bcoll, -1);
234 }
235 else {
236 bonecoll_insert_as_child(armature, bcoll, parent_index);
237 }
238
239 /* Restore the active bone collection pointer, as its index might have changed. */
241
242 return bcoll;
243}
244
256 const BoneCollection *bcoll_to_copy)
257{
258 BoneCollection *bcoll = static_cast<BoneCollection *>(MEM_dupallocN(bcoll_to_copy));
259
260 /* Reset the child_index and child_count properties. These are unreliable when
261 * coming from an override, as the original array might have been completely
262 * reshuffled. Children will have to be copied separately. */
263 bcoll->child_index = 0;
264 bcoll->child_count = 0;
265
266 if (bcoll->prop) {
267 bcoll->prop = IDP_CopyProperty_ex(bcoll_to_copy->prop,
268 0 /*do_id_user ? 0 : LIB_ID_CREATE_NO_USER_REFCOUNT*/);
269 }
270
271 /* Remap the bone pointers to the given armature, as `bcoll_to_copy` is
272 * assumed to be owned by another armature. */
273 BLI_duplicatelist(&bcoll->bones, &bcoll->bones);
274 BLI_assert_msg(armature_dst->bonehash, "Expected armature bone hash to be there");
275 LISTBASE_FOREACH (BoneCollectionMember *, member, &bcoll->bones) {
276 member->bone = BKE_armature_find_bone_name(const_cast<bArmature *>(armature_dst),
277 member->bone->name);
278 }
279
280 /* Now that the collection points to the right bones, these bones can be
281 * updated to point to this collection. */
283
284 return bcoll;
285}
286
293 const bArmature *armature_src,
294 const int parent_bcoll_dst_index,
295 const BoneCollection *parent_bcoll_src)
296{
297 BLI_assert_msg(parent_bcoll_dst_index >= 0,
298 "this function can only add children to another collection, it cannot add roots");
299
300 /* Iterate over the children in `armature_src`, and clone them one by one into `armature_dst`.
301 *
302 * This uses two loops. The first one adds all the children, the second loop iterates over those
303 * children for the recursion step. As this performs a "breadth-first insertion", it requires
304 * considerably less shuffling of the array as when the recursion was done immediately after
305 * inserting a child. */
306 BoneCollection *parent_bcoll_dst = armature_dst->collection_array[parent_bcoll_dst_index];
307
308 /* Big Fat Assumption: because this code runs as part of the library override system, it is
309 * assumed that the parent is either a newly added root, or another child that was also added by
310 * the liboverride system. Because this would never add a child to an original "sequence of
311 * siblings", insertions of children always happen at the end of the array. This means that
312 * `parent_bcoll_dst_index` remains constant during this entire function. */
313
314 /* Copy & insert all the children. */
315 for (int bcoll_src_index = parent_bcoll_src->child_index;
316 bcoll_src_index < parent_bcoll_src->child_index + parent_bcoll_src->child_count;
317 bcoll_src_index++)
318 {
319 const BoneCollection *bcoll_src = armature_src->collection_array[bcoll_src_index];
320 BoneCollection *bcoll_dst = copy_and_update_ownership(armature_dst, bcoll_src);
321
322 const int bcoll_index_dst = bonecoll_insert_as_child(
323 armature_dst, bcoll_dst, parent_bcoll_dst_index);
324
325#ifndef NDEBUG
326 /* Check that the above Big Fat Assumption holds. */
327 BLI_assert_msg(bcoll_index_dst > parent_bcoll_dst_index,
328 "expecting children to be added to the array AFTER their parent");
329#else
330 (void)bcoll_index_dst;
331#endif
332
333 bonecoll_ensure_name_unique(armature_dst, bcoll_dst);
334 }
335
336 /* Double-check that the above Big Fat Assumption holds. */
337#ifndef NDEBUG
338 const int new_parent_bcoll_dst_index = armature_bonecoll_find_index(armature_dst,
339 parent_bcoll_dst);
340 BLI_assert_msg(new_parent_bcoll_dst_index == parent_bcoll_dst_index,
341 "did not expect parent_bcoll_dst_index to change");
342#endif
343
344 /* Recurse into the children to copy grandchildren. */
345 BLI_assert_msg(parent_bcoll_dst->child_count == parent_bcoll_src->child_count,
346 "all children should have been copied");
347 for (int child_num = 0; child_num < parent_bcoll_dst->child_count; child_num++) {
348 const int bcoll_src_index = parent_bcoll_src->child_index + child_num;
349 const int bcoll_dst_index = parent_bcoll_dst->child_index + child_num;
350
351 const BoneCollection *bcoll_src = armature_src->collection_array[bcoll_src_index];
352 liboverride_recursively_add_children(armature_dst, armature_src, bcoll_dst_index, bcoll_src);
353 }
354}
355
357 const bArmature *armature_src,
358 const BoneCollection *anchor_in_dst,
359 const BoneCollection *bcoll_to_copy)
360{
361#ifndef NDEBUG
362 /* Check that this bone collection is really a root, as this is assumed by the
363 * rest of this function. This is an O(n) check, though, so that's why it's
364 * only running in debug builds. */
365 const int bcoll_index_src = armature_bonecoll_find_index(armature_src, bcoll_to_copy);
366 if (!armature_bonecoll_is_root(armature_src, bcoll_index_src)) {
367 printf(
368 "Armature \"%s\" has library override operation that adds non-root bone collection "
369 "\"%s\". This is unexpected, please file a bug report.\n",
370 armature_src->id.name + 2,
371 bcoll_to_copy->name);
372 }
373#endif
374
375 BoneCollection *bcoll = copy_and_update_ownership(armature_dst, bcoll_to_copy);
376
377 const int anchor_index = armature_bonecoll_find_index(armature_dst, anchor_in_dst);
378 const int bcoll_index = anchor_index + 1;
380 bcoll_index <= armature_dst->collection_root_count,
381 "did not expect library override to add a child bone collection, only roots are expected");
382 bonecoll_insert_as_root(armature_dst, bcoll, bcoll_index);
383 bonecoll_ensure_name_unique(armature_dst, bcoll);
384
385 /* Library override operations are only constructed for the root bones. This means that handling
386 * this operation should also include copying the children. */
387 liboverride_recursively_add_children(armature_dst, armature_src, bcoll_index, bcoll_to_copy);
388
390 return bcoll;
391}
392
394{
395 armature->runtime.active_collection_index = -1;
396 armature->runtime.active_collection = nullptr;
397 armature->active_collection_name[0] = '\0';
398}
399
401{
402 if (bcoll == nullptr) {
404 return;
405 }
406
407 const int index = armature_bonecoll_find_index(armature, bcoll);
408 if (index == -1) {
409 /* TODO: print warning? Or just ignore this case? */
411 return;
412 }
413
414 STRNCPY(armature->active_collection_name, bcoll->name);
415 armature->runtime.active_collection_index = index;
416 armature->runtime.active_collection = bcoll;
417}
418
419void ANIM_armature_bonecoll_active_index_set(bArmature *armature, const int bone_collection_index)
420{
421 if (bone_collection_index < 0 || bone_collection_index >= armature->collection_array_num) {
423 return;
424 }
425
426 BoneCollection *bcoll = armature->collection_array[bone_collection_index];
427
428 STRNCPY(armature->active_collection_name, bcoll->name);
429 armature->runtime.active_collection_index = bone_collection_index;
430 armature->runtime.active_collection = bcoll;
431}
432
433void ANIM_armature_bonecoll_active_name_set(bArmature *armature, const char *name)
434{
435 BoneCollection *bcoll = ANIM_armature_bonecoll_get_by_name(armature, name);
436 ANIM_armature_bonecoll_active_set(armature, bcoll);
437}
438
440{
441 const std::string_view active_name = armature->active_collection_name;
442 if (active_name.empty()) {
444 return;
445 }
446
447 int index = 0;
448 for (BoneCollection *bcoll : armature->collections_span()) {
449 if (bcoll->name == active_name) {
450 armature->runtime.active_collection_index = index;
451 armature->runtime.active_collection = bcoll;
452 return;
453 }
454 index++;
455 }
456
457 /* No bone collection with the name was found, so better to clear everything. */
459}
460
462{
463 const bool is_override = ID_IS_OVERRIDE_LIBRARY(armature);
464 if (ID_IS_LINKED(armature) && !is_override) {
465 return false;
466 }
467
468 if (is_override && BKE_lib_override_library_is_system_defined(nullptr, &armature->id)) {
469 /* A system override is still not editable. */
470 return false;
471 }
472
473 if (is_override && (bcoll->flags & BONE_COLLECTION_OVERRIDE_LIBRARY_LOCAL) == 0) {
474 /* This particular collection was not added in the local override, so not editable. */
475 return false;
476 }
477 return true;
478}
479
481 const int from_index,
482 const int to_index)
483{
484 if (from_index >= armature->collection_array_num || to_index >= armature->collection_array_num ||
485 from_index == to_index)
486 {
487 return false;
488 }
489
490 /* Only allow moving within the same parent. This is written a bit awkwardly to avoid two calls
491 * to `armature_bonecoll_find_parent_index()` as that is O(n) in the number of bone collections.
492 */
493 const int parent_index = armature_bonecoll_find_parent_index(armature, from_index);
494 if (!armature_bonecoll_is_child_of(armature, parent_index, to_index)) {
495 return false;
496 }
497
498 if (parent_index < 0) {
499 /* Roots can just be moved around, as there is no `child_index` to update in this case. */
500 internal::bonecolls_move_to_index(armature, from_index, to_index);
501 return true;
502 }
503
504 /* Store the parent's child_index, as that might move if to_index is the first child
505 * (bonecolls_move_to_index() will keep it pointing at that first child). */
506 BoneCollection *parent_bcoll = armature->collection_array[parent_index];
507 const int old_parent_child_index = parent_bcoll->child_index;
508
509 internal::bonecolls_move_to_index(armature, from_index, to_index);
510
511 parent_bcoll->child_index = old_parent_child_index;
512
513 return true;
514}
515
516static int bonecoll_child_number(const bArmature *armature,
517 const int parent_bcoll_index,
518 const int bcoll_index)
519{
520 if (parent_bcoll_index < 0) {
521 /* Root bone collections are always at the start of the array, and thus their index is the
522 * 'child number'. */
523 return bcoll_index;
524 }
525
526 const BoneCollection *parent_bcoll = armature->collection_array[parent_bcoll_index];
527 return bcoll_index - parent_bcoll->child_index;
528}
529
531 const int from_index,
532 int to_index,
533 const MoveLocation before_after)
534{
535 const int from_parent_index = armature_bonecoll_find_parent_index(armature, from_index);
536 const int to_parent_index = armature_bonecoll_find_parent_index(armature, to_index);
537
538 if (from_parent_index != to_parent_index) {
539 /* Moving between parents. */
540 int to_child_num = bonecoll_child_number(armature, to_parent_index, to_index);
541 if (before_after == MoveLocation::After) {
542 to_child_num++;
543 }
544
546 armature, from_index, to_child_num, from_parent_index, to_parent_index);
547 }
548
549 /* Moving between siblings. */
550 switch (before_after) {
552 if (to_index > from_index) {
553 /* Moving to the right, but needs to go before that one, so needs a decrement. */
554 to_index--;
555 }
556 break;
557
559 if (to_index < from_index) {
560 /* Moving to the left, but needs to go after that one, so needs a decrement. */
561 to_index++;
562 }
563 break;
564 }
565
566 if (!ANIM_armature_bonecoll_move_to_index(armature, from_index, to_index)) {
567 return -1;
568 }
569 return to_index;
570}
571
572bool ANIM_armature_bonecoll_move(bArmature *armature, BoneCollection *bcoll, const int step)
573{
574 if (bcoll == nullptr) {
575 return false;
576 }
577
578 const int bcoll_index = armature_bonecoll_find_index(armature, bcoll);
579 const int to_index = bcoll_index + step;
580 if (bcoll_index < 0 || to_index < 0 || to_index >= armature->collection_array_num) {
581 return false;
582 }
583
584 ANIM_armature_bonecoll_move_to_index(armature, bcoll_index, to_index);
585
586 return true;
587}
588
589void ANIM_armature_bonecoll_name_set(bArmature *armature, BoneCollection *bcoll, const char *name)
590{
591 char old_name[sizeof(bcoll->name)];
592
593 STRNCPY(old_name, bcoll->name);
594
595 if (name[0] == '\0') {
596 /* Refuse to have nameless collections. The name of the active collection is stored in DNA, and
597 * an empty string means 'no active collection'. */
598 STRNCPY(bcoll->name, DATA_(bonecoll_default_name));
599 }
600 else {
601 STRNCPY_UTF8(bcoll->name, name);
602 }
603
604 bonecoll_ensure_name_unique(armature, bcoll);
605
606 /* Bone collections can be reached via .collections (4.0+) and .collections_all (4.1+).
607 * Animation data from 4.0 should have been versioned to only use `.collections_all`. */
608 BKE_animdata_fix_paths_rename_all(&armature->id, "collections", old_name, bcoll->name);
609 BKE_animdata_fix_paths_rename_all(&armature->id, "collections_all", old_name, bcoll->name);
610}
611
613{
614 BLI_assert(0 <= index && index < armature->collection_array_num);
615
616 BoneCollection *bcoll = armature->collection_array[index];
617
618 /* Get the active bone collection index before the armature is manipulated. */
619 const int active_collection_index = armature->runtime.active_collection_index;
620
621 /* The parent needs updating, so better to find it before this bone collection is removed. */
622 int parent_bcoll_index = armature_bonecoll_find_parent_index(armature, index);
623 BoneCollection *parent_bcoll = parent_bcoll_index >= 0 ?
624 armature->collection_array[parent_bcoll_index] :
625 nullptr;
626
627 /* Move all the children of the to-be-removed bone collection to their grandparent. */
628 int move_to_child_num = bonecoll_child_number(armature, parent_bcoll_index, index);
629 while (bcoll->child_count > 0) {
630 /* Move the child to its grandparent, at the same spot as the to-be-removed
631 * bone collection. The latter thus (potentially) shifts by 1 in the array.
632 * After removal, this effectively makes it appear like the removed bone
633 * collection is replaced by all its children. */
635 bcoll->child_index, /* Move from index... */
636 move_to_child_num, /* to this child number. */
637 index, /* From this parent... */
638 parent_bcoll_index /* to that parent. */
639 );
640
641 /* Both 'index' and 'parent_bcoll_index' can change each iteration. */
642 index = internal::bonecolls_find_index_near(armature, bcoll, index);
643 BLI_assert_msg(index >= 0, "could not find bone collection after moving things around");
644
645 if (parent_bcoll_index >= 0) { /* If there is no parent, its index should stay -1. */
646 parent_bcoll_index = internal::bonecolls_find_index_near(
647 armature, parent_bcoll, parent_bcoll_index);
648 BLI_assert_msg(parent_bcoll_index >= 0,
649 "could not find bone collection parent after moving things around");
650 }
651
652 move_to_child_num++;
653 }
654
655 /* Adjust the parent for the removal of its child. */
656 if (parent_bcoll_index < 0) {
657 /* Removing a root, so the armature itself needs to be updated. */
658 armature->collection_root_count--;
659 BLI_assert_msg(armature->collection_root_count >= 0, "armature root count cannot be negative");
660 }
661 else {
662 parent_bcoll->child_count--;
663 if (parent_bcoll->child_count == 0) {
664 parent_bcoll->child_index = 0;
665 }
666 }
667
668 /* Rotate the to-be-removed collection to the last array element. */
669 internal::bonecolls_move_to_index(armature, index, armature->collection_array_num - 1);
670
671 /* NOTE: we don't bother to shrink the allocation. It's okay if the
672 * capacity has extra space, because the number of valid items is tracked. */
673 armature->collection_array_num--;
674 armature->collection_array[armature->collection_array_num] = nullptr;
675
676 /* Update the active BoneCollection. */
677 if (active_collection_index >= 0) {
678 /* Default: select the next sibling.
679 * If there is none: select the previous sibling.
680 * If there is none: select the parent.
681 */
682 if (armature_bonecoll_is_child_of(armature, parent_bcoll_index, active_collection_index)) {
683 /* active_collection_index still points to a sibling of the removed collection. */
684 ANIM_armature_bonecoll_active_index_set(armature, active_collection_index);
685 }
686 else if (active_collection_index > 0 &&
688 armature, parent_bcoll_index, active_collection_index - 1))
689 {
690 /* The child preceding active_collection_index is a sibling of the removed collection. */
691 ANIM_armature_bonecoll_active_index_set(armature, active_collection_index - 1);
692 }
693 else {
694 /* Select the parent, or nothing if this was a root collection. In that case, if there are no
695 * siblings either, this just means all bone collections have been removed. */
696 ANIM_armature_bonecoll_active_index_set(armature, parent_bcoll_index);
697 }
698 }
699
700 const bool is_solo = bcoll->is_solo();
702 if (is_solo) {
703 /* This might have been the last solo'ed bone collection, so check whether
704 * solo'ing should still be active on the armature. */
706 }
707}
708
714
715template<typename MaybeConstBoneCollection>
716static MaybeConstBoneCollection *bonecolls_get_by_name(
717 blender::Span<MaybeConstBoneCollection *> bonecolls, const char *name)
718{
719 for (MaybeConstBoneCollection *bcoll : bonecolls) {
720 if (STREQ(bcoll->name, name)) {
721 return bcoll;
722 }
723 }
724 return nullptr;
725}
726
728{
729 return bonecolls_get_by_name(armature->collections_span(), name);
730}
731
733{
734 for (int index = 0; index < armature->collection_array_num; index++) {
735 const BoneCollection *bcoll = armature->collection_array[index];
736 if (STREQ(bcoll->name, name)) {
737 return index;
738 }
739 }
740 return -1;
741}
742
745{
746 for (BoneCollection *bcoll : armature->collection_children(parent_bcoll)) {
747 bcoll->flags &= ~BONE_COLLECTION_ANCESTORS_VISIBLE;
749 }
750}
751
754{
755 if (!parent_bcoll->is_visible_with_ancestors()) {
756 /* If this bone collection is not visible itself, or any of its ancestors are
757 * invisible, all descendants have an invisible ancestor. */
758 ancestors_visible_descendants_clear(armature, parent_bcoll);
759 return;
760 }
761
762 /* parent_bcoll is visible, and so are its ancestors. This means that all direct children have
763 * visible ancestors. The grandchildren depend on the children's visibility as well, hence the
764 * recursion. */
765 for (BoneCollection *bcoll : armature->collection_children(parent_bcoll)) {
766 bcoll->flags |= BONE_COLLECTION_ANCESTORS_VISIBLE;
768 }
769}
770
773 const BoneCollection *parent_bcoll,
774 BoneCollection *bcoll)
775{
776 if (parent_bcoll == nullptr || parent_bcoll->is_visible_with_ancestors()) {
778 }
779 else {
780 bcoll->flags &= ~BONE_COLLECTION_ANCESTORS_VISIBLE;
781 }
783}
784
786{
789}
790
792{
793 bcoll->flags &= ~BONE_COLLECTION_VISIBLE;
795}
796
798 BoneCollection *bcoll,
799 const bool is_visible)
800{
801 if (is_visible) {
802 ANIM_bonecoll_show(armature, bcoll);
803 }
804 else {
805 ANIM_bonecoll_hide(armature, bcoll);
806 }
807}
808
810 BoneCollection *bcoll,
811 const bool is_solo)
812{
813 if (is_solo) {
814 /* Enabling solo is simple. */
815 bcoll->flags |= BONE_COLLECTION_SOLO;
816 armature->flag |= ARM_BCOLL_SOLO_ACTIVE;
817 return;
818 }
819
820 /* Disabling is harder, as the armature flag can only be disabled when there
821 * are no more bone collections with the SOLO flag set. */
822 bcoll->flags &= ~BONE_COLLECTION_SOLO;
824}
825
827{
828 bool any_bcoll_solo = false;
829 for (const BoneCollection *bcoll : armature->collections_span()) {
830 if (bcoll->flags & BONE_COLLECTION_SOLO) {
831 any_bcoll_solo = true;
832 break;
833 }
834 }
835
836 if (any_bcoll_solo) {
837 armature->flag |= ARM_BCOLL_SOLO_ACTIVE;
838 }
839 else {
840 armature->flag &= ~ARM_BCOLL_SOLO_ACTIVE;
841 }
842}
843
845 const BoneCollection *bcoll)
846{
847 const bool is_solo_active = armature->flag & ARM_BCOLL_SOLO_ACTIVE;
848
849 if (is_solo_active) {
850 /* If soloing is active, nothing in the hierarchy matters except the solo flag. */
851 return bcoll->is_solo();
852 }
853
854 return bcoll->is_visible_with_ancestors();
855}
856
858{
859 if (is_expanded) {
861 }
862 else {
863 bcoll->flags &= ~BONE_COLLECTION_EXPANDED;
864 }
865}
866
867/* Store the bone's membership on the collection. */
868static void add_membership(BoneCollection *bcoll, Bone *bone)
869{
870 BoneCollectionMember *member = MEM_cnew<BoneCollectionMember>(__func__);
871 member->bone = bone;
872 BLI_addtail(&bcoll->bones, member);
873}
874/* Store reverse membership on the bone. */
875static void add_reference(Bone *bone, BoneCollection *bcoll)
876{
877 BoneCollectionReference *ref = MEM_cnew<BoneCollectionReference>(__func__);
878 ref->bcoll = bcoll;
879 BLI_addtail(&bone->runtime.collections, ref);
880}
881
883{
884 /* Precondition check: bail out if already a member. */
885 LISTBASE_FOREACH (BoneCollectionMember *, member, &bcoll->bones) {
886 if (member->bone == bone) {
887 return false;
888 }
889 }
890
891 add_membership(bcoll, bone);
892 add_reference(bone, bcoll);
893
894 return true;
895}
896
898{
899 /* Precondition check: bail out if already a member. */
901 if (ref->bcoll == bcoll) {
902 return false;
903 }
904 }
905
906 /* Store membership on the edit bone. Bones will be rebuilt when the armature
907 * goes out of edit mode, and by then the newly created bones will be added to
908 * the actual collection on the Armature. */
909 BoneCollectionReference *ref = MEM_cnew<BoneCollectionReference>(__func__);
910 ref->bcoll = bcoll;
911 BLI_addtail(&ebone->bone_collections, ref);
912
913 return true;
914}
915
921
927
929{
930 bool was_found = false;
931
932 /* Remove membership from collection. */
934 if (member->bone == bone) {
935 BLI_freelinkN(&bcoll->bones, member);
936 was_found = true;
937 break;
938 }
939 }
940
941 /* Remove reverse membership from the bone.
942 * For data consistency sake, this is always done, regardless of whether the
943 * above loop found the membership. */
945 if (ref->bcoll == bcoll) {
946 BLI_freelinkN(&bone->runtime.collections, ref);
947 break;
948 }
949 }
950
951 return was_found;
952}
953
955{
957 /* TODO: include Armature as parameter, and check that the bone collection to unassign from is
958 * actually editable. */
959 ANIM_armature_bonecoll_unassign(ref->bcoll, bone);
960 }
961}
962
969
971{
972 bool was_found = false;
973
974 /* Edit bone membership is only stored on the edit bone itself. */
976 if (ref->bcoll == bcoll) {
977 BLI_freelinkN(&ebone->bone_collections, ref);
978 was_found = true;
979 break;
980 }
981 }
982 return was_found;
983}
984
986{
987 /* Remove all the old collection memberships. */
988 for (BoneCollection *bcoll : armature->collections_span()) {
989 BLI_freelistN(&bcoll->bones);
990 }
991
992 /* For all bones, restore their collection memberships. */
993 ANIM_armature_foreach_bone(&armature->bonebase, [&](Bone *bone) {
994 LISTBASE_FOREACH (BoneCollectionReference *, ref, &bone->runtime.collections) {
995 add_membership(ref->bcoll, bone);
996 }
997 });
998}
999
1000static bool any_bone_collection_visible(const bArmature *armature,
1001 const ListBase /*BoneCollectionRef*/ *collection_refs)
1002{
1003 /* Special case: when a bone is not in any collection, it is visible. */
1004 if (BLI_listbase_is_empty(collection_refs)) {
1005 return true;
1006 }
1007
1008 LISTBASE_FOREACH (const BoneCollectionReference *, bcoll_ref, collection_refs) {
1009 const BoneCollection *bcoll = bcoll_ref->bcoll;
1010 if (ANIM_armature_bonecoll_is_visible_effectively(armature, bcoll)) {
1011 return true;
1012 }
1013 }
1014 return false;
1015}
1016
1017bool ANIM_bone_in_visible_collection(const bArmature *armature, const Bone *bone)
1018{
1019 return any_bone_collection_visible(armature, &bone->runtime.collections);
1020}
1021bool ANIM_bonecoll_is_visible_editbone(const bArmature *armature, const EditBone *ebone)
1022{
1023 return any_bone_collection_visible(armature, &ebone->bone_collections);
1024}
1025
1027{
1028 for (BoneCollection *bcoll : armature->collections_span()) {
1029 ANIM_bonecoll_show(armature, bcoll);
1030 }
1031}
1032
1034{
1035 for (BoneCollection *bcoll : armature->collections_span()) {
1036 ANIM_bonecoll_hide(armature, bcoll);
1037 }
1038}
1039
1040/* ********************************* */
1041/* Armature Layers transitional API. */
1042
1044{
1045 if (armature->runtime.active_collection == nullptr) {
1046 /* No active collection, do not assign to any. */
1047 return;
1048 }
1049
1051}
1052
1053static bool bcoll_list_contains(const ListBase /*BoneCollectionRef*/ *collection_refs,
1054 const BoneCollection *bcoll)
1055{
1056 LISTBASE_FOREACH (const BoneCollectionReference *, bcoll_ref, collection_refs) {
1057 if (bcoll == bcoll_ref->bcoll) {
1058 return true;
1059 }
1060 }
1061 return false;
1062}
1063
1065 const BoneCollection *bcoll)
1066{
1067 if (armature->edbo) {
1068 if (!armature->act_edbone) {
1069 return false;
1070 }
1071 return bcoll_list_contains(&armature->act_edbone->bone_collections, bcoll);
1072 }
1073
1074 if (!armature->act_bone) {
1075 return false;
1076 }
1077 return bcoll_list_contains(&armature->act_bone->runtime.collections, bcoll);
1078}
1079
1081{
1082 if (ANIM_bone_in_visible_collection(armature, bone)) {
1083 return;
1084 }
1085
1086 /* Making the first collection visible is enough to make the bone visible.
1087 *
1088 * Since bones without collection are considered visible,
1089 * bone->runtime.collections.first is certainly a valid pointer. */
1091 bone->runtime.collections.first);
1093}
1094
1096{
1097 if (ANIM_bonecoll_is_visible_editbone(armature, ebone)) {
1098 return;
1099 }
1100
1101 /* Making the first collection visible is enough to make the bone visible.
1102 *
1103 * Since bones without collection are considered visible,
1104 * ebone->bone_collections.first is certainly a valid pointer. */
1106 ebone->bone_collections.first);
1108}
1109
1111{
1113}
1114
1115/* ********* */
1116/* C++ only. */
1117namespace blender::animrig {
1118
1120{
1121 int index = 0;
1122 for (const BoneCollection *arm_bcoll : armature->collections_span()) {
1123 if (arm_bcoll == bcoll) {
1124 return index;
1125 }
1126 index++;
1127 }
1128
1129 return -1;
1130}
1131
1132int armature_bonecoll_find_parent_index(const bArmature *armature, const int bcoll_index)
1133{
1134 if (bcoll_index < armature->collection_root_count) {
1135 /* Don't bother iterating all collections when it's known to be a root. */
1136 return -1;
1137 }
1138
1139 int index = 0;
1140 for (const BoneCollection *potential_parent : armature->collections_span()) {
1141 if (potential_parent->child_index <= bcoll_index &&
1142 bcoll_index < potential_parent->child_index + potential_parent->child_count)
1143 {
1144 return index;
1145 }
1146
1147 index++;
1148 }
1149
1150 return -1;
1151}
1152
1153int armature_bonecoll_child_number_find(const bArmature *armature, const ::BoneCollection *bcoll)
1154{
1155 const int bcoll_index = armature_bonecoll_find_index(armature, bcoll);
1156 const int parent_index = armature_bonecoll_find_parent_index(armature, bcoll_index);
1157 return bonecoll_child_number(armature, parent_index, bcoll_index);
1158}
1159
1161 ::BoneCollection *bcoll,
1162 int new_child_number)
1163{
1164 const int bcoll_index = armature_bonecoll_find_index(armature, bcoll);
1165 const int parent_index = armature_bonecoll_find_parent_index(armature, bcoll_index);
1166
1167 BoneCollection fake_armature_parent = {};
1168 fake_armature_parent.child_count = armature->collection_root_count;
1169
1170 BoneCollection *parent_bcoll;
1171 if (parent_index < 0) {
1172 parent_bcoll = &fake_armature_parent;
1173 }
1174 else {
1175 parent_bcoll = armature->collection_array[parent_index];
1176 }
1177
1178 /* Bounds checks. */
1179 if (new_child_number >= parent_bcoll->child_count) {
1180 return -1;
1181 }
1182 if (new_child_number < 0) {
1183 new_child_number = parent_bcoll->child_count - 1;
1184 }
1185
1186 /* Store the parent's child_index, as that might move if to_index is the first child
1187 * (bonecolls_move_to_index() will keep it pointing at that first child). */
1188 const int old_parent_child_index = parent_bcoll->child_index;
1189 const int to_index = parent_bcoll->child_index + new_child_number;
1190 internal::bonecolls_move_to_index(armature, bcoll_index, to_index);
1191
1192 parent_bcoll->child_index = old_parent_child_index;
1193
1194 /* Make sure that if this was the active bone collection, its index also changes. */
1195 if (armature->runtime.active_collection_index == bcoll_index) {
1196 ANIM_armature_bonecoll_active_index_set(armature, to_index);
1197 }
1198
1199 return to_index;
1200}
1201
1202bool armature_bonecoll_is_root(const bArmature *armature, const int bcoll_index)
1203{
1204 BLI_assert(bcoll_index >= 0);
1205 return bcoll_index < armature->collection_root_count;
1206}
1207
1209 const int potential_parent_index,
1210 const int potential_child_index)
1211{
1212 /* Check for roots, before we try and access collection_array[-1]. */
1213 if (armature_bonecoll_is_root(armature, potential_child_index)) {
1214 return potential_parent_index == -1;
1215 }
1216 if (potential_parent_index < 0) {
1217 return false;
1218 }
1219
1220 const BoneCollection *potential_parent = armature->collection_array[potential_parent_index];
1221 const int upper_bound = potential_parent->child_index + potential_parent->child_count;
1222
1223 return potential_parent->child_index <= potential_child_index &&
1224 potential_child_index < upper_bound;
1225}
1226
1228 const int potential_parent_index,
1229 const int potential_descendant_index)
1230{
1231 BLI_assert_msg(potential_descendant_index >= 0,
1232 "Potential descendant has to exist for this function call to make sense.");
1233
1234 if (armature_bonecoll_is_child_of(armature, potential_parent_index, potential_descendant_index))
1235 {
1236 /* Found a direct child. */
1237 return true;
1238 }
1239
1240 const BoneCollection *potential_parent = armature->collection_array[potential_parent_index];
1241 const int upper_bound = potential_parent->child_index + potential_parent->child_count;
1242
1243 for (int visit_index = potential_parent->child_index; visit_index < upper_bound; visit_index++) {
1244 if (armature_bonecoll_is_descendant_of(armature, visit_index, potential_descendant_index)) {
1245 return true;
1246 }
1247 }
1248
1249 return false;
1250}
1251
1253{
1254 return bcoll->child_count > 0;
1255}
1256
1258 Span<const BoneCollection *> bcolls_source)
1259{
1260 /* Try to preserve the bone collection expanded/collapsed states. These are UI
1261 * changes that shouldn't impact undo steps. Care has to be taken to match the
1262 * old and the new bone collections, though, as they may have been reordered
1263 * or renamed.
1264 *
1265 * Reordering is handled by looking up collections by name.
1266 * Renames are handled by skipping those that cannot be found by name. */
1267
1268 auto find_old = [bcolls_source](const char *name, const int index) -> const BoneCollection * {
1269 /* Only check index when it's valid in the old armature. */
1270 if (index < bcolls_source.size()) {
1271 const BoneCollection *bcoll = bcolls_source[index];
1272 if (STREQ(bcoll->name, name)) {
1273 /* Index and name matches, let's use */
1274 return bcoll;
1275 }
1276 }
1277
1278 /* Try to find by name as a last resort. This function only works with
1279 * non-const pointers, hence the const_cast. */
1280 const BoneCollection *bcoll = bonecolls_get_by_name(bcolls_source, name);
1281 return bcoll;
1282 };
1283
1284 for (int i = 0; i < bcolls_dest.size(); i++) {
1285 BoneCollection *bcoll_new = bcolls_dest[i];
1286
1287 const BoneCollection *bcoll_old = find_old(bcoll_new->name, i);
1288 if (!bcoll_old) {
1289 continue;
1290 }
1291
1292 ANIM_armature_bonecoll_is_expanded_set(bcoll_new, bcoll_old->is_expanded());
1293 }
1294}
1295
1297 const int from_bcoll_index,
1298 int to_child_num,
1299 const int from_parent_index,
1300 const int to_parent_index)
1301{
1302 BLI_assert(0 <= from_bcoll_index && from_bcoll_index < armature->collection_array_num);
1303 BLI_assert(-1 <= from_parent_index && from_parent_index < armature->collection_array_num);
1304 BLI_assert(-1 <= to_parent_index && to_parent_index < armature->collection_array_num);
1305
1306 if (from_parent_index == to_parent_index) {
1307 /* TODO: use `to_child_num` to still move the child to the desired position. */
1308 return from_bcoll_index;
1309 }
1310
1311 /* The Armature itself acts like some sort of 'parent' for the root collections. By having this
1312 * as a 'fake' BoneCollection, all the code below can just be blissfully unaware of the special
1313 * 'all root collections should be at the start of the array' rule. */
1314 BoneCollection armature_root;
1315 armature_root.child_count = armature->collection_root_count;
1316 armature_root.child_index = 0;
1317 armature_root.flags = default_flags;
1318
1319 BoneCollection *from_parent = from_parent_index >= 0 ?
1320 armature->collection_array[from_parent_index] :
1321 &armature_root;
1322 BoneCollection *to_parent = to_parent_index >= 0 ? armature->collection_array[to_parent_index] :
1323 &armature_root;
1324
1325 BLI_assert_msg(-1 <= to_child_num && to_child_num <= to_parent->child_count,
1326 "to_child_num must point to an index of a child of the new parent, or the index "
1327 "of the last child + 1, or be -1 to indicate 'after last child'");
1328 if (to_child_num < 0) {
1329 to_child_num = to_parent->child_count;
1330 }
1331
1332 /* The new parent might not have children yet. */
1333 int to_bcoll_index;
1334 if (to_parent->child_count == 0) {
1335 /* New parents always get their children at the end of the array. */
1336 to_bcoll_index = armature->collection_array_num - 1;
1337 }
1338 else {
1339 to_bcoll_index = to_parent->child_index + to_child_num;
1340
1341 /* Check whether the new parent's children are to the left or right of bcoll_index.
1342 * This determines which direction the collections have to shift, and thus which index to
1343 * move the bcoll to. */
1344 if (to_bcoll_index > from_bcoll_index) {
1345 to_bcoll_index--;
1346 }
1347 }
1348
1349 /* In certain cases the 'from_parent' gets its first child removed, and needs to have its
1350 * child_index incremented. This needs to be done by comparing these fields before the actual
1351 * move happens (as that could also change the child_index). */
1352 const bool needs_post_move_child_index_bump = from_parent->child_index == from_bcoll_index &&
1353 to_bcoll_index <= from_bcoll_index;
1354 /* bonecolls_move_to_index() will try and keep the hierarchy correct, and thus change
1355 * to_parent->child_index to keep pointing to its current-first child. */
1356 const bool becomes_new_first_child = to_child_num == 0 || to_parent->child_count == 0;
1357 internal::bonecolls_move_to_index(armature, from_bcoll_index, to_bcoll_index);
1358
1359 /* Update child index & count of the old parent. */
1360 from_parent->child_count--;
1361 if (from_parent->child_count == 0) {
1362 /* Clean up the child index when the parent has no more children. */
1363 from_parent->child_index = 0;
1364 }
1365 else if (needs_post_move_child_index_bump) {
1366 /* The start of the block of children of the old parent has moved, because
1367 * we took out the first child. This only needs to be compensated for when
1368 * moving it to the left (or staying put), as then its old siblings stay in
1369 * place.
1370 *
1371 * This only needs to be done if there are any children left, though. */
1372 from_parent->child_index++;
1373 }
1374
1375 /* Update child index & count of the new parent. */
1376 if (becomes_new_first_child) {
1377 to_parent->child_index = to_bcoll_index;
1378 }
1379 to_parent->child_count++;
1380
1381 /* Copy the information from the 'fake' BoneCollection back to the armature. */
1382 armature->collection_root_count = armature_root.child_count;
1383 BLI_assert(armature_root.child_index == 0);
1384
1385 /* Since the parent changed, the effective visibility might change too. */
1386 ancestors_visible_update(armature, to_parent, armature->collection_array[to_bcoll_index]);
1387
1388 return to_bcoll_index;
1389}
1390
1391/* Utility functions for Armature edit-mode undo. */
1392
1394 BoneCollection ***bcoll_array_dst,
1395 int *bcoll_array_dst_num,
1396 BoneCollection **bcoll_array_src,
1397 const int bcoll_array_src_num,
1398 const bool do_id_user)
1399{
1400 BLI_assert(*bcoll_array_dst == nullptr);
1401 BLI_assert(*bcoll_array_dst_num == 0);
1402
1403 *bcoll_array_dst = static_cast<BoneCollection **>(
1404 MEM_malloc_arrayN(bcoll_array_src_num, sizeof(BoneCollection *), __func__));
1405 *bcoll_array_dst_num = bcoll_array_src_num;
1406
1408 for (int i = 0; i < bcoll_array_src_num; i++) {
1409 BoneCollection *bcoll_src = bcoll_array_src[i];
1410 BoneCollection *bcoll_dst = static_cast<BoneCollection *>(MEM_dupallocN(bcoll_src));
1411
1412 /* This will be rebuilt from the edit bones, so we don't need to copy it. */
1413 BLI_listbase_clear(&bcoll_dst->bones);
1414
1415 if (bcoll_src->prop) {
1416 bcoll_dst->prop = IDP_CopyProperty_ex(bcoll_src->prop,
1417 do_id_user ? 0 : LIB_ID_CREATE_NO_USER_REFCOUNT);
1418 }
1419
1420 (*bcoll_array_dst)[i] = bcoll_dst;
1421
1422 bcoll_map.add(bcoll_src, bcoll_dst);
1423 }
1424
1425 return bcoll_map;
1426}
1427
1429 int *bcoll_array_num,
1430 const bool do_id_user)
1431{
1432 for (int i = 0; i < *bcoll_array_num; i++) {
1433 BoneCollection *bcoll = (*bcoll_array)[i];
1434
1435 if (bcoll->prop) {
1436 IDP_FreeProperty_ex(bcoll->prop, do_id_user);
1437 }
1438
1439 /* This will usually already be empty, because the passed BoneCollection
1440 * list is usually from ANIM_bonecoll_listbase_copy_no_membership().
1441 * However, during undo this is also used to free the BoneCollection
1442 * list on the Armature itself before copying over the undo BoneCollection
1443 * list, in which case this of Bone pointers may not be empty. */
1444 BLI_freelistN(&bcoll->bones);
1445
1446 MEM_freeN(bcoll);
1447 }
1448 MEM_SAFE_FREE(*bcoll_array);
1449
1450 *bcoll_array_num = 0;
1451}
1452
1454namespace internal {
1455
1457 const int start_index,
1458 const int count,
1459 const int direction)
1460{
1461 BLI_assert_msg(direction == 1 || direction == -1, "`direction` must be either -1 or +1");
1462
1463 if (count == 0) {
1464 return;
1465 }
1466
1467 /* When the block [start_index:start_index+count] is moved, it causes a duplication of one
1468 * element and overwrites another element. For example: given an array [0, 1, 2, 3, 4], moving
1469 * indices [1, 2] by +1 would result in one double element (1) and one missing element (3): [0,
1470 * 1, 1, 2, 4].
1471 *
1472 * This is resolved by moving that element to the other side of the block, so the result will be
1473 * [0, 3, 1, 2, 4]. This breaks the hierarchical information, so it's up to the caller to update
1474 * this one moved element.
1475 */
1476
1477 const int move_from_index = (direction > 0 ? start_index + count : start_index - 1);
1478 const int move_to_index = (direction > 0 ? start_index : start_index + count - 1);
1479 BoneCollection *bcoll_to_move = armature->collection_array[move_from_index];
1480
1481 BoneCollection **start = armature->collection_array + start_index;
1482 memmove((void *)(start + direction), (void *)start, count * sizeof(BoneCollection *));
1483
1484 armature->collection_array[move_to_index] = bcoll_to_move;
1485
1486 /* Update all child indices that reference something in the moved block. */
1487 for (BoneCollection *bcoll : armature->collections_span()) {
1488 /* Having both child_index and child_count zeroed out just means "no children"; these shouldn't
1489 * be updated at all, as here child_index is not really referencing the element at index 0. */
1490 if (bcoll->child_index == 0 && bcoll->child_count == 0) {
1491 continue;
1492 }
1493
1494 /* Compare to the original start & end of the block (i.e. pre-move). If a
1495 * child_index is within this range, it'll need updating. */
1496 if (start_index <= bcoll->child_index && bcoll->child_index < start_index + count) {
1497 bcoll->child_index += direction;
1498 }
1499 }
1500
1501 /* Make sure the active bone collection index is moved as well. */
1502 const int active_index = armature->runtime.active_collection_index;
1503 if (active_index == move_from_index) {
1504 armature->runtime.active_collection_index = move_to_index;
1505 }
1506 else if (start_index <= active_index && active_index < start_index + count) {
1507 armature->runtime.active_collection_index += direction;
1508 }
1509}
1510
1511void bonecolls_move_to_index(bArmature *armature, const int from_index, const int to_index)
1512{
1513 if (from_index == to_index) {
1514 return;
1515 }
1516
1517 BLI_assert(0 <= from_index);
1518 BLI_assert(from_index < armature->collection_array_num);
1519 BLI_assert(0 <= to_index);
1520 BLI_assert(to_index < armature->collection_array_num);
1521
1522 if (from_index < to_index) {
1523 const int block_start_index = from_index + 1;
1524 const int block_count = to_index - from_index;
1525 bonecolls_rotate_block(armature, block_start_index, block_count, -1);
1526 }
1527 else {
1528 const int block_start_index = to_index;
1529 const int block_count = from_index - to_index;
1530 bonecolls_rotate_block(armature, block_start_index, block_count, +1);
1531 }
1532}
1533
1534int bonecolls_find_index_near(bArmature *armature, BoneCollection *bcoll, const int index)
1535{
1536 BoneCollection **collections = armature->collection_array;
1537
1538 if (collections[index] == bcoll) {
1539 return index;
1540 }
1541 if (index > 0 && collections[index - 1] == bcoll) {
1542 return index - 1;
1543 }
1544 if (index < armature->collection_array_num - 1 && collections[index + 1] == bcoll) {
1545 return index + 1;
1546 }
1547 return -1;
1548}
1549
1550void bonecolls_debug_list(const bArmature *armature)
1551{
1552 printf("\033[38;5;214mBone collections of armature \"%s\":\033[0m\n", armature->id.name + 2);
1553 constexpr int root_ansi_color = 95;
1554 printf(
1555 " - \033[%dmroot\033[0m count: %d\n", root_ansi_color, armature->collection_root_count);
1556 for (int i = 0; i < armature->collection_array_num; ++i) {
1557 const BoneCollection *bcoll = armature->collection_array[i];
1558 printf(" - \033[%dmcolls[%d] = %24s\033[0m ",
1559 i < armature->collection_root_count ? root_ansi_color : 0,
1560 i,
1561 bcoll->name);
1562 if (bcoll->child_index == 0 && bcoll->child_count == 0) {
1563 printf("(leaf)");
1564 }
1565 else {
1566 printf("(child index: %d, count: %d)", bcoll->child_index, bcoll->child_count);
1567 }
1568 printf("\n");
1569 }
1570}
1571
1573{
1574 /* Remove bone membership. */
1576 ANIM_armature_bonecoll_unassign(bcoll, member->bone);
1577 }
1578 if (armature->edbo) {
1579 LISTBASE_FOREACH (EditBone *, ebone, armature->edbo) {
1581 }
1582 }
1583
1584 ANIM_bonecoll_free(bcoll);
1585}
1586
1587} // namespace internal
1588
1589} // namespace blender::animrig
Iterators for armatures.
C++ functions to deal with Armature collections (i.e. the successor of bone layers).
void ANIM_armature_bonecoll_is_expanded_set(BoneCollection *bcoll, bool is_expanded)
bool ANIM_armature_bonecoll_unassign_editbone(BoneCollection *bcoll, EditBone *ebone)
void ANIM_bonecoll_free(BoneCollection *bcoll, bool do_id_user_count=true)
bool ANIM_armature_bonecoll_unassign(BoneCollection *bcoll, Bone *bone)
void ANIM_armature_bonecoll_active_index_set(bArmature *armature, int bone_collection_index)
void BKE_animdata_fix_paths_rename_all(struct ID *ref_id, const char *prefix, const char *oldName, const char *newName)
Bone * BKE_armature_find_bone_name(bArmature *arm, const char *name)
Definition armature.cc:779
IDProperty * IDP_CopyProperty_ex(const IDProperty *prop, int flag) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
Definition idprop.cc:843
void IDP_FreeProperty_ex(IDProperty *prop, bool do_id_user)
Definition idprop.cc:1221
@ LIB_ID_CREATE_NO_USER_REFCOUNT
bool BKE_lib_override_library_is_system_defined(const Main *bmain, const ID *id)
#define BLI_assert(a)
Definition BLI_assert.h:50
#define BLI_assert_msg(a, msg)
Definition BLI_assert.h:57
BLI_INLINE bool BLI_listbase_is_empty(const struct ListBase *lb)
#define LISTBASE_FOREACH(type, var, list)
void BLI_freelinkN(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:269
#define LISTBASE_FOREACH_MUTABLE(type, var, list)
void void void void void void BLI_duplicatelist(struct ListBase *dst, const struct ListBase *src) ATTR_NONNULL(1
BLI_INLINE void BLI_listbase_clear(struct ListBase *lb)
void void BLI_freelistN(struct ListBase *listbase) ATTR_NONNULL(1)
Definition listbase.cc:496
void BLI_addtail(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:110
#define STRNCPY(dst, src)
Definition BLI_string.h:593
#define STRNCPY_UTF8(dst, src)
size_t void BLI_uniquename_cb(UniquenameCheckCallback unique_check, void *arg, const char *defname, char delim, char *name, size_t name_maxncpy) ATTR_NONNULL(1
#define STREQ(a, b)
#define DATA_(msgid)
#define ID_IS_LINKED(_id)
Definition DNA_ID.h:654
#define ID_IS_OVERRIDE_LIBRARY(_id)
Definition DNA_ID.h:683
eBoneCollection_Flag
@ BONE_COLLECTION_VISIBLE
@ BONE_COLLECTION_SELECTABLE
@ BONE_COLLECTION_ANCESTORS_VISIBLE
@ BONE_COLLECTION_SOLO
@ BONE_COLLECTION_EXPANDED
@ BONE_COLLECTION_OVERRIDE_LIBRARY_LOCAL
@ ARM_BCOLL_SOLO_ACTIVE
Read Guarded memory(de)allocation.
#define MEM_SAFE_FREE(v)
void ANIM_armature_bonecoll_unassign_all_editbone(EditBone *ebone)
void ANIM_armature_refresh_solo_active(bArmature *armature)
static void add_membership(BoneCollection *bcoll, Bone *bone)
void ANIM_armature_runtime_free(bArmature *armature)
void ANIM_armature_bonecoll_remove_from_index(bArmature *armature, int index)
bool ANIM_armature_bonecoll_assign_and_move(BoneCollection *bcoll, Bone *bone)
void ANIM_armature_bonecoll_active_set(bArmature *armature, BoneCollection *bcoll)
static void bonecoll_insert_at_index(bArmature *armature, BoneCollection *bcoll, const int index)
static void bonecoll_insert_as_root(bArmature *armature, BoneCollection *bcoll, int at_index)
void ANIM_armature_bonecoll_is_visible_set(bArmature *armature, BoneCollection *bcoll, const bool is_visible)
void ANIM_armature_bonecoll_hide_all(bArmature *armature)
bool ANIM_armature_bonecoll_is_editable(const bArmature *armature, const BoneCollection *bcoll)
static void ancestors_visible_update(bArmature *armature, const BoneCollection *parent_bcoll, BoneCollection *bcoll)
BoneCollection * ANIM_armature_bonecoll_insert_copy_after(bArmature *armature_dst, const bArmature *armature_src, const BoneCollection *anchor_in_dst, const BoneCollection *bcoll_to_copy)
bool ANIM_armature_bonecoll_assign_and_move_editbone(BoneCollection *bcoll, EditBone *ebone)
void ANIM_armature_bonecoll_is_expanded_set(BoneCollection *bcoll, bool is_expanded)
void ANIM_armature_bonecoll_active_index_set(bArmature *armature, const int bone_collection_index)
void ANIM_bonecoll_hide(bArmature *armature, BoneCollection *bcoll)
bool ANIM_armature_bonecoll_move(bArmature *armature, BoneCollection *bcoll, const int step)
static bool any_bone_collection_visible(const bArmature *armature, const ListBase *collection_refs)
bool ANIM_armature_bonecoll_move_to_index(bArmature *armature, const int from_index, const int to_index)
void ANIM_armature_runtime_refresh(bArmature *armature)
void ANIM_armature_bonecoll_active_name_set(bArmature *armature, const char *name)
void ANIM_armature_bonecoll_show_from_ebone(bArmature *armature, const EditBone *ebone)
static MaybeConstBoneCollection * bonecolls_get_by_name(blender::Span< MaybeConstBoneCollection * > bonecolls, const char *name)
void ANIM_armature_bonecoll_show_from_bone(bArmature *armature, const Bone *bone)
static void ancestors_visible_descendants_clear(bArmature *armature, BoneCollection *parent_bcoll)
BoneCollection * ANIM_armature_bonecoll_get_by_name(bArmature *armature, const char *name)
static int bonecoll_insert_as_child(bArmature *armature, BoneCollection *bcoll, const int parent_index)
bool ANIM_bonecoll_is_visible_editbone(const bArmature *armature, const EditBone *ebone)
static void liboverride_recursively_add_children(bArmature *armature_dst, const bArmature *armature_src, const int parent_bcoll_dst_index, const BoneCollection *parent_bcoll_src)
int ANIM_armature_bonecoll_move_before_after_index(bArmature *armature, const int from_index, int to_index, const MoveLocation before_after)
bool ANIM_armature_bonecoll_unassign_editbone(BoneCollection *bcoll, EditBone *ebone)
static void bonecoll_ensure_name_unique(bArmature *armature, BoneCollection *bcoll)
void ANIM_armature_bonecoll_assign_active(const bArmature *armature, EditBone *ebone)
bool ANIM_armature_bonecoll_assign(BoneCollection *bcoll, Bone *bone)
void ANIM_armature_bonecoll_remove(bArmature *armature, BoneCollection *bcoll)
bool ANIM_armature_bonecoll_unassign(BoneCollection *bcoll, Bone *bone)
void ANIM_armature_bonecoll_unassign_all(Bone *bone)
void ANIM_bonecoll_show(bArmature *armature, BoneCollection *bcoll)
void ANIM_armature_bonecoll_show_from_pchan(bArmature *armature, const bPoseChannel *pchan)
void ANIM_armature_bonecoll_active_runtime_refresh(bArmature *armature)
static int bonecoll_child_number(const bArmature *armature, const int parent_bcoll_index, const int bcoll_index)
static void ancestors_visible_descendants_update(bArmature *armature, BoneCollection *parent_bcoll)
static BoneCollection * copy_and_update_ownership(const bArmature *armature_dst, const BoneCollection *bcoll_to_copy)
BoneCollection * ANIM_bonecoll_new(const char *name)
bool ANIM_armature_bonecoll_is_visible_effectively(const bArmature *armature, const BoneCollection *bcoll)
void ANIM_armature_bonecoll_name_set(bArmature *armature, BoneCollection *bcoll, const char *name)
static void add_reference(Bone *bone, BoneCollection *bcoll)
static bool bcoll_list_contains(const ListBase *collection_refs, const BoneCollection *bcoll)
void ANIM_armature_bonecoll_show_all(bArmature *armature)
void ANIM_armature_bonecoll_solo_set(bArmature *armature, BoneCollection *bcoll, const bool is_solo)
void ANIM_bonecoll_free(BoneCollection *bcoll, const bool do_id_user_count)
void ANIM_armature_bonecoll_reconstruct(bArmature *armature)
bool ANIM_armature_bonecoll_contains_active_bone(const bArmature *armature, const BoneCollection *bcoll)
bool ANIM_bone_in_visible_collection(const bArmature *armature, const Bone *bone)
int ANIM_armature_bonecoll_get_index_by_name(bArmature *armature, const char *name)
static void add_reverse_pointers(BoneCollection *bcoll)
BoneCollection * ANIM_armature_bonecoll_new(bArmature *armature, const char *name, const int parent_index)
static void armature_bonecoll_active_clear(bArmature *armature)
bool ANIM_armature_bonecoll_assign_editbone(BoneCollection *bcoll, EditBone *ebone)
Internal C++ functions to deal with bone collections. These are mostly here for internal use in bone_...
constexpr int64_t size() const
Definition BLI_span.hh:253
#define printf
int count
void *(* MEM_reallocN_id)(void *vmemh, size_t len, const char *str)
Definition mallocn.cc:40
void *(* MEM_malloc_arrayN)(size_t len, size_t size, const char *str)
Definition mallocn.cc:45
void MEM_freeN(void *vmemh)
Definition mallocn.cc:105
void *(* MEM_dupallocN)(const void *vmemh)
Definition mallocn.cc:39
void bonecolls_rotate_block(bArmature *armature, const int start_index, const int count, const int direction)
void bonecolls_debug_list(const bArmature *armature)
void bonecoll_unassign_and_free(bArmature *armature, BoneCollection *bcoll)
int bonecolls_find_index_near(bArmature *armature, BoneCollection *bcoll, const int index)
void bonecolls_move_to_index(bArmature *armature, const int from_index, const int to_index)
blender::Map< BoneCollection *, BoneCollection * > ANIM_bonecoll_array_copy_no_membership(BoneCollection ***bcoll_array_dst, int *bcoll_array_dst_num, BoneCollection **bcoll_array_src, int bcoll_array_src_num, bool do_id_user)
bool armature_bonecoll_is_root(const bArmature *armature, int bcoll_index)
static void ANIM_armature_foreach_bone(ListBase *bones, CB callback)
void bonecolls_copy_expanded_flag(Span< BoneCollection * > bcolls_dest, Span< const BoneCollection * > bcolls_source)
int armature_bonecoll_child_number_find(const bArmature *armature, const ::BoneCollection *bcoll)
bool bonecoll_has_children(const BoneCollection *bcoll)
int armature_bonecoll_find_index(const bArmature *armature, const ::BoneCollection *bcoll)
bool armature_bonecoll_is_descendant_of(const bArmature *armature, int potential_parent_index, int potential_descendant_index)
int armature_bonecoll_find_parent_index(const bArmature *armature, int bcoll_index)
int armature_bonecoll_child_number_set(bArmature *armature, ::BoneCollection *bcoll, int new_child_number)
void ANIM_bonecoll_array_free(BoneCollection ***bcoll_array, int *bcoll_array_num, bool do_id_user)
int armature_bonecoll_move_to_parent(bArmature *armature, int from_bcoll_index, int to_child_num, int from_parent_index, int to_parent_index)
bool armature_bonecoll_is_child_of(const bArmature *armature, int potential_parent_index, int potential_child_index)
struct BoneCollection * bcoll
struct IDProperty * prop
Bone_Runtime runtime
ListBase bone_collections
char name[66]
Definition DNA_ID.h:425
void * first
struct BoneCollection * active_collection
struct BoneCollection ** collection_array
char active_collection_name[64]
struct GHash * bonehash
struct EditBone * act_edbone
ListBase * edbo
struct bArmature_Runtime runtime
struct Bone * bone