Compare commits
15 Commits
temp-attri
...
outliner-c
Author | SHA1 | Date | |
---|---|---|---|
ca7079a44e | |||
fdd9cb713e | |||
8c4f121492 | |||
c796364f05 | |||
4a572f06e9 | |||
fa91f90287 | |||
2d60f64960 | |||
fc6338d863 | |||
d962ca7387 | |||
e1a361d287 | |||
8b787089b3 | |||
126b6f8cc7 | |||
86fe1ec968 | |||
6f87489536 | |||
cf94571762 |
@@ -1640,6 +1640,7 @@ static void direct_link_area(BlendDataReader *reader, ScrArea *area)
|
||||
}
|
||||
space_outliner->treehash = NULL;
|
||||
space_outliner->tree.first = space_outliner->tree.last = NULL;
|
||||
space_outliner->runtime = NULL;
|
||||
}
|
||||
else if (sl->spacetype == SPACE_IMAGE) {
|
||||
SpaceImage *sima = (SpaceImage *)sl;
|
||||
|
@@ -56,7 +56,8 @@ template<typename T> class ListBaseWrapper {
|
||||
|
||||
Iterator &operator++()
|
||||
{
|
||||
current_ = current_->next;
|
||||
/* Some types store next/prev using `void *`, so cast is necessary. */
|
||||
current_ = static_cast<T *>(current_->next);
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
@@ -44,8 +44,14 @@ set(SRC
|
||||
outliner_tree.c
|
||||
outliner_utils.c
|
||||
space_outliner.c
|
||||
tree/common.cc
|
||||
tree/tree_display.cc
|
||||
tree/tree_display_libraries.cc
|
||||
tree/tree_display_view_layer.cc
|
||||
|
||||
outliner_intern.h
|
||||
tree/tree_display.h
|
||||
tree/tree_display.hh
|
||||
)
|
||||
|
||||
set(LIB
|
||||
|
@@ -25,6 +25,10 @@
|
||||
|
||||
#include "RNA_types.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* internal exports only */
|
||||
|
||||
struct ARegion;
|
||||
@@ -42,6 +46,13 @@ struct bPoseChannel;
|
||||
struct wmKeyConfig;
|
||||
struct wmOperatorType;
|
||||
|
||||
typedef struct SpaceOutliner_Runtime {
|
||||
/**
|
||||
* Internal C++ object to create and manage the tree for a specific display type (View Layers,
|
||||
* Scenes, Blender File, etc.). */
|
||||
struct TreeDisplay *tree_display;
|
||||
} SpaceOutliner_Runtime;
|
||||
|
||||
typedef enum TreeElementInsertType {
|
||||
TE_INSERT_BEFORE,
|
||||
TE_INSERT_AFTER,
|
||||
@@ -534,3 +545,7 @@ void outliner_tag_redraw_avoid_rebuild_on_open_change(const struct SpaceOutliner
|
||||
/* outliner_sync.c ---------------------------------------------- */
|
||||
|
||||
void outliner_sync_selection(const struct bContext *C, struct SpaceOutliner *space_outliner);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
@@ -85,6 +85,7 @@
|
||||
#include "UI_interface.h"
|
||||
|
||||
#include "outliner_intern.h"
|
||||
#include "tree/tree_display.h"
|
||||
|
||||
#ifdef WIN32
|
||||
# include "BLI_math_base.h" /* M_PI */
|
||||
@@ -94,7 +95,6 @@
|
||||
static TreeElement *outliner_add_collection_recursive(SpaceOutliner *space_outliner,
|
||||
Collection *collection,
|
||||
TreeElement *ten);
|
||||
static void outliner_make_object_parent_hierarchy(ListBase *lb);
|
||||
static int outliner_exclude_filter_get(const SpaceOutliner *space_outliner);
|
||||
|
||||
/* ********************************************************* */
|
||||
@@ -237,14 +237,6 @@ void outliner_free_tree_element(TreeElement *element, ListBase *parent_subtree)
|
||||
|
||||
/* ********************************************************* */
|
||||
|
||||
/* Prototype, see functions below */
|
||||
static TreeElement *outliner_add_element(SpaceOutliner *space_outliner,
|
||||
ListBase *lb,
|
||||
void *idv,
|
||||
TreeElement *parent,
|
||||
short type,
|
||||
short index);
|
||||
|
||||
/* -------------------------------------------------------- */
|
||||
|
||||
bool outliner_requires_rebuild_on_select_or_active_change(const SpaceOutliner *space_outliner)
|
||||
@@ -920,12 +912,12 @@ static void outliner_add_id_contents(SpaceOutliner *space_outliner,
|
||||
* \note: If child items are only added to the tree if the item is open, the TSE_ type _must_ be
|
||||
* added to #outliner_element_needs_rebuild_on_open_change().
|
||||
*/
|
||||
static TreeElement *outliner_add_element(SpaceOutliner *space_outliner,
|
||||
ListBase *lb,
|
||||
void *idv,
|
||||
TreeElement *parent,
|
||||
short type,
|
||||
short index)
|
||||
TreeElement *outliner_add_element(SpaceOutliner *space_outliner,
|
||||
ListBase *lb,
|
||||
void *idv,
|
||||
TreeElement *parent,
|
||||
short type,
|
||||
short index)
|
||||
{
|
||||
TreeElement *te;
|
||||
TreeStoreElem *tselem;
|
||||
@@ -1390,110 +1382,6 @@ static void outliner_add_seq_dup(SpaceOutliner *space_outliner,
|
||||
|
||||
/* ----------------------------------------------- */
|
||||
|
||||
static const char *outliner_idcode_to_plural(short idcode)
|
||||
{
|
||||
const char *propname = BKE_idtype_idcode_to_name_plural(idcode);
|
||||
PropertyRNA *prop = RNA_struct_type_find_property(&RNA_BlendData, propname);
|
||||
return (prop) ? RNA_property_ui_name(prop) : "UNKNOWN";
|
||||
}
|
||||
|
||||
static bool outliner_library_id_show(Library *lib, ID *id, short filter_id_type)
|
||||
{
|
||||
if (id->lib != lib) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (filter_id_type == ID_GR) {
|
||||
/* Don't show child collections of non-scene master collection,
|
||||
* they are already shown as children. */
|
||||
Collection *collection = (Collection *)id;
|
||||
bool has_non_scene_parent = false;
|
||||
|
||||
LISTBASE_FOREACH (CollectionParent *, cparent, &collection->parents) {
|
||||
if (!(cparent->collection->flag & COLLECTION_IS_MASTER)) {
|
||||
has_non_scene_parent = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (has_non_scene_parent) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static TreeElement *outliner_add_library_contents(Main *mainvar,
|
||||
SpaceOutliner *space_outliner,
|
||||
ListBase *lb,
|
||||
Library *lib)
|
||||
{
|
||||
TreeElement *ten, *tenlib = NULL;
|
||||
ListBase *lbarray[MAX_LIBARRAY];
|
||||
int a, tot;
|
||||
short filter_id_type = (space_outliner->filter & SO_FILTER_ID_TYPE) ?
|
||||
space_outliner->filter_id_type :
|
||||
0;
|
||||
|
||||
if (filter_id_type) {
|
||||
lbarray[0] = which_libbase(mainvar, space_outliner->filter_id_type);
|
||||
tot = 1;
|
||||
}
|
||||
else {
|
||||
tot = set_listbasepointers(mainvar, lbarray);
|
||||
}
|
||||
|
||||
for (a = 0; a < tot; a++) {
|
||||
if (lbarray[a] && lbarray[a]->first) {
|
||||
ID *id = lbarray[a]->first;
|
||||
const bool is_library = (GS(id->name) == ID_LI) && (lib != NULL);
|
||||
|
||||
/* check if there's data in current lib */
|
||||
for (; id; id = id->next) {
|
||||
if (id->lib == lib) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* We always want to create an entry for libraries, even if/when we have no more IDs from
|
||||
* them. This invalid state is important to show to user as well.*/
|
||||
if (id != NULL || is_library) {
|
||||
if (!tenlib) {
|
||||
/* Create library tree element on demand, depending if there are any data-blocks. */
|
||||
if (lib) {
|
||||
tenlib = outliner_add_element(space_outliner, lb, lib, NULL, 0, 0);
|
||||
}
|
||||
else {
|
||||
tenlib = outliner_add_element(space_outliner, lb, mainvar, NULL, TSE_ID_BASE, 0);
|
||||
tenlib->name = IFACE_("Current File");
|
||||
}
|
||||
}
|
||||
|
||||
/* Create data-block list parent element on demand. */
|
||||
if (id != NULL) {
|
||||
if (filter_id_type) {
|
||||
ten = tenlib;
|
||||
}
|
||||
else {
|
||||
ten = outliner_add_element(
|
||||
space_outliner, &tenlib->subtree, lbarray[a], NULL, TSE_ID_BASE, 0);
|
||||
ten->directdata = lbarray[a];
|
||||
ten->name = outliner_idcode_to_plural(GS(id->name));
|
||||
}
|
||||
|
||||
for (id = lbarray[a]->first; id; id = id->next) {
|
||||
if (outliner_library_id_show(lib, id, filter_id_type)) {
|
||||
outliner_add_element(space_outliner, &ten->subtree, id, ten, 0, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return tenlib;
|
||||
}
|
||||
|
||||
static void outliner_add_orphaned_datablocks(Main *mainvar, SpaceOutliner *space_outliner)
|
||||
{
|
||||
TreeElement *ten;
|
||||
@@ -1546,82 +1434,6 @@ static void outliner_add_orphaned_datablocks(Main *mainvar, SpaceOutliner *space
|
||||
}
|
||||
}
|
||||
|
||||
static void outliner_add_layer_collection_objects(SpaceOutliner *space_outliner,
|
||||
ListBase *tree,
|
||||
ViewLayer *layer,
|
||||
LayerCollection *lc,
|
||||
TreeElement *ten)
|
||||
{
|
||||
LISTBASE_FOREACH (CollectionObject *, cob, &lc->collection->gobject) {
|
||||
Base *base = BKE_view_layer_base_find(layer, cob->ob);
|
||||
TreeElement *te_object = outliner_add_element(space_outliner, tree, base->object, ten, 0, 0);
|
||||
te_object->directdata = base;
|
||||
|
||||
if (!(base->flag & BASE_VISIBLE_VIEWLAYER)) {
|
||||
te_object->flag |= TE_DISABLED;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void outliner_add_layer_collections_recursive(SpaceOutliner *space_outliner,
|
||||
ListBase *tree,
|
||||
ViewLayer *layer,
|
||||
ListBase *layer_collections,
|
||||
TreeElement *parent_ten,
|
||||
const bool show_objects)
|
||||
{
|
||||
LISTBASE_FOREACH (LayerCollection *, lc, layer_collections) {
|
||||
const bool exclude = (lc->flag & LAYER_COLLECTION_EXCLUDE) != 0;
|
||||
TreeElement *ten;
|
||||
|
||||
if (exclude && ((space_outliner->show_restrict_flags & SO_RESTRICT_ENABLE) == 0)) {
|
||||
ten = parent_ten;
|
||||
}
|
||||
else {
|
||||
ID *id = &lc->collection->id;
|
||||
ten = outliner_add_element(space_outliner, tree, id, parent_ten, TSE_LAYER_COLLECTION, 0);
|
||||
|
||||
ten->name = id->name + 2;
|
||||
ten->directdata = lc;
|
||||
|
||||
/* Open by default, except linked collections, which may contain many elements. */
|
||||
TreeStoreElem *tselem = TREESTORE(ten);
|
||||
if (!(tselem->used || ID_IS_LINKED(id) || ID_IS_OVERRIDE_LIBRARY(id))) {
|
||||
tselem->flag &= ~TSE_CLOSED;
|
||||
}
|
||||
|
||||
if (exclude || (lc->runtime_flag & LAYER_COLLECTION_VISIBLE_VIEW_LAYER) == 0) {
|
||||
ten->flag |= TE_DISABLED;
|
||||
}
|
||||
}
|
||||
|
||||
outliner_add_layer_collections_recursive(
|
||||
space_outliner, &ten->subtree, layer, &lc->layer_collections, ten, show_objects);
|
||||
if (!exclude && show_objects) {
|
||||
outliner_add_layer_collection_objects(space_outliner, &ten->subtree, layer, lc, ten);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void outliner_add_view_layer(SpaceOutliner *space_outliner,
|
||||
ListBase *tree,
|
||||
TreeElement *parent,
|
||||
ViewLayer *layer,
|
||||
const bool show_objects)
|
||||
{
|
||||
/* First layer collection is for master collection, don't show it. */
|
||||
LayerCollection *lc = layer->layer_collections.first;
|
||||
if (lc == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
outliner_add_layer_collections_recursive(
|
||||
space_outliner, tree, layer, &lc->layer_collections, parent, show_objects);
|
||||
if (show_objects) {
|
||||
outliner_add_layer_collection_objects(space_outliner, tree, layer, lc, parent);
|
||||
}
|
||||
}
|
||||
|
||||
BLI_INLINE void outliner_add_collection_init(TreeElement *te, Collection *collection)
|
||||
{
|
||||
te->name = BKE_collection_ui_name_get(collection);
|
||||
@@ -1661,7 +1473,7 @@ static TreeElement *outliner_add_collection_recursive(SpaceOutliner *space_outli
|
||||
/* Hierarchy --------------------------------------------- */
|
||||
|
||||
/* make sure elements are correctly nested */
|
||||
static void outliner_make_object_parent_hierarchy(ListBase *lb)
|
||||
void outliner_make_object_parent_hierarchy(ListBase *lb)
|
||||
{
|
||||
TreeElement *te, *ten, *tep;
|
||||
TreeStoreElem *tselem;
|
||||
@@ -1686,103 +1498,6 @@ static void outliner_make_object_parent_hierarchy(ListBase *lb)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* For all objects in the tree, lookup the parent in this map,
|
||||
* and move or add tree elements as needed.
|
||||
*/
|
||||
static void outliner_make_object_parent_hierarchy_collections(SpaceOutliner *space_outliner,
|
||||
GHash *object_tree_elements_hash)
|
||||
{
|
||||
GHashIterator gh_iter;
|
||||
GHASH_ITER (gh_iter, object_tree_elements_hash) {
|
||||
Object *child = BLI_ghashIterator_getKey(&gh_iter);
|
||||
|
||||
if (child->parent == NULL) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ListBase *child_ob_tree_elements = BLI_ghashIterator_getValue(&gh_iter);
|
||||
ListBase *parent_ob_tree_elements = BLI_ghash_lookup(object_tree_elements_hash, child->parent);
|
||||
if (parent_ob_tree_elements == NULL) {
|
||||
continue;
|
||||
}
|
||||
|
||||
LISTBASE_FOREACH (LinkData *, link, parent_ob_tree_elements) {
|
||||
TreeElement *parent_ob_tree_element = link->data;
|
||||
TreeElement *parent_ob_collection_tree_element = NULL;
|
||||
bool found = false;
|
||||
|
||||
/* We always want to remove the child from the direct collection its parent is nested under.
|
||||
* This is particularly important when dealing with multi-level nesting (grandchildren). */
|
||||
parent_ob_collection_tree_element = parent_ob_tree_element->parent;
|
||||
while (!ELEM(TREESTORE(parent_ob_collection_tree_element)->type,
|
||||
TSE_VIEW_COLLECTION_BASE,
|
||||
TSE_LAYER_COLLECTION)) {
|
||||
parent_ob_collection_tree_element = parent_ob_collection_tree_element->parent;
|
||||
}
|
||||
|
||||
LISTBASE_FOREACH (LinkData *, link_iter, child_ob_tree_elements) {
|
||||
TreeElement *child_ob_tree_element = link_iter->data;
|
||||
|
||||
if (child_ob_tree_element->parent == parent_ob_collection_tree_element) {
|
||||
/* Move from the collection subtree into the parent object subtree. */
|
||||
BLI_remlink(&parent_ob_collection_tree_element->subtree, child_ob_tree_element);
|
||||
BLI_addtail(&parent_ob_tree_element->subtree, child_ob_tree_element);
|
||||
child_ob_tree_element->parent = parent_ob_tree_element;
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
/* We add the child in the tree even if it is not in the collection.
|
||||
* We deliberately clear its sub-tree though, to make it less prominent. */
|
||||
TreeElement *child_ob_tree_element = outliner_add_element(
|
||||
space_outliner, &parent_ob_tree_element->subtree, child, parent_ob_tree_element, 0, 0);
|
||||
outliner_free_tree(&child_ob_tree_element->subtree);
|
||||
child_ob_tree_element->flag |= TE_CHILD_NOT_IN_COLLECTION;
|
||||
BLI_addtail(child_ob_tree_elements, BLI_genericNodeN(child_ob_tree_element));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a map from Object* to a list of TreeElement* matching the object.
|
||||
*/
|
||||
static void outliner_object_tree_elements_lookup_create_recursive(GHash *object_tree_elements_hash,
|
||||
TreeElement *te_parent)
|
||||
{
|
||||
LISTBASE_FOREACH (TreeElement *, te, &te_parent->subtree) {
|
||||
TreeStoreElem *tselem = TREESTORE(te);
|
||||
|
||||
if (tselem->type == TSE_LAYER_COLLECTION) {
|
||||
outliner_object_tree_elements_lookup_create_recursive(object_tree_elements_hash, te);
|
||||
}
|
||||
else if (tselem->type == 0 && te->idcode == ID_OB) {
|
||||
Object *ob = (Object *)tselem->id;
|
||||
ListBase *tree_elements = BLI_ghash_lookup(object_tree_elements_hash, ob);
|
||||
|
||||
if (tree_elements == NULL) {
|
||||
tree_elements = MEM_callocN(sizeof(ListBase), __func__);
|
||||
BLI_ghash_insert(object_tree_elements_hash, ob, tree_elements);
|
||||
}
|
||||
|
||||
BLI_addtail(tree_elements, BLI_genericNodeN(te));
|
||||
outliner_object_tree_elements_lookup_create_recursive(object_tree_elements_hash, te);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void outliner_object_tree_elements_lookup_free(GHash *object_tree_elements_hash)
|
||||
{
|
||||
GHASH_FOREACH_BEGIN (ListBase *, tree_elements, object_tree_elements_hash) {
|
||||
BLI_freelistN(tree_elements);
|
||||
MEM_freeN(tree_elements);
|
||||
}
|
||||
GHASH_FOREACH_END();
|
||||
}
|
||||
|
||||
/* Sorting ------------------------------------------------------ */
|
||||
|
||||
typedef struct tTreeSort {
|
||||
@@ -2499,59 +2214,23 @@ void outliner_build_tree(Main *mainvar,
|
||||
|
||||
outliner_free_tree(&space_outliner->tree);
|
||||
outliner_storage_cleanup(space_outliner);
|
||||
outliner_tree_display_destroy(&space_outliner->runtime->tree_display);
|
||||
|
||||
space_outliner->runtime->tree_display = outliner_tree_display_create(space_outliner->outlinevis,
|
||||
space_outliner);
|
||||
if (space_outliner->runtime->tree_display) {
|
||||
TreeSourceData source_data = {.bmain = mainvar, .scene = scene, .view_layer = view_layer};
|
||||
space_outliner->tree = outliner_tree_display_build_tree(space_outliner->runtime->tree_display,
|
||||
&source_data);
|
||||
}
|
||||
|
||||
if (space_outliner->runtime->tree_display) {
|
||||
/* Skip if there's a tree-display that's responsible for adding all elements. */
|
||||
}
|
||||
/* options */
|
||||
if (space_outliner->outlinevis == SO_LIBRARIES) {
|
||||
Library *lib;
|
||||
|
||||
/* current file first - mainvar provides tselem with unique pointer - not used */
|
||||
ten = outliner_add_library_contents(mainvar, space_outliner, &space_outliner->tree, NULL);
|
||||
if (ten) {
|
||||
tselem = TREESTORE(ten);
|
||||
if (!tselem->used) {
|
||||
tselem->flag &= ~TSE_CLOSED;
|
||||
}
|
||||
}
|
||||
|
||||
for (lib = mainvar->libraries.first; lib; lib = lib->id.next) {
|
||||
ten = outliner_add_library_contents(mainvar, space_outliner, &space_outliner->tree, lib);
|
||||
/* NULL-check matters, due to filtering there may not be a new element. */
|
||||
if (ten) {
|
||||
lib->id.newid = (ID *)ten;
|
||||
}
|
||||
}
|
||||
/* make hierarchy */
|
||||
ten = space_outliner->tree.first;
|
||||
if (ten != NULL) {
|
||||
ten = ten->next; /* first one is main */
|
||||
while (ten) {
|
||||
TreeElement *nten = ten->next, *par;
|
||||
tselem = TREESTORE(ten);
|
||||
lib = (Library *)tselem->id;
|
||||
if (lib && lib->parent) {
|
||||
par = (TreeElement *)lib->parent->id.newid;
|
||||
if (tselem->id->tag & LIB_TAG_INDIRECT) {
|
||||
/* Only remove from 'first level' if lib is not also directly used. */
|
||||
BLI_remlink(&space_outliner->tree, ten);
|
||||
BLI_addtail(&par->subtree, ten);
|
||||
ten->parent = par;
|
||||
}
|
||||
else {
|
||||
/* Else, make a new copy of the libtree for our parent. */
|
||||
TreeElement *dupten = outliner_add_library_contents(
|
||||
mainvar, space_outliner, &par->subtree, lib);
|
||||
if (dupten) {
|
||||
dupten->parent = par;
|
||||
}
|
||||
}
|
||||
}
|
||||
ten = nten;
|
||||
}
|
||||
}
|
||||
/* restore newid pointers */
|
||||
for (lib = mainvar->libraries.first; lib; lib = lib->id.next) {
|
||||
lib->id.newid = NULL;
|
||||
}
|
||||
else if (space_outliner->outlinevis == SO_LIBRARIES) {
|
||||
/* Ported to new tree-display, should be built there already. */
|
||||
BLI_assert(false);
|
||||
}
|
||||
else if (space_outliner->outlinevis == SO_SCENES) {
|
||||
Scene *sce;
|
||||
@@ -2612,38 +2291,8 @@ void outliner_build_tree(Main *mainvar,
|
||||
outliner_add_orphaned_datablocks(mainvar, space_outliner);
|
||||
}
|
||||
else if (space_outliner->outlinevis == SO_VIEW_LAYER) {
|
||||
if (space_outliner->filter & SO_FILTER_NO_COLLECTION) {
|
||||
/* Show objects in the view layer. */
|
||||
LISTBASE_FOREACH (Base *, base, &view_layer->object_bases) {
|
||||
TreeElement *te_object = outliner_add_element(
|
||||
space_outliner, &space_outliner->tree, base->object, NULL, 0, 0);
|
||||
te_object->directdata = base;
|
||||
}
|
||||
|
||||
if ((space_outliner->filter & SO_FILTER_NO_CHILDREN) == 0) {
|
||||
outliner_make_object_parent_hierarchy(&space_outliner->tree);
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* Show collections in the view layer. */
|
||||
ten = outliner_add_element(
|
||||
space_outliner, &space_outliner->tree, scene, NULL, TSE_VIEW_COLLECTION_BASE, 0);
|
||||
ten->name = IFACE_("Scene Collection");
|
||||
TREESTORE(ten)->flag &= ~TSE_CLOSED;
|
||||
|
||||
bool show_objects = !(space_outliner->filter & SO_FILTER_NO_OBJECT);
|
||||
outliner_add_view_layer(space_outliner, &ten->subtree, ten, view_layer, show_objects);
|
||||
|
||||
if ((space_outliner->filter & SO_FILTER_NO_CHILDREN) == 0) {
|
||||
GHash *object_tree_elements_hash = BLI_ghash_new(
|
||||
BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp, __func__);
|
||||
outliner_object_tree_elements_lookup_create_recursive(object_tree_elements_hash, ten);
|
||||
outliner_make_object_parent_hierarchy_collections(space_outliner,
|
||||
object_tree_elements_hash);
|
||||
outliner_object_tree_elements_lookup_free(object_tree_elements_hash);
|
||||
BLI_ghash_free(object_tree_elements_hash, NULL, NULL);
|
||||
}
|
||||
}
|
||||
/* Ported to new tree-display, should be built there already. */
|
||||
BLI_assert(false);
|
||||
}
|
||||
|
||||
if ((space_outliner->flag & SO_SKIP_SORT_ALPHA) == 0) {
|
||||
|
@@ -351,11 +351,21 @@ static void outliner_free(SpaceLink *sl)
|
||||
if (space_outliner->treehash) {
|
||||
BKE_outliner_treehash_free(space_outliner->treehash);
|
||||
}
|
||||
|
||||
if (space_outliner->runtime) {
|
||||
MEM_freeN(space_outliner->runtime);
|
||||
}
|
||||
}
|
||||
|
||||
/* spacetype; init callback */
|
||||
static void outliner_init(wmWindowManager *UNUSED(wm), ScrArea *UNUSED(area))
|
||||
static void outliner_init(wmWindowManager *UNUSED(wm), ScrArea *area)
|
||||
{
|
||||
SpaceOutliner *space_outliner = area->spacedata.first;
|
||||
|
||||
if (space_outliner->runtime == NULL) {
|
||||
space_outliner->runtime = MEM_callocN(sizeof(*space_outliner->runtime),
|
||||
"SpaceOutliner_Runtime");
|
||||
}
|
||||
}
|
||||
|
||||
static SpaceLink *outliner_duplicate(SpaceLink *sl)
|
||||
@@ -369,6 +379,10 @@ static SpaceLink *outliner_duplicate(SpaceLink *sl)
|
||||
|
||||
space_outliner_new->sync_select_dirty = WM_OUTLINER_SYNC_SELECT_FROM_ALL;
|
||||
|
||||
if (space_outliner->runtime) {
|
||||
space_outliner_new->runtime = MEM_dupallocN(space_outliner->runtime);
|
||||
}
|
||||
|
||||
return (SpaceLink *)space_outliner_new;
|
||||
}
|
||||
|
||||
|
41
source/blender/editors/space_outliner/tree/common.cc
Normal file
41
source/blender/editors/space_outliner/tree/common.cc
Normal file
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \ingroup spoutliner
|
||||
*
|
||||
* Functions and helpers shared between tree-display types or other tree related code.
|
||||
*/
|
||||
|
||||
#include "BKE_idtype.h"
|
||||
|
||||
#include "RNA_access.h"
|
||||
|
||||
#include "tree_display.hh"
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name ID Helpers.
|
||||
*
|
||||
* \{ */
|
||||
|
||||
const char *outliner_idcode_to_plural(short idcode)
|
||||
{
|
||||
const char *propname = BKE_idtype_idcode_to_name_plural(idcode);
|
||||
PropertyRNA *prop = RNA_struct_type_find_property(&RNA_BlendData, propname);
|
||||
return (prop) ? RNA_property_ui_name(prop) : "UNKNOWN";
|
||||
}
|
||||
|
||||
/** \} */
|
60
source/blender/editors/space_outliner/tree/tree_display.cc
Normal file
60
source/blender/editors/space_outliner/tree/tree_display.cc
Normal file
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \ingroup spoutliner
|
||||
*/
|
||||
|
||||
#include "BLI_listbase.h"
|
||||
|
||||
#include "DNA_listBase.h"
|
||||
|
||||
#include "tree_display.hh"
|
||||
|
||||
using namespace blender::ed::outliner;
|
||||
|
||||
TreeDisplay *outliner_tree_display_create(eSpaceOutliner_Mode mode, SpaceOutliner *space_outliner)
|
||||
{
|
||||
AbstractTreeDisplay *tree_display = nullptr;
|
||||
|
||||
switch (mode) {
|
||||
case SO_SCENES:
|
||||
break;
|
||||
case SO_LIBRARIES:
|
||||
tree_display = new TreeDisplayLibraries(*space_outliner);
|
||||
break;
|
||||
case SO_SEQUENCE:
|
||||
case SO_DATA_API:
|
||||
case SO_ID_ORPHANS:
|
||||
break;
|
||||
case SO_VIEW_LAYER:
|
||||
tree_display = new TreeDisplayViewLayer(*space_outliner);
|
||||
break;
|
||||
}
|
||||
|
||||
return reinterpret_cast<TreeDisplay *>(tree_display);
|
||||
}
|
||||
|
||||
void outliner_tree_display_destroy(TreeDisplay **tree_display)
|
||||
{
|
||||
delete reinterpret_cast<AbstractTreeDisplay *>(*tree_display);
|
||||
*tree_display = nullptr;
|
||||
}
|
||||
|
||||
ListBase outliner_tree_display_build_tree(TreeDisplay *tree_display, TreeSourceData *source_data)
|
||||
{
|
||||
return reinterpret_cast<AbstractTreeDisplay *>(tree_display)->buildTree(*source_data);
|
||||
}
|
64
source/blender/editors/space_outliner/tree/tree_display.h
Normal file
64
source/blender/editors/space_outliner/tree/tree_display.h
Normal file
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \ingroup spoutliner
|
||||
*
|
||||
* C-API for the Tree-Display types.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "DNA_space_types.h"
|
||||
|
||||
struct ListBase;
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/** C alias for an #AbstractTreeDisplay handle. */
|
||||
typedef struct TreeDisplay TreeDisplay;
|
||||
|
||||
/**
|
||||
* \brief The data to build the tree from.
|
||||
*/
|
||||
typedef struct TreeSourceData {
|
||||
struct Main *bmain;
|
||||
struct Scene *scene;
|
||||
struct ViewLayer *view_layer;
|
||||
} TreeSourceData;
|
||||
|
||||
TreeDisplay *outliner_tree_display_create(eSpaceOutliner_Mode mode, SpaceOutliner *space_outliner);
|
||||
void outliner_tree_display_destroy(TreeDisplay **tree_display);
|
||||
|
||||
ListBase outliner_tree_display_build_tree(TreeDisplay *tree_display, TreeSourceData *source_data);
|
||||
|
||||
/* The following functions are needed to build the tree. They are calls back into C; the way
|
||||
* elements are created should be refactored and ported to C++ with a new design/API too. */
|
||||
struct TreeElement *outliner_add_element(SpaceOutliner *space_outliner,
|
||||
ListBase *lb,
|
||||
void *idv,
|
||||
struct TreeElement *parent,
|
||||
short type,
|
||||
short index);
|
||||
void outliner_make_object_parent_hierarchy(ListBase *lb);
|
||||
|
||||
const char *outliner_idcode_to_plural(short idcode);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
113
source/blender/editors/space_outliner/tree/tree_display.hh
Normal file
113
source/blender/editors/space_outliner/tree/tree_display.hh
Normal file
@@ -0,0 +1,113 @@
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \ingroup spoutliner
|
||||
*
|
||||
* \brief Establish and manage Outliner trees for different display modes.
|
||||
*
|
||||
* Each Outliner display mode (e.g View Layer, Scenes, Blender File) is implemented as a
|
||||
* tree-display class with the #AbstractTreeDisplay interface.
|
||||
*
|
||||
* Their main responsibility is building the Outliner tree for a display mode. For that, they
|
||||
* implement the #buildTree() function, which based on Blender data (#TreeSourceData), builds a
|
||||
* custom tree of whatever data it wants to visualize.
|
||||
* Further, they can implement display mode dependent queries and general operations using the
|
||||
* #AbstractTreeDisplay abstraction as common interface.
|
||||
*
|
||||
* Outliners keep the current tree-display object alive until the next full tree rebuild to keep
|
||||
* access to it.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "tree_display.h"
|
||||
|
||||
struct ListBase;
|
||||
struct Main;
|
||||
struct SpaceOutliner;
|
||||
struct TreeElement;
|
||||
struct TreeSourceData;
|
||||
|
||||
namespace blender::ed::outliner {
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/* Tree-Display Interface */
|
||||
|
||||
/**
|
||||
* \brief Base Class For Tree-Displays
|
||||
*
|
||||
* Abstract base class defining the interface for tree-display variants.
|
||||
*/
|
||||
class AbstractTreeDisplay {
|
||||
public:
|
||||
AbstractTreeDisplay(SpaceOutliner &space_outliner) : space_outliner_(space_outliner)
|
||||
{
|
||||
}
|
||||
virtual ~AbstractTreeDisplay() = default;
|
||||
|
||||
/**
|
||||
* Build a tree for this display mode with the Blender context data given in \a source_data and
|
||||
* the view settings in \a space_outliner.
|
||||
*/
|
||||
virtual ListBase buildTree(const TreeSourceData &source_data) = 0;
|
||||
|
||||
protected:
|
||||
/** All derived classes will need a handle to this, so storing it in the base for convenience. */
|
||||
SpaceOutliner &space_outliner_;
|
||||
};
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/* View Layer Tree-Display */
|
||||
|
||||
/**
|
||||
* \brief Tree-Display for the View Layer display mode.
|
||||
*/
|
||||
class TreeDisplayViewLayer final : public AbstractTreeDisplay {
|
||||
ViewLayer *view_layer_ = nullptr;
|
||||
bool show_objects_ = true;
|
||||
|
||||
public:
|
||||
TreeDisplayViewLayer(SpaceOutliner &space_outliner);
|
||||
|
||||
ListBase buildTree(const TreeSourceData &source_data) override;
|
||||
|
||||
private:
|
||||
void add_view_layer(ListBase &, TreeElement &);
|
||||
void add_layer_collections_recursive(ListBase &, ListBase &, TreeElement &);
|
||||
void add_layer_collection_objects(ListBase &, LayerCollection &, TreeElement &);
|
||||
void add_layer_collection_objects_children(TreeElement &);
|
||||
};
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/* Library Tree-Display */
|
||||
|
||||
/**
|
||||
* \brief Tree-Display for the Libraries display mode.
|
||||
*/
|
||||
class TreeDisplayLibraries final : public AbstractTreeDisplay {
|
||||
public:
|
||||
TreeDisplayLibraries(SpaceOutliner &space_outliner);
|
||||
|
||||
ListBase buildTree(const TreeSourceData &source_data) override;
|
||||
|
||||
private:
|
||||
TreeElement *add_library_contents(Main &, ListBase &, Library *) const;
|
||||
bool library_id_filter_poll(Library *lib, ID *id) const;
|
||||
short id_filter_get() const;
|
||||
};
|
||||
|
||||
} // namespace blender::ed::outliner
|
@@ -0,0 +1,213 @@
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \ingroup spoutliner
|
||||
*/
|
||||
|
||||
#include "BLI_listbase.h"
|
||||
#include "BLI_listbase_wrapper.hh"
|
||||
|
||||
#include "BKE_collection.h"
|
||||
#include "BKE_main.h"
|
||||
|
||||
#include "BLT_translation.h"
|
||||
|
||||
#include "../outliner_intern.h"
|
||||
#include "tree_display.hh"
|
||||
|
||||
namespace blender::ed::outliner {
|
||||
|
||||
/* Convenience/readability. */
|
||||
template<typename T> using List = ListBaseWrapper<T>;
|
||||
|
||||
TreeDisplayLibraries::TreeDisplayLibraries(SpaceOutliner &space_outliner)
|
||||
: AbstractTreeDisplay(space_outliner)
|
||||
{
|
||||
}
|
||||
|
||||
ListBase TreeDisplayLibraries::buildTree(const TreeSourceData &source_data)
|
||||
{
|
||||
ListBase tree = {nullptr};
|
||||
|
||||
{
|
||||
/* current file first - mainvar provides tselem with unique pointer - not used */
|
||||
TreeElement *ten = add_library_contents(*source_data.bmain, tree, nullptr);
|
||||
TreeStoreElem *tselem;
|
||||
|
||||
if (ten) {
|
||||
tselem = TREESTORE(ten);
|
||||
if (!tselem->used) {
|
||||
tselem->flag &= ~TSE_CLOSED;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (ID *id : List<ID>(source_data.bmain->libraries)) {
|
||||
Library *lib = reinterpret_cast<Library *>(id);
|
||||
TreeElement *ten = add_library_contents(*source_data.bmain, tree, lib);
|
||||
/* NULL-check matters, due to filtering there may not be a new element. */
|
||||
if (ten) {
|
||||
lib->id.newid = (ID *)ten;
|
||||
}
|
||||
}
|
||||
|
||||
/* make hierarchy */
|
||||
for (TreeElement *ten : List<TreeElement>(tree)) {
|
||||
if (ten == tree.first) {
|
||||
/* First item is main, skip. */
|
||||
continue;
|
||||
}
|
||||
|
||||
TreeStoreElem *tselem = TREESTORE(ten);
|
||||
Library *lib = (Library *)tselem->id;
|
||||
BLI_assert(!lib || (GS(lib->id.name) == ID_LI));
|
||||
if (!lib || !lib->parent) {
|
||||
continue;
|
||||
}
|
||||
|
||||
TreeElement *parent = (TreeElement *)lib->parent->id.newid;
|
||||
|
||||
if (tselem->id->tag & LIB_TAG_INDIRECT) {
|
||||
/* Only remove from 'first level' if lib is not also directly used. */
|
||||
BLI_remlink(&tree, ten);
|
||||
BLI_addtail(&parent->subtree, ten);
|
||||
ten->parent = parent;
|
||||
}
|
||||
else {
|
||||
/* Else, make a new copy of the libtree for our parent. */
|
||||
TreeElement *dupten = add_library_contents(*source_data.bmain, parent->subtree, lib);
|
||||
if (dupten) {
|
||||
dupten->parent = parent;
|
||||
}
|
||||
}
|
||||
}
|
||||
/* restore newid pointers */
|
||||
for (ID *library_id : List<ID>(source_data.bmain->libraries)) {
|
||||
library_id->newid = nullptr;
|
||||
}
|
||||
|
||||
return tree;
|
||||
}
|
||||
|
||||
TreeElement *TreeDisplayLibraries::add_library_contents(Main &mainvar,
|
||||
ListBase &lb,
|
||||
Library *lib) const
|
||||
{
|
||||
const short filter_id_type = id_filter_get();
|
||||
|
||||
ListBase *lbarray[MAX_LIBARRAY];
|
||||
int tot;
|
||||
if (filter_id_type) {
|
||||
lbarray[0] = which_libbase(&mainvar, space_outliner_.filter_id_type);
|
||||
tot = 1;
|
||||
}
|
||||
else {
|
||||
tot = set_listbasepointers(&mainvar, lbarray);
|
||||
}
|
||||
|
||||
TreeElement *tenlib = nullptr;
|
||||
for (int a = 0; a < tot; a++) {
|
||||
if (!lbarray[a] || !lbarray[a]->first) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ID *id = static_cast<ID *>(lbarray[a]->first);
|
||||
const bool is_library = (GS(id->name) == ID_LI) && (lib != nullptr);
|
||||
|
||||
/* check if there's data in current lib */
|
||||
for (ID *id_iter : List<ID>(lbarray[a])) {
|
||||
if (id_iter->lib == lib) {
|
||||
id = id_iter;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* We always want to create an entry for libraries, even if/when we have no more IDs from
|
||||
* them. This invalid state is important to show to user as well.*/
|
||||
if (id != nullptr || is_library) {
|
||||
if (!tenlib) {
|
||||
/* Create library tree element on demand, depending if there are any data-blocks. */
|
||||
if (lib) {
|
||||
tenlib = outliner_add_element(&space_outliner_, &lb, lib, nullptr, 0, 0);
|
||||
}
|
||||
else {
|
||||
tenlib = outliner_add_element(&space_outliner_, &lb, &mainvar, nullptr, TSE_ID_BASE, 0);
|
||||
tenlib->name = IFACE_("Current File");
|
||||
}
|
||||
}
|
||||
|
||||
/* Create data-block list parent element on demand. */
|
||||
if (id != nullptr) {
|
||||
TreeElement *ten;
|
||||
|
||||
if (filter_id_type) {
|
||||
ten = tenlib;
|
||||
}
|
||||
else {
|
||||
ten = outliner_add_element(
|
||||
&space_outliner_, &tenlib->subtree, lbarray[a], nullptr, TSE_ID_BASE, 0);
|
||||
ten->directdata = lbarray[a];
|
||||
ten->name = outliner_idcode_to_plural(GS(id->name));
|
||||
}
|
||||
|
||||
for (ID *id : List<ID>(lbarray[a])) {
|
||||
if (library_id_filter_poll(lib, id)) {
|
||||
outliner_add_element(&space_outliner_, &ten->subtree, id, ten, 0, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return tenlib;
|
||||
}
|
||||
|
||||
short TreeDisplayLibraries::id_filter_get() const
|
||||
{
|
||||
if (space_outliner_.filter & SO_FILTER_ID_TYPE) {
|
||||
return space_outliner_.filter_id_type;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool TreeDisplayLibraries::library_id_filter_poll(Library *lib, ID *id) const
|
||||
{
|
||||
if (id->lib != lib) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (id_filter_get() == ID_GR) {
|
||||
/* Don't show child collections of non-scene master collection,
|
||||
* they are already shown as children. */
|
||||
Collection *collection = (Collection *)id;
|
||||
bool has_non_scene_parent = false;
|
||||
|
||||
for (CollectionParent *cparent : List<CollectionParent>(collection->parents)) {
|
||||
if (!(cparent->collection->flag & COLLECTION_IS_MASTER)) {
|
||||
has_non_scene_parent = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (has_non_scene_parent) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace blender::ed::outliner
|
@@ -0,0 +1,284 @@
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
/** \file
|
||||
* \ingroup spoutliner
|
||||
*/
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include "DNA_scene_types.h"
|
||||
|
||||
#include "BKE_layer.h"
|
||||
|
||||
#include "BLI_listbase.h"
|
||||
#include "BLI_listbase_wrapper.hh"
|
||||
#include "BLI_map.hh"
|
||||
#include "BLI_vector.hh"
|
||||
|
||||
#include "BLT_translation.h"
|
||||
|
||||
#include "../outliner_intern.h"
|
||||
#include "tree_display.hh"
|
||||
|
||||
namespace blender::ed::outliner {
|
||||
|
||||
/* Convenience/readability. */
|
||||
template<typename T> using List = ListBaseWrapper<T>;
|
||||
|
||||
class ObjectsChildrenBuilder {
|
||||
using TreeChildren = Vector<TreeElement *>;
|
||||
using ObjectTreeElementsMap = Map<Object *, TreeChildren>;
|
||||
|
||||
SpaceOutliner &outliner_;
|
||||
ObjectTreeElementsMap object_tree_elements_map_;
|
||||
|
||||
public:
|
||||
ObjectsChildrenBuilder(SpaceOutliner &);
|
||||
~ObjectsChildrenBuilder() = default;
|
||||
|
||||
void operator()(TreeElement &collection_tree_elem);
|
||||
|
||||
private:
|
||||
void object_tree_elements_lookup_create_recursive(TreeElement *);
|
||||
void make_object_parent_hierarchy_collections();
|
||||
};
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Tree-Display for a View Layer.
|
||||
*
|
||||
* \{ */
|
||||
|
||||
TreeDisplayViewLayer::TreeDisplayViewLayer(SpaceOutliner &space_outliner)
|
||||
: AbstractTreeDisplay(space_outliner)
|
||||
{
|
||||
}
|
||||
|
||||
ListBase TreeDisplayViewLayer::buildTree(const TreeSourceData &source_data)
|
||||
{
|
||||
ListBase tree = {nullptr};
|
||||
|
||||
view_layer_ = source_data.view_layer;
|
||||
show_objects_ = !(space_outliner_.filter & SO_FILTER_NO_OBJECT);
|
||||
|
||||
const bool show_children = (space_outliner_.filter & SO_FILTER_NO_CHILDREN) == 0;
|
||||
|
||||
if (space_outliner_.filter & SO_FILTER_NO_COLLECTION) {
|
||||
/* Show objects in the view layer. */
|
||||
for (Base *base : List<Base>(view_layer_->object_bases)) {
|
||||
TreeElement *te_object = outliner_add_element(
|
||||
&space_outliner_, &tree, base->object, nullptr, 0, 0);
|
||||
te_object->directdata = base;
|
||||
}
|
||||
|
||||
if (show_children) {
|
||||
outliner_make_object_parent_hierarchy(&tree);
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* Show collections in the view layer. */
|
||||
TreeElement &ten = *outliner_add_element(
|
||||
&space_outliner_, &tree, source_data.scene, nullptr, TSE_VIEW_COLLECTION_BASE, 0);
|
||||
ten.name = IFACE_("Scene Collection");
|
||||
TREESTORE(&ten)->flag &= ~TSE_CLOSED;
|
||||
|
||||
add_view_layer(ten.subtree, ten);
|
||||
if (show_children) {
|
||||
add_layer_collection_objects_children(ten);
|
||||
}
|
||||
}
|
||||
|
||||
return tree;
|
||||
}
|
||||
|
||||
void TreeDisplayViewLayer::add_view_layer(ListBase &tree, TreeElement &parent)
|
||||
{
|
||||
/* First layer collection is for master collection, don't show it. */
|
||||
LayerCollection *lc = static_cast<LayerCollection *>(view_layer_->layer_collections.first);
|
||||
if (lc == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
add_layer_collections_recursive(tree, lc->layer_collections, parent);
|
||||
if (show_objects_) {
|
||||
add_layer_collection_objects(tree, *lc, parent);
|
||||
}
|
||||
}
|
||||
|
||||
void TreeDisplayViewLayer::add_layer_collections_recursive(ListBase &tree,
|
||||
ListBase &layer_collections,
|
||||
TreeElement &parent_ten)
|
||||
{
|
||||
for (LayerCollection *lc : List<LayerCollection>(layer_collections)) {
|
||||
const bool exclude = (lc->flag & LAYER_COLLECTION_EXCLUDE) != 0;
|
||||
TreeElement *ten;
|
||||
|
||||
if (exclude && ((space_outliner_.show_restrict_flags & SO_RESTRICT_ENABLE) == 0)) {
|
||||
ten = &parent_ten;
|
||||
}
|
||||
else {
|
||||
ID *id = &lc->collection->id;
|
||||
ten = outliner_add_element(
|
||||
&space_outliner_, &tree, id, &parent_ten, TSE_LAYER_COLLECTION, 0);
|
||||
|
||||
ten->name = id->name + 2;
|
||||
ten->directdata = lc;
|
||||
|
||||
/* Open by default, except linked collections, which may contain many elements. */
|
||||
TreeStoreElem *tselem = TREESTORE(ten);
|
||||
if (!(tselem->used || ID_IS_LINKED(id) || ID_IS_OVERRIDE_LIBRARY(id))) {
|
||||
tselem->flag &= ~TSE_CLOSED;
|
||||
}
|
||||
|
||||
if (exclude || (lc->runtime_flag & LAYER_COLLECTION_VISIBLE_VIEW_LAYER) == 0) {
|
||||
ten->flag |= TE_DISABLED;
|
||||
}
|
||||
}
|
||||
|
||||
add_layer_collections_recursive(ten->subtree, lc->layer_collections, *ten);
|
||||
if (!exclude && show_objects_) {
|
||||
add_layer_collection_objects(ten->subtree, *lc, *ten);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TreeDisplayViewLayer::add_layer_collection_objects(ListBase &tree,
|
||||
LayerCollection &lc,
|
||||
TreeElement &ten)
|
||||
{
|
||||
for (CollectionObject *cob : List<CollectionObject>(lc.collection->gobject)) {
|
||||
Base *base = BKE_view_layer_base_find(view_layer_, cob->ob);
|
||||
TreeElement *te_object = outliner_add_element(
|
||||
&space_outliner_, &tree, base->object, &ten, 0, 0);
|
||||
te_object->directdata = base;
|
||||
|
||||
if (!(base->flag & BASE_VISIBLE_VIEWLAYER)) {
|
||||
te_object->flag |= TE_DISABLED;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TreeDisplayViewLayer::add_layer_collection_objects_children(TreeElement &collection_tree_elem)
|
||||
{
|
||||
/* Call helper to add children. */
|
||||
ObjectsChildrenBuilder child_builder{space_outliner_};
|
||||
child_builder(collection_tree_elem);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Object Children helper.
|
||||
*
|
||||
* Helper to add child objects to the sub-tree of their parent, recursively covering all nested
|
||||
* collections.
|
||||
*
|
||||
* \{ */
|
||||
|
||||
ObjectsChildrenBuilder::ObjectsChildrenBuilder(SpaceOutliner &outliner) : outliner_(outliner)
|
||||
{
|
||||
}
|
||||
|
||||
void ObjectsChildrenBuilder::operator()(TreeElement &collection_tree_elem)
|
||||
{
|
||||
object_tree_elements_lookup_create_recursive(&collection_tree_elem);
|
||||
make_object_parent_hierarchy_collections();
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a map from Object* to a list of TreeElement* matching the object.
|
||||
*/
|
||||
void ObjectsChildrenBuilder::object_tree_elements_lookup_create_recursive(TreeElement *te_parent)
|
||||
{
|
||||
for (TreeElement *te : List<TreeElement>(te_parent->subtree)) {
|
||||
TreeStoreElem *tselem = TREESTORE(te);
|
||||
|
||||
if (tselem->type == TSE_LAYER_COLLECTION) {
|
||||
object_tree_elements_lookup_create_recursive(te);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (tselem->type == 0 && te->idcode == ID_OB) {
|
||||
Object *ob = (Object *)tselem->id;
|
||||
/* Lookup children or add new, empty children vector. */
|
||||
Vector<TreeElement *> &tree_elements = object_tree_elements_map_.lookup_or_add(ob, {});
|
||||
|
||||
tree_elements.append(te);
|
||||
object_tree_elements_lookup_create_recursive(te);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* For all objects in the tree, lookup the parent in this map,
|
||||
* and move or add tree elements as needed.
|
||||
*/
|
||||
void ObjectsChildrenBuilder::make_object_parent_hierarchy_collections()
|
||||
{
|
||||
for (ObjectTreeElementsMap::MutableItem item : object_tree_elements_map_.items()) {
|
||||
Object *child = item.key;
|
||||
|
||||
if (child->parent == nullptr) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Vector<TreeElement *> &child_ob_tree_elements = item.value;
|
||||
Vector<TreeElement *> *parent_ob_tree_elements = object_tree_elements_map_.lookup_ptr(
|
||||
child->parent);
|
||||
if (parent_ob_tree_elements == nullptr) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (TreeElement *parent_ob_tree_element : *parent_ob_tree_elements) {
|
||||
TreeElement *parent_ob_collection_tree_element = nullptr;
|
||||
bool found = false;
|
||||
|
||||
/* We always want to remove the child from the direct collection its parent is nested under.
|
||||
* This is particularly important when dealing with multi-level nesting (grandchildren). */
|
||||
parent_ob_collection_tree_element = parent_ob_tree_element->parent;
|
||||
while (!ELEM(TREESTORE(parent_ob_collection_tree_element)->type,
|
||||
TSE_VIEW_COLLECTION_BASE,
|
||||
TSE_LAYER_COLLECTION)) {
|
||||
parent_ob_collection_tree_element = parent_ob_collection_tree_element->parent;
|
||||
}
|
||||
|
||||
for (TreeElement *child_ob_tree_element : child_ob_tree_elements) {
|
||||
if (child_ob_tree_element->parent == parent_ob_collection_tree_element) {
|
||||
/* Move from the collection subtree into the parent object subtree. */
|
||||
BLI_remlink(&parent_ob_collection_tree_element->subtree, child_ob_tree_element);
|
||||
BLI_addtail(&parent_ob_tree_element->subtree, child_ob_tree_element);
|
||||
child_ob_tree_element->parent = parent_ob_tree_element;
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
/* We add the child in the tree even if it is not in the collection.
|
||||
* We deliberately clear its sub-tree though, to make it less prominent. */
|
||||
TreeElement *child_ob_tree_element = outliner_add_element(
|
||||
&outliner_, &parent_ob_tree_element->subtree, child, parent_ob_tree_element, 0, 0);
|
||||
outliner_free_tree(&child_ob_tree_element->subtree);
|
||||
child_ob_tree_element->flag |= TE_CHILD_NOT_IN_COLLECTION;
|
||||
child_ob_tree_elements.append(child_ob_tree_element);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender::ed::outliner
|
@@ -234,6 +234,9 @@ typedef enum eSpaceButtons_Flag {
|
||||
/** \name Outliner
|
||||
* \{ */
|
||||
|
||||
/* Defined in `outliner_intern.h`. */
|
||||
typedef struct SpaceOutliner_Runtime SpaceOutliner_Runtime;
|
||||
|
||||
/* Outliner */
|
||||
typedef struct SpaceOutliner {
|
||||
SpaceLink *next, *prev;
|
||||
@@ -276,6 +279,8 @@ typedef struct SpaceOutliner {
|
||||
* Pointers to treestore elements, grouped by (id, type, nr)
|
||||
* in hashtable for faster searching */
|
||||
void *treehash;
|
||||
|
||||
SpaceOutliner_Runtime *runtime;
|
||||
} SpaceOutliner;
|
||||
|
||||
/* SpaceOutliner.flag */
|
||||
|
Reference in New Issue
Block a user