diff options
author | Dalai Felinto <dfelinto@gmail.com> | 2018-02-02 02:11:59 +0300 |
---|---|---|
committer | Dalai Felinto <dfelinto@gmail.com> | 2018-02-02 17:25:05 +0300 |
commit | c7c070c2ece0f41772cbcfe3445f3e0be684dd1d (patch) | |
tree | 28f43537045a31a7a7b5c3d75d35035a73277de0 /source/blender/blenkernel/intern | |
parent | a4d2b102f313b8d427e6ff6066f38cc3a2394628 (diff) |
Collections: Operator to duplicate a collection
When duplicating a layer collection directly linked to the view layer we copy
the collection and link it.
For all the not directly linked layer collectionns, we try to sync the layer
collection flags, overrides, ...
Also we make sure the new collection is right after the original collection.
We also expose this in RNA, via collection.duplicate().
Diffstat (limited to 'source/blender/blenkernel/intern')
-rw-r--r-- | source/blender/blenkernel/intern/collection.c | 55 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/layer.c | 164 |
2 files changed, 202 insertions, 17 deletions
diff --git a/source/blender/blenkernel/intern/collection.c b/source/blender/blenkernel/intern/collection.c index ca77969ccaa..729be02bfc7 100644 --- a/source/blender/blenkernel/intern/collection.c +++ b/source/blender/blenkernel/intern/collection.c @@ -68,10 +68,9 @@ static SceneCollection *collection_master_from_id(const ID *owner_id) } /** - * Add a collection to a collection ListBase and syncronize all render layers - * The ListBase is NULL when the collection is to be added to the master collection + * Add a new collection, but don't handle syncing with layer collections */ -SceneCollection *BKE_collection_add(ID *owner_id, SceneCollection *sc_parent, const int type, const char *name_custom) +static SceneCollection *collection_add(ID *owner_id, SceneCollection *sc_parent, const int type, const char *name_custom) { SceneCollection *sc_master = collection_master_from_id(owner_id); SceneCollection *sc = MEM_callocN(sizeof(SceneCollection), "New Collection"); @@ -99,8 +98,6 @@ SceneCollection *BKE_collection_add(ID *owner_id, SceneCollection *sc_parent, co BLI_addtail(&sc_parent->scene_collections, sc); BKE_collection_rename((Scene *)owner_id, sc, name); - BKE_layer_sync_new_scene_collection(owner_id, sc_parent, sc); - if (name != name_custom) { MEM_freeN((char *)name); } @@ -109,6 +106,17 @@ SceneCollection *BKE_collection_add(ID *owner_id, SceneCollection *sc_parent, co } /** + * Add a collection to a collection ListBase and syncronize all render layers + * The ListBase is NULL when the collection is to be added to the master collection + */ +SceneCollection *BKE_collection_add(ID *owner_id, SceneCollection *sc_parent, const int type, const char *name_custom) +{ + SceneCollection *scene_collection = collection_add(owner_id, sc_parent, type, name_custom); + BKE_layer_sync_new_scene_collection(owner_id, sc_parent, scene_collection); + return scene_collection; +} + +/** * Free the collection items recursively */ static void collection_free(SceneCollection *sc, const bool do_id_user) @@ -270,6 +278,43 @@ void BKE_collection_copy_data(SceneCollection *sc_dst, SceneCollection *sc_src, } } +/** + * Makes a shallow copy of a SceneCollection + * + * Add a new collection in the same level as the old one, copy any nested collections + * but link the objects to the new collection (as oppose to copy them). + */ +SceneCollection *BKE_collection_duplicate(ID *owner_id, SceneCollection *scene_collection) +{ + SceneCollection *scene_collection_master = BKE_collection_master(owner_id); + SceneCollection *scene_collection_parent = find_collection_parent(scene_collection, scene_collection_master); + + /* It's not allowed to copy the master collection. */ + if (scene_collection_master == scene_collection) { + return NULL; + } + + SceneCollection *scene_collection_new = collection_add( + owner_id, + scene_collection_parent, + scene_collection->type, + scene_collection->name); + + if (scene_collection_new != scene_collection->next) { + BLI_remlink(&scene_collection_parent->scene_collections, scene_collection_new); + BLI_insertlinkafter(&scene_collection_parent->scene_collections, scene_collection, scene_collection_new); + } + + BKE_collection_copy_data(scene_collection_new, scene_collection, 0); + BKE_layer_sync_new_scene_collection(owner_id, scene_collection_parent, scene_collection_new); + + /* Make sure every linked instance of the new collection has the same values (flags, overrides, ...) as the + * corresponding original collection. */ + BKE_layer_collection_sync_flags(owner_id, scene_collection_new, scene_collection); + + return scene_collection_new; +} + static SceneCollection *master_collection_from_id(const ID *owner_id) { switch (GS(owner_id->name)) { diff --git a/source/blender/blenkernel/intern/layer.c b/source/blender/blenkernel/intern/layer.c index 4333faea863..9807c6e4f10 100644 --- a/source/blender/blenkernel/intern/layer.c +++ b/source/blender/blenkernel/intern/layer.c @@ -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); @@ -353,27 +354,94 @@ 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) { 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 +506,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--; |