/* * ***** BEGIN GPL LICENSE BLOCK ***** * * 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. * * Contributor(s): Dalai Felinto * * ***** END GPL LICENSE BLOCK ***** */ /** \file blender/blenkernel/intern/layer.c * \ingroup bke */ #include #include "BLI_listbase.h" #include "BLI_string.h" #include "BLI_string_utf8.h" #include "BLI_string_utils.h" #include "BLT_translation.h" #include "BKE_collection.h" #include "BKE_layer.h" #include "BKE_main.h" #include "BKE_node.h" #include "DNA_ID.h" #include "DNA_layer_types.h" #include "DNA_object_types.h" #include "DNA_node_types.h" #include "DNA_scene_types.h" #include "DRW_engine.h" #include "MEM_guardedalloc.h" /* prototype */ struct CollectionEngineSettingsCB_Type; static void layer_collection_free(SceneLayer *sl, LayerCollection *lc); static LayerCollection *layer_collection_add(SceneLayer *sl, ListBase *lb, SceneCollection *sc); static LayerCollection *find_layer_collection_by_scene_collection(LayerCollection *lc, const SceneCollection *sc); static CollectionEngineSettings *collection_engine_settings_create(struct CollectionEngineSettingsCB_Type *ces_type); static void layer_collection_engine_settings_free(LayerCollection *lc); static void layer_collection_create_engine_settings(LayerCollection *lc); static void layer_collection_create_mode_settings(LayerCollection *lc); static void scene_layer_engine_settings_update(SceneLayer *sl, Object *ob); static void object_bases_Iterator_next(Iterator *iter, const int flag); /* RenderLayer */ /** * Returns the SceneLayer to be used for rendering * Most of the time BKE_scene_layer_context_active should be used instead */ SceneLayer *BKE_scene_layer_render_active(const Scene *scene) { SceneLayer *sl = BLI_findlink(&scene->render_layers, scene->active_layer); BLI_assert(sl); return sl; } /** * Returns the SceneLayer to be used for drawing, outliner, and * other context related areas. */ SceneLayer *BKE_scene_layer_context_active(Scene *scene) { /* waiting for workspace to get the layer from context*/ TODO_LAYER_CONTEXT; return BKE_scene_layer_render_active(scene); } /** * Add a new renderlayer * by default, a renderlayer has the master collection */ SceneLayer *BKE_scene_layer_add(Scene *scene, const char *name) { if (!name) { name = DATA_("Render Layer"); } SceneLayer *sl = MEM_callocN(sizeof(SceneLayer), "Scene Layer"); sl->flag |= SCENE_LAYER_RENDER; BLI_addtail(&scene->render_layers, sl); /* unique name */ BLI_strncpy_utf8(sl->name, name, sizeof(sl->name)); BLI_uniquename(&scene->render_layers, sl, DATA_("SceneLayer"), '.', offsetof(SceneLayer, name), sizeof(sl->name)); SceneCollection *sc = BKE_collection_master(scene); layer_collection_add(sl, &sl->layer_collections, sc); return sl; } bool BKE_scene_layer_remove(Main *bmain, Scene *scene, SceneLayer *sl) { const int act = BLI_findindex(&scene->render_layers, sl); if (act == -1) { return false; } else if ( (scene->render_layers.first == scene->render_layers.last) && (scene->render_layers.first == sl)) { /* ensure 1 layer is kept */ return false; } BLI_remlink(&scene->render_layers, sl); BKE_scene_layer_free(sl); MEM_freeN(sl); scene->active_layer = 0; /* TODO WORKSPACE: set active_layer to 0 */ for (Scene *sce = bmain->scene.first; sce; sce = sce->id.next) { if (sce->nodetree) { BKE_nodetree_remove_layer_n(sce->nodetree, scene, act); } } return true; } /** * Free (or release) any data used by this SceneLayer (does not free the SceneLayer itself). */ void BKE_scene_layer_free(SceneLayer *sl) { sl->basact = NULL; BLI_freelistN(&sl->object_bases); for (LayerCollection *lc = sl->layer_collections.first; lc; lc = lc->next) { layer_collection_free(NULL, lc); } BLI_freelistN(&sl->layer_collections); } /** * Set the render engine of a renderlayer */ void BKE_scene_layer_engine_set(SceneLayer *sl, const char *engine) { BLI_strncpy_utf8(sl->engine, engine, sizeof(sl->engine)); } /** * Tag all the selected objects of a renderlayer */ void BKE_scene_layer_selected_objects_tag(SceneLayer *sl, const int tag) { for (Base *base = sl->object_bases.first; base; base = base->next) { if ((base->flag & BASE_SELECTED) != 0) { base->object->flag |= tag; } else { base->object->flag &= ~tag; } } } static bool find_scene_collection_in_scene_collections(ListBase *lb, const LayerCollection *lc) { for (LayerCollection *lcn = lb->first; lcn; lcn = lcn->next) { if (lcn == lc) { return true; } if (find_scene_collection_in_scene_collections(&lcn->layer_collections, lc)) { return true; } } return false; } /** * Find the SceneLayer a LayerCollection belongs to */ SceneLayer *BKE_scene_layer_find_from_collection(Scene *scene, LayerCollection *lc) { for (SceneLayer *sl = scene->render_layers.first; sl; sl = sl->next) { if (find_scene_collection_in_scene_collections(&sl->layer_collections, lc)) { return sl; } } return NULL; } /* Base */ Base *BKE_scene_layer_base_find(SceneLayer *sl, Object *ob) { return BLI_findptr(&sl->object_bases, ob, offsetof(Base, object)); } void BKE_scene_layer_base_deselect_all(SceneLayer *sl) { Base *base; for (base = sl->object_bases.first; base; base = base->next) { base->flag &= ~BASE_SELECTED; } } void BKE_scene_layer_base_select(struct SceneLayer *sl, Base *selbase) { sl->basact = selbase; if ((selbase->flag & BASE_SELECTABLED) != 0) { selbase->flag |= BASE_SELECTED; } } static void scene_layer_object_base_unref(SceneLayer *sl, Base *base) { base->refcount--; /* It only exists in the RenderLayer */ if (base->refcount == 0) { if (sl->basact == base) { sl->basact = NULL; } BLI_remlink(&sl->object_bases, base); MEM_freeN(base); } } static void layer_collection_base_flag_recalculate( LayerCollection *lc, const bool tree_is_visible, const bool tree_is_selectable) { bool is_visible = tree_is_visible && ((lc->flag & COLLECTION_VISIBLE) != 0); /* an object can only be selected if it's visible */ bool is_selectable = tree_is_selectable && is_visible && ((lc->flag & COLLECTION_SELECTABLE) != 0); for (LinkData *link = lc->object_bases.first; link; link = link->next) { Base *base = link->data; if (is_visible) { base->flag |= BASE_VISIBLED; } if (is_selectable) { base->flag |= BASE_SELECTABLED; } } for (LayerCollection *lcn = lc->layer_collections.first; lcn; lcn = lcn->next) { layer_collection_base_flag_recalculate(lcn, is_visible, is_selectable); } } /** * Re-evaluate the ObjectBase flags for SceneLayer */ void BKE_scene_layer_base_flag_recalculate(SceneLayer *sl) { for (Base *base = sl->object_bases.first; base; base = base->next) { base->flag &= ~(BASE_VISIBLED | BASE_SELECTABLED); } for (LayerCollection *lc = sl->layer_collections.first; lc; lc = lc->next) { layer_collection_base_flag_recalculate(lc, true, true); } /* if base is not selectabled, clear select */ for (Base *base = sl->object_bases.first; base; base = base->next) { if ((base->flag & BASE_SELECTABLED) == 0) { base->flag &= ~BASE_SELECTED; } } } /** * Tag Scene Layer to recalculation * * Temporary function, waiting for real depsgraph */ void BKE_scene_layer_engine_settings_recalculate(SceneLayer *sl) { sl->flag |= SCENE_LAYER_ENGINE_DIRTY; for (Base *base = sl->object_bases.first; base; base = base->next) { base->flag |= BASE_DIRTY_ENGINE_SETTINGS; } } /** * Tag Object in SceneLayer to recalculation * * Temporary function, waiting for real depsgraph */ void BKE_scene_layer_engine_settings_object_recalculate(SceneLayer *sl, Object *ob) { Base *base = BLI_findptr(&sl->object_bases, ob, offsetof(Base, object)); if (base) { sl->flag |= SCENE_LAYER_ENGINE_DIRTY; base->flag |= BASE_DIRTY_ENGINE_SETTINGS; } } /** * Tag all Objects in LayerCollection to recalculation * * Temporary function, waiting for real depsgraph */ void BKE_scene_layer_engine_settings_collection_recalculate(SceneLayer *sl, LayerCollection *lc) { sl->flag |= SCENE_LAYER_ENGINE_DIRTY; for (LinkData *link = lc->object_bases.first; link; link = link->next) { Base *base = (Base *)link->data; base->flag |= BASE_DIRTY_ENGINE_SETTINGS; } for (LayerCollection *lcn = lc->layer_collections.first; lcn; lcn = lcn->next) { BKE_scene_layer_engine_settings_collection_recalculate(sl, lcn); } } /** * Re-calculate the engine settings for all the objects in SceneLayer * * Temporary function, waiting for real depsgraph */ void BKE_scene_layer_engine_settings_update(struct SceneLayer *sl) { if ((sl->flag & SCENE_LAYER_ENGINE_DIRTY) == 0) { return; } /* do the complete settings update */ for (Base *base = sl->object_bases.first; base; base = base->next) { if (((base->flag & BASE_DIRTY_ENGINE_SETTINGS) != 0) && \ (base->flag & BASE_VISIBLED) != 0) { scene_layer_engine_settings_update(sl, base->object); base->flag &= ~BASE_DIRTY_ENGINE_SETTINGS; } } sl->flag &= ~SCENE_LAYER_ENGINE_DIRTY; } /** * Return the base if existent, or create it if necessary * Always bump the refcount */ static Base *object_base_add(SceneLayer *sl, Object *ob) { Base *base; base = BKE_scene_layer_base_find(sl, ob); if (base == NULL) { base = MEM_callocN(sizeof(Base), "Object Base"); /* do not bump user count, leave it for SceneCollections */ base->object = ob; BLI_addtail(&sl->object_bases, base); } base->refcount++; return base; } /* LayerCollection */ /** * When freeing the entire SceneLayer at once we don't bother with unref * otherwise SceneLayer is passed to keep the syncing of the LayerCollection tree */ static void layer_collection_free(SceneLayer *sl, LayerCollection *lc) { if (sl) { for (LinkData *link = lc->object_bases.first; link; link = link->next) { scene_layer_object_base_unref(sl, link->data); } } BLI_freelistN(&lc->object_bases); BLI_freelistN(&lc->overrides); layer_collection_engine_settings_free(lc); for (LayerCollection *nlc = lc->layer_collections.first; nlc; nlc = nlc->next) { layer_collection_free(sl, nlc); } BLI_freelistN(&lc->layer_collections); } /** * Free (or release) LayerCollection from SceneLayer * (does not free the LayerCollection itself). */ void BKE_layer_collection_free(SceneLayer *sl, LayerCollection *lc) { layer_collection_free(sl, lc); } /* LayerCollection */ /** * Recursively get the collection for a given index */ static LayerCollection *collection_from_index(ListBase *lb, const int number, int *i) { for (LayerCollection *lc = lb->first; lc; lc = lc->next) { if (*i == number) { return lc; } (*i)++; LayerCollection *lc_nested = collection_from_index(&lc->layer_collections, number, i); if (lc_nested) { return lc_nested; } } return NULL; } /** * Get the active collection */ LayerCollection *BKE_layer_collection_active(SceneLayer *sl) { int i = 0; return collection_from_index(&sl->layer_collections, sl->active_collection, &i); } /** * Recursively get the count of collections */ static int collection_count(ListBase *lb) { int i = 0; for (LayerCollection *lc = lb->first; lc; lc = lc->next) { i += collection_count(&lc->layer_collections) + 1; } return i; } /** * Get the total number of collections * (including all the nested collections) */ int BKE_layer_collection_count(SceneLayer *sl) { return collection_count(&sl->layer_collections); } /** * Recursively get the index for a given collection */ static int index_from_collection(ListBase *lb, LayerCollection *lc, int *i) { for (LayerCollection *lcol = lb->first; lcol; lcol = lcol->next) { if (lcol == lc) { return *i; } (*i)++; int i_nested = index_from_collection(&lcol->layer_collections, lc, i); if (i_nested != -1) { return i_nested; } } return -1; } /** * Return -1 if not found */ int BKE_layer_collection_findindex(SceneLayer *sl, LayerCollection *lc) { int i = 0; return index_from_collection(&sl->layer_collections, lc, &i); } /** * Lookup the listbase that contains \a lc. */ static ListBase *layer_collection_listbase_find(ListBase *lb, LayerCollection *lc) { for (LayerCollection *lc_iter = lb->first; lc_iter; lc_iter = lc_iter->next) { if (lc_iter == lc) { return lb; } ListBase *lb_child_result; if ((lb_child_result = layer_collection_listbase_find(&lc_iter->layer_collections, lc))) { return lb_child_result; } } return NULL; } /** * Lookup the listbase that contains \a sc. */ static ListBase *scene_collection_listbase_find(ListBase *lb, SceneCollection *sc) { for (SceneCollection *sc_iter = lb->first; sc_iter; sc_iter = sc_iter->next) { if (sc_iter == sc) { return lb; } ListBase *lb_child_result; if ((lb_child_result = scene_collection_listbase_find(&sc_iter->scene_collections, sc))) { return lb_child_result; } } return NULL; } /** * Move \a lc_reinsert so that it follows \a lc_after. Both have to be stored in \a sl. * \param lc_after: Can be NULL to reinsert \a lc_after as first collection of its own list. */ void BKE_layer_collection_reinsert_after( const Scene *scene, SceneLayer *sl, LayerCollection *lc_reinsert, LayerCollection *lc_after) { SceneCollection *sc_master = BKE_collection_master(scene); SceneCollection *sc_reinsert = lc_reinsert->scene_collection; ListBase *lc_reinsert_lb = layer_collection_listbase_find(&sl->layer_collections, lc_reinsert); ListBase *sc_reinsert_lb = scene_collection_listbase_find(&sc_master->scene_collections, sc_reinsert); BLI_assert(BLI_findindex(lc_reinsert_lb, lc_reinsert) > -1); BLI_assert(BLI_findindex(sc_reinsert_lb, sc_reinsert) > -1); BLI_remlink(lc_reinsert_lb, lc_reinsert); BLI_remlink(sc_reinsert_lb, sc_reinsert); /* insert after lc_after or */ if (lc_after == NULL) { BLI_addhead(lc_reinsert_lb, lc_reinsert); BLI_addhead(sc_reinsert_lb, sc_reinsert); } else { SceneCollection *sc_after = lc_after->scene_collection; ListBase *lc_after_lb = layer_collection_listbase_find(&sl->layer_collections, lc_after); ListBase *sc_after_lb = scene_collection_listbase_find(&sc_master->scene_collections, sc_after); BLI_insertlinkafter(lc_after_lb, lc_after, lc_reinsert); BLI_insertlinkafter(sc_after_lb, sc_after, sc_reinsert); } BKE_scene_layer_base_flag_recalculate(sl); BKE_scene_layer_engine_settings_collection_recalculate(sl, lc_reinsert); } /** * Link a collection to a renderlayer * The collection needs to be created separately */ LayerCollection *BKE_collection_link(SceneLayer *sl, SceneCollection *sc) { LayerCollection *lc = layer_collection_add(sl, &sl->layer_collections, sc); sl->active_collection = BKE_layer_collection_findindex(sl, lc); return lc; } /** * Unlink a collection base from a renderlayer * The corresponding collection is not removed from the master collection */ void BKE_collection_unlink(SceneLayer *sl, LayerCollection *lc) { BKE_layer_collection_free(sl, lc); BKE_scene_layer_base_flag_recalculate(sl); BKE_scene_layer_engine_settings_collection_recalculate(sl, lc); BLI_remlink(&sl->layer_collections, lc); MEM_freeN(lc); sl->active_collection = 0; } static void layer_collection_object_add(SceneLayer *sl, LayerCollection *lc, Object *ob) { Base *base = object_base_add(sl, ob); /* only add an object once - prevent SceneCollection->objects and * SceneCollection->filter_objects to add the same object */ if (BLI_findptr(&lc->object_bases, base, offsetof(LinkData, data))) { return; } BLI_addtail(&lc->object_bases, BLI_genericNodeN(base)); BKE_scene_layer_base_flag_recalculate(sl); BKE_scene_layer_engine_settings_object_recalculate(sl, ob); } static void layer_collection_object_remove(SceneLayer *sl, LayerCollection *lc, Object *ob) { Base *base; base = BKE_scene_layer_base_find(sl, ob); LinkData *link = BLI_findptr(&lc->object_bases, base, offsetof(LinkData, data)); BLI_remlink(&lc->object_bases, link); MEM_freeN(link); scene_layer_object_base_unref(sl, base); } static void layer_collection_objects_populate(SceneLayer *sl, LayerCollection *lc, ListBase *objects) { for (LinkData *link = objects->first; link; link = link->next) { layer_collection_object_add(sl, lc, link->data); } } static void layer_collection_populate(SceneLayer *sl, LayerCollection *lc, SceneCollection *sc) { layer_collection_objects_populate(sl, lc, &sc->objects); layer_collection_objects_populate(sl, lc, &sc->filter_objects); for (SceneCollection *nsc = sc->scene_collections.first; nsc; nsc = nsc->next) { layer_collection_add(sl, &lc->layer_collections, nsc); } } static LayerCollection *layer_collection_add(SceneLayer *sl, ListBase *lb, SceneCollection *sc) { LayerCollection *lc = MEM_callocN(sizeof(LayerCollection), "Collection Base"); BLI_addtail(lb, lc); lc->scene_collection = sc; lc->flag = COLLECTION_VISIBLE + COLLECTION_SELECTABLE + COLLECTION_FOLDED; layer_collection_create_engine_settings(lc); layer_collection_create_mode_settings(lc); layer_collection_populate(sl, lc, sc); return lc; } /* ---------------------------------------------------------------------- */ /** * See if render layer has the scene collection linked directly, or indirectly (nested) */ bool BKE_scene_layer_has_collection(struct SceneLayer *sl, struct SceneCollection *sc) { for (LayerCollection *lc = sl->layer_collections.first; lc; lc = lc->next) { if (find_layer_collection_by_scene_collection(lc, sc) != NULL) { return true; } } return false; } /** * See if the object is in any of the scene layers of the scene */ bool BKE_scene_has_object(Scene *scene, Object *ob) { for (SceneLayer *sl = scene->render_layers.first; sl; sl = sl->next) { Base *base = BKE_scene_layer_base_find(sl, ob); if (base) { return true; } } return false; } /* ---------------------------------------------------------------------- */ /* Syncing */ static LayerCollection *find_layer_collection_by_scene_collection(LayerCollection *lc, const SceneCollection *sc) { if (lc->scene_collection == sc) { return lc; } for (LayerCollection *nlc = lc->layer_collections.first; nlc; nlc = nlc->next) { LayerCollection *found = find_layer_collection_by_scene_collection(nlc, sc); if (found) { return found; } } return NULL; } /** * Add a new LayerCollection for all the SceneLayers that have sc_parent */ void BKE_layer_sync_new_scene_collection(Scene *scene, const SceneCollection *sc_parent, SceneCollection *sc) { for (SceneLayer *sl = scene->render_layers.first; sl; sl = sl->next) { for (LayerCollection *lc = sl->layer_collections.first; lc; lc = lc->next) { LayerCollection *lc_parent = find_layer_collection_by_scene_collection(lc, sc_parent); if (lc_parent) { layer_collection_add(sl, &lc_parent->layer_collections, sc); } } } } /** * Add a corresponding ObjectBase to all the equivalent LayerCollection */ void BKE_layer_sync_object_link(Scene *scene, SceneCollection *sc, Object *ob) { for (SceneLayer *sl = scene->render_layers.first; sl; sl = sl->next) { for (LayerCollection *lc = sl->layer_collections.first; lc; lc = lc->next) { LayerCollection *found = find_layer_collection_by_scene_collection(lc, sc); if (found) { layer_collection_object_add(sl, found, ob); } } } } /** * Remove the equivalent object base to all layers that have this collection * also remove all reference to ob in the filter_objects */ void BKE_layer_sync_object_unlink(Scene *scene, SceneCollection *sc, Object *ob) { for (SceneLayer *sl = scene->render_layers.first; sl; sl = sl->next) { for (LayerCollection *lc = sl->layer_collections.first; lc; lc = lc->next) { LayerCollection *found = find_layer_collection_by_scene_collection(lc, sc); if (found) { layer_collection_object_remove(sl, found, ob); } } BKE_scene_layer_base_flag_recalculate(sl); BKE_scene_layer_engine_settings_object_recalculate(sl, ob); } } /* ---------------------------------------------------------------------- */ /* Override */ /** * Add a new datablock override */ void BKE_collection_override_datablock_add(LayerCollection *UNUSED(lc), const char *UNUSED(data_path), ID *UNUSED(id)) { TODO_LAYER_OVERRIDE; } /* ---------------------------------------------------------------------- */ /* Engine Settings */ ListBase R_engines_settings_callbacks = {NULL, NULL}; typedef struct CollectionEngineSettingsCB_Type { struct CollectionEngineSettingsCB_Type *next, *prev; char name[MAX_NAME]; /* engine name */ CollectionEngineSettingsCB callback; } CollectionEngineSettingsCB_Type; static void create_engine_settings_layer_collection(LayerCollection *lc, CollectionEngineSettingsCB_Type *ces_type) { if (BKE_layer_collection_engine_get(lc, COLLECTION_MODE_NONE, ces_type->name)) { return; } CollectionEngineSettings *ces = collection_engine_settings_create(ces_type); BLI_addtail(&lc->engine_settings, ces); for (LayerCollection *lcn = lc->layer_collections.first; lcn; lcn = lcn->next) { create_engine_settings_layer_collection(lcn, ces_type); } } static void create_engines_settings_scene(Scene *scene, CollectionEngineSettingsCB_Type *ces_type) { for (SceneLayer *sl = scene->render_layers.first; sl; sl = sl->next) { for (LayerCollection *lc = sl->layer_collections.first; lc; lc = lc->next) { create_engine_settings_layer_collection(lc, ces_type); } } } void BKE_layer_collection_engine_settings_callback_register( Main *bmain, const char *engine_name, CollectionEngineSettingsCB func) { CollectionEngineSettingsCB_Type *ces_type; /* cleanup in case it existed */ ces_type = BLI_findstring(&R_engines_settings_callbacks, engine_name, offsetof(CollectionEngineSettingsCB_Type, name)); if (ces_type) { BLI_remlink(&R_engines_settings_callbacks, ces_type); MEM_freeN(ces_type); } ces_type = MEM_callocN(sizeof(CollectionEngineSettingsCB_Type), "collection_engine_type"); BLI_strncpy_utf8(ces_type->name, engine_name, sizeof(ces_type->name)); ces_type->callback = func; BLI_addtail(&R_engines_settings_callbacks, ces_type); if (bmain) { /* populate all of the collections of the scene with those settings */ for (Scene *scene = bmain->scene.first; scene; scene = scene->id.next) { create_engines_settings_scene(scene, ces_type); } } } void BKE_layer_collection_engine_settings_callback_free(void) { BLI_freelistN(&R_engines_settings_callbacks); } static CollectionEngineSettings *collection_engine_settings_create(CollectionEngineSettingsCB_Type *ces_type) { /* create callback data */ CollectionEngineSettings *ces = MEM_callocN(sizeof(CollectionEngineSettings), "Collection Engine Settings"); BLI_strncpy_utf8(ces->name, ces_type->name, sizeof(ces->name)); /* call callback */ ces_type->callback(NULL, ces); return ces; } /** * Initialize a CollectionEngineSettings * * Usually we would pass LayerCollection->engine_settings * But depsgraph uses this for Object->collection_settings */ CollectionEngineSettings *BKE_layer_collection_engine_settings_create(const char *engine_name) { CollectionEngineSettingsCB_Type *ces_type; ces_type = BLI_findstring(&R_engines_settings_callbacks, engine_name, offsetof(CollectionEngineSettingsCB_Type, name)); BLI_assert(ces_type); CollectionEngineSettings *ces = collection_engine_settings_create(ces_type); return ces; } /** * Free the CollectionEngineSettings */ void BKE_layer_collection_engine_settings_free(CollectionEngineSettings *ces) { BLI_freelistN(&ces->properties); } static void layer_collection_engine_settings_free(LayerCollection *lc) { for (CollectionEngineSettings *ces = lc->engine_settings.first; ces; ces = ces->next) { BKE_layer_collection_engine_settings_free(ces); } BLI_freelistN(&lc->engine_settings); for (CollectionEngineSettings *ces = lc->mode_settings.first; ces; ces = ces->next) { BKE_layer_collection_engine_settings_free(ces); } BLI_freelistN(&lc->mode_settings); } /** * Initialize the render settings for a single LayerCollection */ static void layer_collection_create_engine_settings(LayerCollection *lc) { CollectionEngineSettingsCB_Type *ces_type; for (ces_type = R_engines_settings_callbacks.first; ces_type; ces_type = ces_type->next) { create_engine_settings_layer_collection(lc, ces_type); } } static void layer_collection_create_mode_settings_object(ListBase *lb) { CollectionEngineSettings *ces; ces = MEM_callocN(sizeof(CollectionEngineSettings), "Object Mode Settings"); BLI_strncpy_utf8(ces->name, "Object Mode", sizeof(ces->name)); ces->type = COLLECTION_MODE_OBJECT; /* properties */ OBJECT_collection_settings_create(ces); BLI_addtail(lb, ces); } static void layer_collection_create_mode_settings_edit(ListBase *lb) { CollectionEngineSettings *ces; ces = MEM_callocN(sizeof(CollectionEngineSettings), "Edit Mode Settings"); BLI_strncpy_utf8(ces->name, "Edit Mode", sizeof(ces->name)); ces->type = COLLECTION_MODE_EDIT; /* properties */ EDIT_MESH_collection_settings_create(ces); BLI_addtail(lb, ces); } static void collection_create_mode_settings(ListBase *lb) { layer_collection_create_mode_settings_object(lb); layer_collection_create_mode_settings_edit(lb); } static void layer_collection_create_mode_settings(LayerCollection *lc) { collection_create_mode_settings(&lc->mode_settings); } /** * Return collection enginne settings for either Object s of LayerCollection s */ static CollectionEngineSettings *collection_engine_get( ListBase *lb_render, ListBase *lb_mode, const int type, const char *engine_name) { if (type == COLLECTION_MODE_NONE) { return BLI_findstring(lb_render, engine_name, offsetof(CollectionEngineSettings, name)); } else { CollectionEngineSettings *ces; for (ces = lb_mode->first; ces; ces = ces->next) { if (ces->type == type) { return ces; } } } BLI_assert(false); return NULL; } /** * Return collection engine settings from Object for specified engine of mode */ CollectionEngineSettings *BKE_object_collection_engine_get(Object *ob, const int type, const char *engine_name) { return collection_engine_get(&ob->collection_settings, &ob->collection_settings, type, engine_name); } /** * Return layer collection engine settings for specified engine */ CollectionEngineSettings *BKE_layer_collection_engine_get(LayerCollection *lc, const int type, const char *engine_name) { return collection_engine_get(&lc->engine_settings, &lc->mode_settings, type, engine_name); } /* ---------------------------------------------------------------------- */ /* Engine Settings Properties */ void BKE_collection_engine_property_add_float(CollectionEngineSettings *ces, const char *name, float value) { CollectionEnginePropertyFloat *prop; prop = MEM_callocN(sizeof(CollectionEnginePropertyFloat), "collection engine settings float"); prop->data.type = COLLECTION_PROP_TYPE_FLOAT; BLI_strncpy_utf8(prop->data.name, name, sizeof(prop->data.name)); prop->value = value; BLI_addtail(&ces->properties, prop); } void BKE_collection_engine_property_add_int(CollectionEngineSettings *ces, const char *name, int value) { CollectionEnginePropertyInt *prop; prop = MEM_callocN(sizeof(CollectionEnginePropertyInt), "collection engine settings int"); prop->data.type = COLLECTION_PROP_TYPE_INT; BLI_strncpy_utf8(prop->data.name, name, sizeof(prop->data.name)); prop->value = value; BLI_addtail(&ces->properties, prop); } void BKE_collection_engine_property_add_bool(CollectionEngineSettings *ces, const char *name, bool value) { CollectionEnginePropertyBool *prop; prop = MEM_callocN(sizeof(CollectionEnginePropertyBool), "collection engine settings bool"); prop->data.type = COLLECTION_PROP_TYPE_BOOL; BLI_strncpy_utf8(prop->data.name, name, sizeof(prop->data.name)); prop->value = value; BLI_addtail(&ces->properties, prop); } CollectionEngineProperty *BKE_collection_engine_property_get(CollectionEngineSettings *ces, const char *name) { return BLI_findstring(&ces->properties, name, offsetof(CollectionEngineProperty, name)); } int BKE_collection_engine_property_value_get_int(CollectionEngineSettings *ces, const char *name) { CollectionEnginePropertyInt *prop; prop = (CollectionEnginePropertyInt *)BLI_findstring(&ces->properties, name, offsetof(CollectionEngineProperty, name)); return prop->value; } float BKE_collection_engine_property_value_get_float(CollectionEngineSettings *ces, const char *name) { CollectionEnginePropertyFloat *prop; prop = (CollectionEnginePropertyFloat *)BLI_findstring(&ces->properties, name, offsetof(CollectionEngineProperty, name)); return prop->value; } bool BKE_collection_engine_property_value_get_bool(CollectionEngineSettings *ces, const char *name) { CollectionEnginePropertyBool *prop; prop = (CollectionEnginePropertyBool *)BLI_findstring(&ces->properties, name, offsetof(CollectionEngineProperty, name)); return prop->value; } void BKE_collection_engine_property_value_set_int(CollectionEngineSettings *ces, const char *name, int value) { CollectionEnginePropertyInt *prop; prop = (CollectionEnginePropertyInt *)BLI_findstring(&ces->properties, name, offsetof(CollectionEngineProperty, name)); prop->value = value; prop->data.flag |= COLLECTION_PROP_USE; } void BKE_collection_engine_property_value_set_float(CollectionEngineSettings *ces, const char *name, float value) { CollectionEnginePropertyFloat *prop; prop = (CollectionEnginePropertyFloat *)BLI_findstring(&ces->properties, name, offsetof(CollectionEngineProperty, name)); prop->value = value; prop->data.flag |= COLLECTION_PROP_USE; } void BKE_collection_engine_property_value_set_bool(CollectionEngineSettings *ces, const char *name, bool value) { CollectionEnginePropertyBool *prop; prop = (CollectionEnginePropertyBool *)BLI_findstring(&ces->properties, name, offsetof(CollectionEngineProperty, name)); prop->value = value; prop->data.flag |= COLLECTION_PROP_USE; } bool BKE_collection_engine_property_use_get(CollectionEngineSettings *ces, const char *name) { CollectionEngineProperty *prop; prop = (CollectionEngineProperty *)BLI_findstring(&ces->properties, name, offsetof(CollectionEngineProperty, name)); return ((prop->flag & COLLECTION_PROP_USE) != 0); } void BKE_collection_engine_property_use_set(CollectionEngineSettings *ces, const char *name, bool value) { CollectionEngineProperty *prop; prop = (CollectionEngineProperty *)BLI_findstring(&ces->properties, name, offsetof(CollectionEngineProperty, name)); if (value) { prop->flag |= COLLECTION_PROP_USE; } else { prop->flag &= ~COLLECTION_PROP_USE; } } /* Engine Settings recalculate */ static void collection_engine_settings_init(ListBase *lb) { CollectionEngineSettingsCB_Type *ces_type; for (ces_type = R_engines_settings_callbacks.first; ces_type; ces_type = ces_type->next) { CollectionEngineSettings *ces = collection_engine_settings_create(ces_type); BLI_strncpy_utf8(ces->name, ces_type->name, sizeof(ces->name)); BLI_addtail(lb, ces); /* call callback */ ces_type->callback(NULL, ces); } /* edit modes */ collection_create_mode_settings(lb); } static void collection_engine_settings_copy(ListBase *lb_dst, ListBase *lb_src) { for (CollectionEngineSettings *ces_src = lb_src->first; ces_src; ces_src = ces_src->next) { CollectionEngineSettings *ces_dst = MEM_callocN(sizeof(*ces_dst), "CollectionEngineSettings copy"); BLI_strncpy_utf8(ces_dst->name, ces_src->name, sizeof(ces_dst->name)); ces_dst->type = ces_src->type; BLI_addtail(lb_dst, ces_dst); for (CollectionEngineProperty *prop = ces_src->properties.first; prop; prop = prop->next) { CollectionEngineProperty *prop_new = MEM_dupallocN(prop); BLI_addtail(&ces_dst->properties, prop_new); } } } /** * Set a value from a CollectionProperty to another */ static void collection_engine_property_set (CollectionEngineProperty *prop_dst, CollectionEngineProperty *prop_src) { if ((prop_src->flag & COLLECTION_PROP_USE) != 0) { /* mark the property as used, so the engine knows if the value was ever set*/ prop_dst->flag |= COLLECTION_PROP_USE; switch (prop_src->type) { case COLLECTION_PROP_TYPE_FLOAT: ((CollectionEnginePropertyFloat *)prop_dst)->value = ((CollectionEnginePropertyFloat *)prop_src)->value; break; case COLLECTION_PROP_TYPE_INT: ((CollectionEnginePropertyInt *)prop_dst)->value = ((CollectionEnginePropertyInt *)prop_src)->value; break; case COLLECTION_PROP_TYPE_BOOL: ((CollectionEnginePropertyBool *)prop_dst)->value = ((CollectionEnginePropertyBool *)prop_src)->value; break; default: BLI_assert(false); break; } } } static void collection_engine_settings_merge(ListBase *lb_dst, ListBase *lb_src) { for (CollectionEngineSettings *ces_src = lb_src->first; ces_src; ces_src = ces_src->next) { CollectionEngineSettings *ces_dst = collection_engine_get(lb_dst, lb_dst, ces_src->type, ces_src->name); BLI_assert(ces_dst); CollectionEngineProperty *prop_dst, *prop_src; for (prop_dst = ces_dst->properties.first; prop_dst; prop_dst = prop_dst->next) { prop_src = BLI_findstring(&ces_src->properties, prop_dst->name, offsetof(CollectionEngineProperty, name)); BLI_assert(prop_src); collection_engine_property_set(prop_dst, prop_src); } } } static void layer_collection_engine_settings_update( LayerCollection *lc, ListBase *lb_parent, Base *base, ListBase *lb_object) { if ((lc->flag & COLLECTION_VISIBLE) == 0) { return; } ListBase lb_collection = {NULL}; collection_engine_settings_copy(&lb_collection, lb_parent); collection_engine_settings_merge(&lb_collection, &lc->engine_settings); collection_engine_settings_merge(&lb_collection, &lc->mode_settings); if (BLI_findptr(&lc->object_bases, base, offsetof(LinkData, data)) != NULL) { collection_engine_settings_merge(lb_object, &lb_collection); } /* do it recursively */ for (LayerCollection *lcn = lc->layer_collections.first; lcn; lcn = lcn->next) { layer_collection_engine_settings_update(lcn, &lb_collection, base, lb_object); } BKE_layer_collection_engine_settings_list_free(&lb_collection); } /** * Empty all the CollectionEngineSettings in the list */ void BKE_layer_collection_engine_settings_list_free(struct ListBase *lb) { for (CollectionEngineSettings *ces = lb->first; ces; ces = ces->next) { BKE_layer_collection_engine_settings_free(ces); } BLI_freelistN(lb); } /** * Update the collection settings pointer allocated in the object * This is to be flushed from the Depsgraph */ static void scene_layer_engine_settings_update(SceneLayer *sl, Object *ob) { Base *base = BKE_scene_layer_base_find(sl, ob); ListBase ces_layer = {NULL}; collection_engine_settings_init(&ces_layer); /* start fresh */ BKE_layer_collection_engine_settings_list_free(&ob->collection_settings); collection_engine_settings_init(&ob->collection_settings); for (LayerCollection *lc = sl->layer_collections.first; lc; lc = lc->next) { layer_collection_engine_settings_update(lc, &ces_layer, base, &ob->collection_settings); } BKE_layer_collection_engine_settings_list_free(&ces_layer); } /* ---------------------------------------------------------------------- */ /* Iterators */ static void object_bases_Iterator_begin(Iterator *iter, void *data_in, const int flag) { SceneLayer *sl = data_in; Base *base = sl->object_bases.first; /* when there are no objects */ if (base == NULL) { iter->valid = false; return; } iter->valid = true; iter->data = base; if ((base->flag & flag) == 0) { object_bases_Iterator_next(iter, flag); } else { iter->current = base; } } static void object_bases_Iterator_next(Iterator *iter, const int flag) { Base *base = ((Base *)iter->data)->next; while (base) { if ((base->flag & flag) != 0) { iter->current = base; iter->data = base; return; } base = base->next; } iter->current = NULL; iter->valid = false; } static void objects_Iterator_begin(Iterator *iter, void *data_in, const int flag) { object_bases_Iterator_begin(iter, data_in, flag); if (iter->valid) { iter->current = ((Base *)iter->current)->object; } } static void objects_Iterator_next(Iterator *iter, const int flag) { object_bases_Iterator_next(iter, flag); if (iter->valid) { iter->current = ((Base *)iter->current)->object; } } void BKE_selected_objects_Iterator_begin(Iterator *iter, void *data_in) { objects_Iterator_begin(iter, data_in, BASE_SELECTED); } void BKE_selected_objects_Iterator_next(Iterator *iter) { objects_Iterator_next(iter, BASE_SELECTED); } void BKE_selected_objects_Iterator_end(Iterator *UNUSED(iter)) { /* do nothing */ } void BKE_visible_objects_Iterator_begin(Iterator *iter, void *data_in) { objects_Iterator_begin(iter, data_in, BASE_VISIBLED); } void BKE_visible_objects_Iterator_next(Iterator *iter) { objects_Iterator_next(iter, BASE_VISIBLED); } void BKE_visible_objects_Iterator_end(Iterator *UNUSED(iter)) { /* do nothing */ } void BKE_visible_bases_Iterator_begin(Iterator *iter, void *data_in) { object_bases_Iterator_begin(iter, data_in, BASE_VISIBLED); } void BKE_visible_bases_Iterator_next(Iterator *iter) { object_bases_Iterator_next(iter, BASE_VISIBLED); } void BKE_visible_bases_Iterator_end(Iterator *UNUSED(iter)) { /* do nothing */ } /* ---------------------------------------------------------------------- */ /* Doversion routine */ /** * Merge CollectionEngineSettings * * \param ces_ref CollectionEngineSettings to use as reference * \param ces CollectionEngineSettings to merge into */ static void scene_layer_doversion_merge_setings(const CollectionEngineSettings *ces_ref, CollectionEngineSettings *ces) { CollectionEngineProperty *cep = ces->properties.first, *cep_ref; for (cep_ref = ces_ref->properties.first; cep_ref; cep_ref = cep_ref->next) { cep = BLI_findstring(&ces->properties, cep_ref->name, offsetof(CollectionEngineProperty, name)); if (cep == NULL) { cep = MEM_dupallocN(cep_ref); BLI_addtail(&ces->properties, cep); } else if (cep->type != cep_ref->type) { CollectionEngineProperty *prev = cep->prev, *next = cep->next; MEM_freeN(cep); cep = MEM_dupallocN(cep_ref); cep->prev = prev; cep->next = next; } else { /* keep the property as it is */ } } } /** * Merge ListBases of LayerCollections * * \param lb_ref ListBase of CollectionEngineSettings to use as reference * \param lb ListBase of CollectionEngineSettings */ static void scene_layer_doversion_merge_layer_collection(const ListBase *lb_ref, ListBase *lb) { CollectionEngineSettings *ces = lb->first, *ces_ref; for (ces_ref = lb_ref->first; ces_ref; ces_ref = ces_ref->next) { ces = BLI_findstring(lb, ces_ref->name, offsetof(CollectionEngineSettings, name)); if (ces == NULL) { ces = MEM_dupallocN(ces_ref); BLI_duplicatelist(&ces->properties, &ces_ref->properties); BLI_addtail(lb, ces); } else { scene_layer_doversion_merge_setings(ces_ref, ces); } } } /** * Create or remove CollectionEngineSettings and CollectionEngineProperty * based on reference LayerCollection * * \param lc_ref reference LayerCollection to merge missing settings from * \param lb ListBase of LayerCollection */ static void scene_layer_doversion_update_collections(const LayerCollection *lc_ref, ListBase *lb) { for (LayerCollection *lc = lb->first; lc; lc = lc->next) { scene_layer_doversion_merge_layer_collection(&lc_ref->engine_settings, &lc->engine_settings); scene_layer_doversion_merge_layer_collection(&lc_ref->mode_settings, &lc->mode_settings); /* continue recursively */ scene_layer_doversion_update_collections(lc_ref, &lc->layer_collections); } } /** * Updates all the CollectionEngineSettings of all * LayerCollection elements in Scene * * \param lc_ref reference LayerCollection to merge missing settings from */ static void scene_layer_doversion_update(const LayerCollection *lc_ref, Scene *scene) { for (SceneLayer *sl = scene->render_layers.first; sl; sl = sl->next) { scene_layer_doversion_update_collections(lc_ref, &sl->layer_collections); } } /** * Return true at the first indicative that the listbases don't match * * It's fine if the individual properties values are different, as long * as we have the same properties across them * * \param lb_ces ListBase of CollectionEngineSettings * \param lb_ces_ref ListBase of CollectionEngineSettings */ static bool scene_layer_doversion_is_outdated_engines(ListBase *lb_ces, ListBase *lb_ces_ref) { if (BLI_listbase_count(lb_ces) != BLI_listbase_count(lb_ces_ref)) { return true; } CollectionEngineSettings *ces, *ces_ref; for (ces = lb_ces->first, ces_ref = lb_ces_ref->first; ces; ces = ces->next, ces_ref = ces_ref->next) { if (BLI_listbase_count(&ces->properties) != BLI_listbase_count(&ces_ref->properties)) { return true; } CollectionEngineProperty *cep, *cep_ref; for (cep = ces->properties.first, cep_ref = ces_ref->properties.first; cep != NULL; cep = cep->next, cep_ref = cep_ref->next) { if (cep->type != cep_ref->type) { return true; } if (STREQ(cep->name, cep_ref->name) == false) { return true; } } } return false; } /** * Get the first available LayerCollection */ static LayerCollection *scene_layer_doversion_collection_get(Main *bmain) { for (Scene *scene = bmain->scene.first; scene; scene = scene->id.next) { for (SceneLayer *sl = scene->render_layers.first; sl; sl = sl->next) { for (LayerCollection *lc = sl->layer_collections.first; lc; lc = lc->next) { return lc; } } } return NULL; } /** * See if a new LayerCollection have the same CollectionEngineSettings * and properties of the saved LayerCollection */ static bool scene_layer_doversion_is_outdated(Main *bmain) { LayerCollection *lc, lc_ref = {NULL}; bool is_outdated = false; lc = scene_layer_doversion_collection_get(bmain); if (lc == NULL) { return false; } layer_collection_create_engine_settings(&lc_ref); layer_collection_create_mode_settings(&lc_ref); if (scene_layer_doversion_is_outdated_engines(&lc->engine_settings, &lc_ref.engine_settings)) { is_outdated = true; } if (scene_layer_doversion_is_outdated_engines(&lc->mode_settings, &lc_ref.mode_settings)) { is_outdated = true; } layer_collection_engine_settings_free(&lc_ref); return is_outdated; } /** * Handle doversion of files during the viewport development * * This is intended to prevent subversion bumping every time a new property * is added to an engine, but it may be relevant in the future as a generic doversion */ void BKE_scene_layer_doversion_update(Main *bmain) { /* if file not outdated, don't bother with the slow merging */ if (scene_layer_doversion_is_outdated(bmain) == false) { return; } /* create a reference LayerCollection to merge missing settings from */ LayerCollection lc_ref = {NULL}; layer_collection_create_engine_settings(&lc_ref); layer_collection_create_mode_settings(&lc_ref); /* bring all the missing properties for the LayerCollections */ for (Scene *scene = bmain->scene.first; scene; scene = scene->id.next) { scene_layer_doversion_update(&lc_ref, scene); } layer_collection_engine_settings_free(&lc_ref); }