Fix collection syncing when creating new collections from the outliner
[blender.git] / source / blender / blenkernel / intern / layer.c
index 9922f772e28123541e3df1b969e60f72ba038626..cb7b72bdabd659a6c1b34646e5aabc0770dd9701 100644 (file)
@@ -61,6 +61,7 @@
 
 /* prototype */
 struct EngineSettingsCB_Type;
+static void layer_collections_sync_flags(ListBase *layer_collections_dst, const ListBase *layer_collections_src);
 static void layer_collection_free(ViewLayer *view_layer, LayerCollection *lc);
 static void layer_collection_objects_populate(ViewLayer *view_layer, LayerCollection *lc, ListBase *objects);
 static LayerCollection *layer_collection_add(ViewLayer *view_layer, LayerCollection *parent, SceneCollection *sc);
@@ -99,7 +100,7 @@ ViewLayer *BKE_view_layer_from_workspace_get(const struct Scene *scene, const st
 
 /**
  * This is a placeholder to know which areas of the code need to be addressed for the Workspace changes.
- * Never use this, you should either use BKE_view_layer_workspace_active or get ViewLayer explicitly.
+ * Never use this, you should either use BKE_view_layer_from_workspace_get or get ViewLayer explicitly.
  */
 ViewLayer *BKE_view_layer_context_active_PLACEHOLDER(const Scene *scene)
 {
@@ -353,27 +354,95 @@ static SceneCollection *scene_collection_from_new_tree(
        return NULL;
 }
 
+static void layer_collection_sync_flags(
+        LayerCollection *layer_collection_dst,
+        const LayerCollection *layer_collection_src)
+{
+       layer_collection_dst->flag = layer_collection_src->flag;
+
+       if (layer_collection_dst->properties != NULL) {
+               IDP_FreeProperty(layer_collection_dst->properties);
+               MEM_SAFE_FREE(layer_collection_dst->properties);
+       }
+
+       if (layer_collection_src->properties != NULL) {
+               layer_collection_dst->properties = IDP_CopyProperty(layer_collection_src->properties);
+       }
+
+       layer_collections_sync_flags(&layer_collection_dst->layer_collections,
+                                    &layer_collection_src->layer_collections);
+}
+
 static void layer_collections_sync_flags(ListBase *layer_collections_dst, const ListBase *layer_collections_src)
 {
+       BLI_assert(BLI_listbase_count(layer_collections_dst) == BLI_listbase_count(layer_collections_src));
        LayerCollection *layer_collection_dst = (LayerCollection *)layer_collections_dst->first;
        const LayerCollection *layer_collection_src = (const LayerCollection *)layer_collections_src->first;
        while (layer_collection_dst != NULL) {
-               layer_collection_dst->flag = layer_collection_src->flag;
+               layer_collection_sync_flags(layer_collection_dst, layer_collection_src);
+               layer_collection_dst = layer_collection_dst->next;
+               layer_collection_src = layer_collection_src->next;
+       }
+}
 
-               if (layer_collection_dst->properties != NULL) {
-                       IDP_FreeProperty(layer_collection_dst->properties);
-                       MEM_SAFE_FREE(layer_collection_dst->properties);
+static bool layer_collection_sync_if_match(
+        ListBase *lb,
+        const SceneCollection *scene_collection_dst,
+        const SceneCollection *scene_collection_src)
+{
+       for (LayerCollection *layer_collection = lb->first;
+            layer_collection;
+            layer_collection = layer_collection->next)
+       {
+               if (layer_collection->scene_collection == scene_collection_src) {
+                       LayerCollection *layer_collection_dst =
+                               BLI_findptr(
+                                   lb,
+                                   scene_collection_dst,
+                                   offsetof(LayerCollection, scene_collection));
+
+                       if (layer_collection_dst != NULL) {
+                               layer_collection_sync_flags(layer_collection_dst, layer_collection);
+                       }
+                       return true;
                }
-
-               if (layer_collection_src->properties != NULL) {
-                       layer_collection_dst->properties = IDP_CopyProperty(layer_collection_src->properties);
+               else {
+                       if (layer_collection_sync_if_match(
+                               &layer_collection->layer_collections,
+                               scene_collection_dst,
+                               scene_collection_src))
+                       {
+                               return true;
+                       }
                }
+       }
+       return false;
+}
 
-               layer_collections_sync_flags(&layer_collection_dst->layer_collections,
-                                            &layer_collection_src->layer_collections);
-
-               layer_collection_dst = layer_collection_dst->next;
-               layer_collection_src = layer_collection_src->next;
+/**
+ * Sync sibling collections across all view layers
+ *
+ * Make sure every linked instance of \a scene_collection_dst has the same values
+ * (flags, overrides, ...) as the corresponding scene_collection_src.
+ *
+ * \note expect scene_collection_dst to be scene_collection_src->next, and it also
+ * expects both collections to have the same ammount of sub-collections.
+ */
+void BKE_layer_collection_sync_flags(
+        ID *owner_id,
+        SceneCollection *scene_collection_dst,
+        SceneCollection *scene_collection_src)
+{
+       for (ViewLayer *view_layer = BKE_view_layer_first_from_id(owner_id); view_layer; view_layer = view_layer->next) {
+               for (LayerCollection *layer_collection = view_layer->layer_collections.first;
+                    layer_collection;
+                    layer_collection = layer_collection->next)
+               {
+                       layer_collection_sync_if_match(
+                                   &layer_collection->layer_collections,
+                                   scene_collection_dst,
+                                   scene_collection_src);
+               }
        }
 }
 
@@ -438,6 +507,78 @@ void BKE_view_layer_copy_data(
        }
 }
 
+/**
+ * Find and return the ListBase of LayerCollection that has \a lc_child as one of its directly
+ * nested LayerCollection.
+ *
+ * \param lb_parent Initial ListBase of LayerCollection to look into recursively
+ * usually the view layer's collection list
+ */
+static ListBase *find_layer_collection_parent_list_base(ListBase *lb_parent, const LayerCollection *lc_child)
+{
+       for (LayerCollection *lc_nested = lb_parent->first; lc_nested; lc_nested = lc_nested->next) {
+               if (lc_nested == lc_child) {
+                       return lb_parent;
+               }
+
+               ListBase *found = find_layer_collection_parent_list_base(&lc_nested->layer_collections, lc_child);
+               if (found != NULL) {
+                       return found;
+               }
+       }
+
+       return NULL;
+}
+
+/**
+ * Makes a shallow copy of a LayerCollection
+ *
+ * Add a new collection in the same level as the old one (linking if necessary),
+ * and copy all the collection data across them.
+ */
+struct LayerCollection *BKE_layer_collection_duplicate(struct ID *owner_id, struct LayerCollection *layer_collection)
+{
+       SceneCollection *scene_collection, *scene_collection_new;
+
+       scene_collection = layer_collection->scene_collection;
+       scene_collection_new = BKE_collection_duplicate(owner_id, scene_collection);
+
+       LayerCollection *layer_collection_new = NULL;
+
+       /* If the original layer_collection was directly linked to the view layer
+          we need to link the new scene collection here as well. */
+       for (ViewLayer *view_layer = BKE_view_layer_first_from_id(owner_id); view_layer; view_layer = view_layer->next) {
+               if (BLI_findindex(&view_layer->layer_collections, layer_collection) != -1) {
+                       layer_collection_new = BKE_collection_link(view_layer, scene_collection_new);
+                       layer_collection_sync_flags(layer_collection_new, layer_collection);
+
+                       if (layer_collection_new != layer_collection->next) {
+                               BLI_remlink(&view_layer->layer_collections, layer_collection_new);
+                               BLI_insertlinkafter(&view_layer->layer_collections, layer_collection, layer_collection_new);
+                       }
+                       break;
+               }
+       }
+
+       /* Otherwise just try to find the corresponding layer collection to return it back. */
+       if (layer_collection_new == NULL) {
+               for (ViewLayer *view_layer = BKE_view_layer_first_from_id(owner_id); view_layer; view_layer = view_layer->next) {
+                       ListBase *layer_collections_parent;
+                       layer_collections_parent = find_layer_collection_parent_list_base(
+                                                      &view_layer->layer_collections,
+                                                      layer_collection);
+                       if (layer_collections_parent != NULL) {
+                               layer_collection_new = BLI_findptr(
+                                                                                  layer_collections_parent,
+                                                                                  scene_collection_new,
+                                                                                  offsetof(LayerCollection, scene_collection));
+                               break;
+                       }
+               }
+       }
+       return layer_collection_new;
+}
+
 static void view_layer_object_base_unref(ViewLayer *view_layer, Base *base)
 {
        base->refcount--;
@@ -987,6 +1128,34 @@ void BKE_layer_collection_resync(const ID *owner_id, const SceneCollection *sc)
 
 /* ---------------------------------------------------------------------- */
 
+/**
+ * Select all the objects of this layer collection
+ *
+ * It also select the objects that are in nested collections.
+ * \note Recursive
+ */
+void BKE_layer_collection_objects_select(struct LayerCollection *layer_collection)
+{
+       if ((layer_collection->flag & COLLECTION_DISABLED) ||
+           ((layer_collection->flag & COLLECTION_SELECTABLE) == 0))
+       {
+               return;
+       }
+
+       for (LinkData *link = layer_collection->object_bases.first; link; link = link->next) {
+               Base *base = link->data;
+               if (base->flag & BASE_SELECTABLED) {
+                       base->flag |= BASE_SELECTED;
+               }
+       }
+
+       for (LayerCollection *iter = layer_collection->layer_collections.first; iter; iter = iter->next) {
+               BKE_layer_collection_objects_select(iter);
+       }
+}
+
+/* ---------------------------------------------------------------------- */
+
 /**
  * Link a collection to a renderlayer
  * The collection needs to be created separately
@@ -1970,10 +2139,11 @@ void BKE_renderable_objects_iterator_begin(BLI_Iterator *iter, void *data_in)
 {
        ObjectsRenderableIteratorData *data = data_in;
 
+       /* Tag objects to prevent going over the same object twice. */
        for (Scene *scene = data->scene; scene; scene = scene->set) {
                for (ViewLayer *view_layer = scene->view_layers.first; view_layer; view_layer = view_layer->next) {
                        for (Base *base = view_layer->object_bases.first; base; base = base->next) {
-                                base->object->id.flag |=  LIB_TAG_DOIT;
+                                base->object->id.flag |= LIB_TAG_DOIT;
                        }
                }
        }
@@ -1981,8 +2151,8 @@ void BKE_renderable_objects_iterator_begin(BLI_Iterator *iter, void *data_in)
        ViewLayer *view_layer = data->scene->view_layers.first;
        data->iter.view_layer = view_layer;
 
-       Base base = {(Base *)view_layer->object_bases.first, NULL};
-       data->iter.base = &base;
+       data->base_temp.next = view_layer->object_bases.first;
+       data->iter.base = &data->base_temp;
 
        data->iter.set = NULL;
 
@@ -1992,6 +2162,9 @@ void BKE_renderable_objects_iterator_begin(BLI_Iterator *iter, void *data_in)
 
 void BKE_renderable_objects_iterator_next(BLI_Iterator *iter)
 {
+       /* Set it early in case we need to exit and we are running from within a loop. */
+       iter->skip = true;
+
        ObjectsRenderableIteratorData *data = iter->data;
        Base *base = data->iter.base->next;
 
@@ -1999,11 +2172,17 @@ void BKE_renderable_objects_iterator_next(BLI_Iterator *iter)
        if (base != NULL) {
                Object *ob = base->object;
 
-               iter->current = ob;
+               /* We need to set the iter.base even if the rest fail otherwise
+                * we keep checking the exactly same base over and over again. */
                data->iter.base = base;
 
-               if ((base->flag & BASE_VISIBLED) == 0) {
-                       BKE_renderable_objects_iterator_next(iter);
+               if (ob->id.flag & LIB_TAG_DOIT) {
+                       ob->id.flag &= ~LIB_TAG_DOIT;
+
+                       if ((base->flag & BASE_VISIBLED) != 0) {
+                               iter->skip = false;
+                               iter->current = ob;
+                       }
                }
                return;
        }
@@ -2013,30 +2192,23 @@ void BKE_renderable_objects_iterator_next(BLI_Iterator *iter)
                while ((data->iter.view_layer = data->iter.view_layer->next)) {
                        ViewLayer *view_layer = data->iter.view_layer;
                        if (view_layer->flag & VIEW_LAYER_RENDER) {
-
-                               Base base_iter = {(Base *)view_layer->object_bases.first, NULL};
-                               data->iter.base = &base_iter;
-
-                               BKE_renderable_objects_iterator_next(iter);
+                               data->base_temp.next = view_layer->object_bases.first;
+                               data->iter.base = &data->base_temp;
                                return;
                        }
                }
 
                /* Setup the "set" for the next iteration. */
-               Scene scene = {.set = data->scene};
-               data->iter.set = &scene;
-               BKE_renderable_objects_iterator_next(iter);
+               data->scene_temp.set = data->scene;
+               data->iter.set = &data->scene_temp;
                return;
        }
 
        /* Look for an object in the next set. */
        while ((data->iter.set = data->iter.set->set)) {
                ViewLayer *view_layer = BKE_view_layer_from_scene_get(data->iter.set);
-
-               Base base_iter = {(Base *)view_layer->object_bases.first, NULL};
-               data->iter.base = &base_iter;
-
-               BKE_renderable_objects_iterator_next(iter);
+               data->base_temp.next = view_layer->object_bases.first;
+               data->iter.base = &data->base_temp;
                return;
        }