Merge branch 'master' into blender2.8
authorCampbell Barton <ideasman42@gmail.com>
Wed, 12 Dec 2018 01:55:20 +0000 (12:55 +1100)
committerCampbell Barton <ideasman42@gmail.com>
Wed, 12 Dec 2018 02:02:09 +0000 (13:02 +1100)
127 files changed:
1  2 
source/blender/alembic/intern/abc_exporter.cc
source/blender/alembic/intern/abc_util.cc
source/blender/alembic/intern/alembic_capi.cc
source/blender/blenkernel/BKE_modifier.h
source/blender/blenkernel/intern/action.c
source/blender/blenkernel/intern/anim.c
source/blender/blenkernel/intern/anim_sys.c
source/blender/blenkernel/intern/appdir.c
source/blender/blenkernel/intern/armature.c
source/blender/blenkernel/intern/brush.c
source/blender/blenkernel/intern/bvhutils.c
source/blender/blenkernel/intern/cachefile.c
source/blender/blenkernel/intern/camera.c
source/blender/blenkernel/intern/collection.c
source/blender/blenkernel/intern/colortools.c
source/blender/blenkernel/intern/context.c
source/blender/blenkernel/intern/curve.c
source/blender/blenkernel/intern/customdata.c
source/blender/blenkernel/intern/displist.c
source/blender/blenkernel/intern/gpencil.c
source/blender/blenkernel/intern/idcode.c
source/blender/blenkernel/intern/idprop.c
source/blender/blenkernel/intern/image.c
source/blender/blenkernel/intern/key.c
source/blender/blenkernel/intern/lamp.c
source/blender/blenkernel/intern/lattice.c
source/blender/blenkernel/intern/layer.c
source/blender/blenkernel/intern/library.c
source/blender/blenkernel/intern/library_query.c
source/blender/blenkernel/intern/lightprobe.c
source/blender/blenkernel/intern/linestyle.c
source/blender/blenkernel/intern/main.c
source/blender/blenkernel/intern/mask.c
source/blender/blenkernel/intern/material.c
source/blender/blenkernel/intern/mball.c
source/blender/blenkernel/intern/mesh.c
source/blender/blenkernel/intern/mesh_evaluate.c
source/blender/blenkernel/intern/mesh_merge.c
source/blender/blenkernel/intern/mesh_validate.c
source/blender/blenkernel/intern/modifier.c
source/blender/blenkernel/intern/movieclip.c
source/blender/blenkernel/intern/nla.c
source/blender/blenkernel/intern/node.c
source/blender/blenkernel/intern/object.c
source/blender/blenkernel/intern/object_deform.c
source/blender/blenkernel/intern/paint.c
source/blender/blenkernel/intern/particle.c
source/blender/blenkernel/intern/rigidbody.c
source/blender/blenkernel/intern/scene.c
source/blender/blenkernel/intern/sound.c
source/blender/blenkernel/intern/text.c
source/blender/blenkernel/intern/texture.c
source/blender/blenkernel/intern/world.c
source/blender/blenlib/BLI_system.h
source/blender/blenlib/intern/BLI_ghash.c
source/blender/blenlib/intern/astar.c
source/blender/blenlib/intern/edgehash.c
source/blender/blenlib/intern/freetypefont.c
source/blender/blenlib/intern/listbase.c
source/blender/blenlib/intern/math_geom.c
source/blender/blenlib/intern/math_rotation.c
source/blender/blenlib/intern/math_solvers.c
source/blender/blenlib/intern/path_util.c
source/blender/blenlib/intern/string.c
source/blender/blenlib/intern/task.c
source/blender/blenloader/intern/readblenentry.c
source/blender/blenloader/intern/readfile.c
source/blender/blenloader/intern/writefile.c
source/blender/bmesh/intern/bmesh_core.c
source/blender/bmesh/intern/bmesh_mesh_conv.c
source/blender/bmesh/intern/bmesh_mods.c
source/blender/bmesh/intern/bmesh_operators.c
source/blender/bmesh/intern/bmesh_polygon.c
source/blender/bmesh/intern/bmesh_query.c
source/blender/bmesh/tools/bmesh_decimate_collapse.c
source/blender/bmesh/tools/bmesh_intersect.c
source/blender/collada/DocumentImporter.h
source/blender/draw/engines/gpencil/gpencil_shader_fx.c
source/blender/editors/animation/anim_channels_edit.c
source/blender/editors/animation/anim_markers.c
source/blender/editors/armature/armature_utils.c
source/blender/editors/armature/pose_transform.c
source/blender/editors/gpencil/editaction_gpencil.c
source/blender/editors/gpencil/gpencil_fill.c
source/blender/editors/gpencil/gpencil_intern.h
source/blender/editors/gpencil/gpencil_paint.c
source/blender/editors/gpencil/gpencil_utils.c
source/blender/editors/interface/interface.c
source/blender/editors/interface/interface_draw.c
source/blender/editors/interface/interface_handlers.c
source/blender/editors/mesh/editmesh_knife.c
source/blender/editors/mesh/editmesh_rip.c
source/blender/editors/mesh/editmesh_select.c
source/blender/editors/mesh/editmesh_utils.c
source/blender/editors/screen/area.c
source/blender/editors/sculpt_paint/sculpt.c
source/blender/editors/space_image/image_ops.c
source/blender/editors/space_node/node_edit.c
source/blender/editors/space_outliner/outliner_collections.c
source/blender/editors/space_outliner/outliner_tree.c
source/blender/editors/space_view3d/view3d_camera_control.c
source/blender/editors/space_view3d/view3d_edit.c
source/blender/editors/space_view3d/view3d_gizmo_navigate_type.c
source/blender/editors/space_view3d/view3d_project.c
source/blender/editors/space_view3d/view3d_utils.c
source/blender/editors/space_view3d/view3d_view.c
source/blender/editors/space_view3d/view3d_walk.c
source/blender/editors/transform/transform.c
source/blender/editors/transform/transform_generics.c
source/blender/editors/util/ed_util.c
source/blender/freestyle/intern/stroke/Stroke.h
source/blender/gpu/intern/gpu_uniformbuffer.c
source/blender/makesdna/intern/dna_genfile.c
source/blender/makesdna/intern/makesdna.c
source/blender/makesrna/intern/rna_access.c
source/blender/makesrna/intern/rna_define.c
source/blender/modifiers/intern/MOD_solidify.c
source/blender/modifiers/intern/MOD_util.c
source/blender/python/bmesh/bmesh_py_ops_call.c
source/blender/python/mathutils/mathutils_Matrix.c
source/blender/windowmanager/gizmo/intern/wm_gizmo.c
source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c
source/blender/windowmanager/intern/wm.c
source/blender/windowmanager/intern/wm_cursors.c
source/blender/windowmanager/intern/wm_event_system.c
source/blender/windowmanager/intern/wm_operators.c
source/blender/windowmanager/intern/wm_window.c

@@@ -140,8 -136,8 +140,8 @@@ static bool object_type_is_exportable(S
  /**
   * Returns whether this object should be exported into the Alembic file.
   *
-  * \param settings export settings, used for options like 'selected only'.
-  * \param ob the object's base in question.
+  * \param settings: export settings, used for options like 'selected only'.
 - * \param ob: the object in question.
++ * \param ob: the object's base in question.
   * \param is_duplicated: Normally false; true when the object is instanced
   * into the scene by a dupli-object (e.g. part of a dupligroup).
   * This ignores selection and layer visibility,
@@@ -61,15 -60,6 +61,15 @@@ std::string get_id_name(const ID * cons
        return name;
  }
  
-  * @brief get_object_dag_path_name returns the name under which the object
 +/**
-  * @param ob
-  * @param dupli_parent
-  * @return
++ * \brief get_object_dag_path_name returns the name under which the object
 + *  will be exported in the Alembic file. It is of the form
 + *  "[../grandparent/]parent/object" if dupli_parent is NULL, or
 + *  "dupli_parent/[../grandparent/]parent/object" otherwise.
++ * \param ob:
++ * \param dupli_parent:
++ * \return
 + */
  std::string get_object_dag_path_name(const Object * const ob, Object *dupli_parent)
  {
        std::string name = get_id_name(ob);
@@@ -156,22 -159,8 +156,22 @@@ typedef struct ModifierTypeInfo 
  
        /* Copy instance data for this modifier type. Should copy all user
         * level settings to the target modifier.
-        * \param flag  Copying options (see BKE_library.h's LIB_ID_COPY_... flags for more).
 +       *
++       * \param flag: Copying options (see BKE_library.h's LIB_ID_COPY_... flags for more).
         */
 -      void (*copyData)(const struct ModifierData *md, struct ModifierData *target);
 +      void (*copyData)(const struct ModifierData *md, struct ModifierData *target, const int flag);
 +
 +
 +      /********************* Deform modifier functions *********************/ /* DEPRECATED */
 +
 +      void (*deformVerts_DM_removed)(void);
 +      void (*deformMatrices_DM_removed)(void);
 +      void (*deformVertsEM_DM_removed)(void);
 +      void (*deformMatricesEM_DM_removed)(void);
 +
 +      /********************* Non-deform modifier functions *********************/ /* DEPRECATED */
 +
 +      void (*applyModifier_DM_removed)(void);
  
        /********************* Deform modifier functions *********************/
  
@@@ -266,12 -262,8 +266,12 @@@ void BKE_animdata_free(ID *id, const bo
  
  /* Copying -------------------------------------------- */
  
 -/* Make a copy of the given AnimData - to be used when copying datablocks */
 -AnimData *BKE_animdata_copy(Main *bmain, AnimData *adt, const bool do_action)
 +/**
 + * Make a copy of the given AnimData - to be used when copying datablocks.
-  * \param flag Control ID pointers management, see LIB_ID_CREATE_.../LIB_ID_COPY_... flags in BKE_library.h
++ * \param flag: Control ID pointers management, see LIB_ID_CREATE_.../LIB_ID_COPY_... flags in BKE_library.h
 + * \return The copied animdata.
 + */
 +AnimData *BKE_animdata_copy(Main *bmain, AnimData *adt, const int flag)
  {
        AnimData *dadt;
  
        return dadt;
  }
  
 -bool BKE_animdata_copy_id(Main *bmain, ID *id_to, ID *id_from, const bool do_action)
 +/**
-  * \param flag Control ID pointers management, see LIB_ID_CREATE_.../LIB_ID_COPY_... flags in BKE_library.h
++ * \param flag: Control ID pointers management, see LIB_ID_CREATE_.../LIB_ID_COPY_... flags in BKE_library.h
 + * \return true is succesfully copied.
 + */
 +bool BKE_animdata_copy_id(Main *bmain, ID *id_to, ID *id_from, const int flag)
  {
        AnimData *adt;
  
@@@ -2155,36 -1950,9 +2155,36 @@@ void BKE_pose_clear_pointers(bPose *pos
        }
  }
  
 -/* only after leave editmode, duplicating, validating older files, library syncing */
 -/* NOTE: pose->flag is set for it */
 -void BKE_pose_rebuild_ex(Object *ob, bArmature *arm, const bool sort_bones)
 +void BKE_pose_remap_bone_pointers(bArmature *armature, bPose *pose)
 +{
 +      GHash *bone_hash = BKE_armature_bone_from_name_map(armature);
 +      for (bPoseChannel *pchan = pose->chanbase.first; pchan; pchan = pchan->next) {
 +              pchan->bone = BLI_ghash_lookup(bone_hash, pchan->name);
 +      }
 +      BLI_ghash_free(bone_hash, NULL, NULL);
 +}
 +
 +/** Find the matching pose channel using the bone name, if not NULL. */
 +static bPoseChannel *pose_channel_find_bone(bPose *pose, Bone *bone)
 +{
 +      return (bone != NULL) ? BKE_pose_channel_find_name(pose, bone->name) : NULL;
 +}
 +
 +/** Update the links for the B-Bone handles from Bone data. */
 +void BKE_pchan_rebuild_bbone_handles(bPose *pose, bPoseChannel *pchan)
 +{
 +      pchan->bbone_prev = pose_channel_find_bone(pose, pchan->bone->bbone_prev);
 +      pchan->bbone_next = pose_channel_find_bone(pose, pchan->bone->bbone_next);
 +}
 +
 +/**
 + * Only after leave editmode, duplicating, validating older files, library syncing.
 + *
 + * \note pose->flag is set for it.
 + *
-  * \param bmain May be NULL, only used to tag depsgraph as being dirty...
++ * \param bmain: May be NULL, only used to tag depsgraph as being dirty...
 + */
 +void BKE_pose_rebuild(Main *bmain, Object *ob, bArmature *arm, const bool do_id_user)
  {
        Bone *bone;
        bPose *pose;
@@@ -712,9 -651,10 +712,10 @@@ BVHTree *bvhtree_from_editmesh_edges
  
  /**
   * Builds a bvh tree where nodes are the given edges .
-  * \param vert/edge_allocated if true, elem freeing will be done when freeing data.
-  * \param edges_mask if not null, true elements give which vert to add to BVH tree.
-  * \param edges_num_active if >= 0, number of active edges to add to BVH tree (else will be computed from mask).
 - * \param vert, vert_allocated: If true, elem freeing will be done when freeing data.
 - * \param edge, edge_allocated: If true, elem freeing will be done when freeing data.
++ * \param vert, vert_allocated: if true, elem freeing will be done when freeing data.
++ * \param edge, edge_allocated: if true, elem freeing will be done when freeing data.
+  * \param edges_mask: if not null, true elements give which vert to add to BVH tree.
+  * \param edges_num_active: if >= 0, number of active edges to add to BVH tree (else will be computed from mask).
   */
  BVHTree *bvhtree_from_mesh_edges_ex(
          BVHTreeFromMesh *data,
@@@ -103,11 -98,11 +103,11 @@@ void *BKE_camera_add(Main *bmain, cons
   *
   * WARNING! This function will not handle ID user count!
   *
-  * \param flag  Copying options (see BKE_library.h's LIB_ID_COPY_... flags for more).
+  * \param flag: Copying options (see BKE_library.h's LIB_ID_COPY_... flags for more).
   */
 -void BKE_camera_copy_data(Main *UNUSED(bmain), Camera *UNUSED(cam_dst), const Camera *UNUSED(cam_src), const int UNUSED(flag))
 +void BKE_camera_copy_data(Main *UNUSED(bmain), Camera *cam_dst, const Camera *cam_src, const int UNUSED(flag))
  {
 -      /* Nothing to do! */
 +      BLI_duplicatelist(&cam_dst->bg_images, &cam_src->bg_images);
  }
  
  Camera *BKE_camera_copy(Main *bmain, const Camera *cam)
index 763c0d5,0000000..9b68c61
mode 100644,000000..100644
--- /dev/null
@@@ -1,1200 -1,0 +1,1200 @@@
-  * \param flag  Copying options (see BKE_library.h's LIB_ID_COPY_... flags for more).
 +/*
 + * ***** 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/collection.c
 + *  \ingroup bke
 + */
 +
 +#include <string.h>
 +
 +#include "BLI_blenlib.h"
 +#include "BLI_ghash.h"
 +#include "BLI_iterator.h"
 +#include "BLI_listbase.h"
 +#include "BLI_math_base.h"
 +#include "BLI_threads.h"
 +#include "BLT_translation.h"
 +#include "BLI_string_utils.h"
 +
 +#include "BKE_collection.h"
 +#include "BKE_icons.h"
 +#include "BKE_idprop.h"
 +#include "BKE_layer.h"
 +#include "BKE_library.h"
 +#include "BKE_main.h"
 +#include "BKE_object.h"
 +#include "BKE_scene.h"
 +
 +#include "DNA_ID.h"
 +#include "DNA_collection_types.h"
 +#include "DNA_layer_types.h"
 +#include "DNA_object_types.h"
 +#include "DNA_scene_types.h"
 +
 +#include "DEG_depsgraph.h"
 +#include "DEG_depsgraph_query.h"
 +
 +#include "MEM_guardedalloc.h"
 +
 +/******************************** Prototypes ********************************/
 +
 +static bool collection_child_add(Collection *parent, Collection *collection, const int flag, const bool add_us);
 +static bool collection_child_remove(Collection *parent, Collection *collection);
 +static bool collection_object_add(Main *bmain, Collection *collection, Object *ob, int flag, const bool add_us);
 +static bool collection_object_remove(Main *bmain, Collection *collection, Object *ob, const bool free_us);
 +
 +static CollectionChild *collection_find_child(Collection *parent, Collection *collection);
 +static CollectionParent *collection_find_parent(Collection *child, Collection *collection);
 +
 +static bool collection_find_child_recursive(Collection *parent, Collection *collection);
 +
 +/***************************** Add Collection *******************************/
 +
 +/* Add new collection, without view layer syncing. */
 +static Collection *collection_add(Main *bmain, Collection *collection_parent, const char *name_custom)
 +{
 +      /* Determine new collection name. */
 +      char name[MAX_NAME];
 +
 +      if (name_custom) {
 +              STRNCPY(name, name_custom);
 +      }
 +      else {
 +              BKE_collection_new_name_get(collection_parent, name);
 +      }
 +
 +      /* Create new collection. */
 +      Collection *collection = BKE_libblock_alloc(bmain, ID_GR, name, 0);
 +
 +      /* We increase collection user count when linking to Collections. */
 +      id_us_min(&collection->id);
 +
 +      /* Optionally add to parent collection. */
 +      if (collection_parent) {
 +              collection_child_add(collection_parent, collection, 0, true);
 +      }
 +
 +      return collection;
 +}
 +
 +/**
 + * Add a collection to a collection ListBase and synchronize all render layers
 + * The ListBase is NULL when the collection is to be added to the master collection
 + */
 +Collection *BKE_collection_add(Main *bmain, Collection *collection_parent, const char *name_custom)
 +{
 +      Collection *collection = collection_add(bmain, collection_parent, name_custom);
 +      BKE_main_collection_sync(bmain);
 +      return collection;
 +}
 +
 +/*********************** Free and Delete Collection ****************************/
 +
 +/** Free (or release) any data used by this collection (does not free the collection itself). */
 +void BKE_collection_free(Collection *collection)
 +{
 +      /* No animdata here. */
 +      BKE_previewimg_free(&collection->preview);
 +
 +      BLI_freelistN(&collection->gobject);
 +      BLI_freelistN(&collection->children);
 +      BLI_freelistN(&collection->parents);
 +
 +      BKE_collection_object_cache_free(collection);
 +}
 +
 +/**
 + * Remove a collection, optionally removing its child objects or moving
 + * them to parent collections.
 + */
 +bool BKE_collection_delete(Main *bmain, Collection *collection, bool hierarchy)
 +{
 +      /* Master collection is not real datablock, can't be removed. */
 +      if (collection->flag & COLLECTION_IS_MASTER) {
 +              BLI_assert(!"Scene master collection can't be deleted");
 +              return false;
 +      }
 +
 +      if (hierarchy) {
 +              /* Remove child objects. */
 +              CollectionObject *cob = collection->gobject.first;
 +              while (cob != NULL) {
 +                      collection_object_remove(bmain, collection, cob->ob, true);
 +                      cob = collection->gobject.first;
 +              }
 +
 +              /* Delete all child collections recursively. */
 +              CollectionChild *child = collection->children.first;
 +              while (child != NULL) {
 +                      BKE_collection_delete(bmain, child->collection, hierarchy);
 +                      child = collection->children.first;
 +              }
 +      }
 +      else {
 +              /* Link child collections into parent collection. */
 +              for (CollectionChild *child = collection->children.first; child; child = child->next) {
 +                      for (CollectionParent *cparent = collection->parents.first; cparent; cparent = cparent->next) {
 +                              Collection *parent = cparent->collection;
 +                              collection_child_add(parent, child->collection, 0, true);
 +                      }
 +              }
 +
 +              CollectionObject *cob = collection->gobject.first;
 +              while (cob != NULL) {
 +                      /* Link child object into parent collections. */
 +                      for (CollectionParent *cparent = collection->parents.first; cparent; cparent = cparent->next) {
 +                              Collection *parent = cparent->collection;
 +                              collection_object_add(bmain, parent, cob->ob, 0, true);
 +                      }
 +
 +                      /* Remove child object. */
 +                      collection_object_remove(bmain, collection, cob->ob, true);
 +                      cob = collection->gobject.first;
 +              }
 +      }
 +
 +      BKE_libblock_delete(bmain, collection);
 +
 +      BKE_main_collection_sync(bmain);
 +
 +      return true;
 +}
 +
 +/***************************** Collection Copy *******************************/
 +
 +/**
 + * Only copy internal data of Collection ID from source to already allocated/initialized destination.
 + * You probably nerver want to use that directly, use id_copy or BKE_id_copy_ex for typical needs.
 + *
 + * WARNING! This function will not handle ID user count!
 + *
-  * \param collection may be \a NULL, in which case whole \a bmain database of collections is checked.
++ * \param flag: Copying options (see BKE_library.h's LIB_ID_COPY_... flags for more).
 + */
 +void BKE_collection_copy_data(
 +        Main *bmain, Collection *collection_dst, const Collection *collection_src, const int flag)
 +{
 +      /* Do not copy collection's preview (same behavior as for objects). */
 +      if ((flag & LIB_ID_COPY_NO_PREVIEW) == 0 && false) {  /* XXX TODO temp hack */
 +              BKE_previewimg_id_copy(&collection_dst->id, &collection_src->id);
 +      }
 +      else {
 +              collection_dst->preview = NULL;
 +      }
 +
 +      collection_dst->flag &= ~COLLECTION_HAS_OBJECT_CACHE;
 +      BLI_listbase_clear(&collection_dst->object_cache);
 +
 +      BLI_listbase_clear(&collection_dst->gobject);
 +      BLI_listbase_clear(&collection_dst->children);
 +      BLI_listbase_clear(&collection_dst->parents);
 +
 +      for (CollectionChild *child = collection_src->children.first; child; child = child->next) {
 +              collection_child_add(collection_dst, child->collection, flag, false);
 +      }
 +      for (CollectionObject *cob = collection_src->gobject.first; cob; cob = cob->next) {
 +              collection_object_add(bmain, collection_dst, cob->ob, flag, false);
 +      }
 +}
 +
 +/**
 + * Makes a shallow copy of a Collection
 + *
 + * 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).
 + */
 +Collection *BKE_collection_copy(Main *bmain, Collection *parent, Collection *collection)
 +{
 +      /* It's not allowed to copy the master collection. */
 +      if (collection->flag & COLLECTION_IS_MASTER) {
 +              BLI_assert("!Master collection can't be copied");
 +              return NULL;
 +      }
 +
 +      Collection *collection_new;
 +      BKE_id_copy_ex(bmain, &collection->id, (ID **)&collection_new, 0, false);
 +      id_us_min(&collection_new->id);  /* Copying add one user by default, need to get rid of that one. */
 +
 +      /* Optionally add to parent. */
 +      if (parent) {
 +              if (collection_child_add(parent, collection_new, 0, true)) {
 +                      /* Put collection right after existing one. */
 +                      CollectionChild *child = collection_find_child(parent, collection);
 +                      CollectionChild *child_new = collection_find_child(parent, collection_new);
 +
 +                      if (child && child_new) {
 +                              BLI_remlink(&parent->children, child_new);
 +                              BLI_insertlinkafter(&parent->children, child, child_new);
 +                      }
 +              }
 +      }
 +
 +      BKE_main_collection_sync(bmain);
 +
 +      return collection_new;
 +}
 +
 +Collection *BKE_collection_copy_master(Main *bmain, Collection *collection, const int flag)
 +{
 +      BLI_assert(collection->flag & COLLECTION_IS_MASTER);
 +
 +      Collection *collection_dst = MEM_dupallocN(collection);
 +      BKE_collection_copy_data(bmain, collection_dst, collection, flag);
 +      return collection_dst;
 +}
 +
 +void BKE_collection_copy_full(Main *UNUSED(bmain), Collection *UNUSED(collection))
 +{
 +      // TODO: implement full scene copy
 +}
 +
 +void BKE_collection_make_local(Main *bmain, Collection *collection, const bool lib_local)
 +{
 +      BKE_id_make_local_generic(bmain, &collection->id, true, lib_local);
 +}
 +
 +/********************************* Naming *******************************/
 +
 +/**
 + * The automatic/fallback name of a new collection.
 + */
 +void BKE_collection_new_name_get(Collection *collection_parent, char *rname)
 +{
 +      char *name;
 +
 +      if (!collection_parent) {
 +              name = BLI_sprintfN("Collection");
 +      }
 +      else if (collection_parent->flag & COLLECTION_IS_MASTER) {
 +              name = BLI_sprintfN("Collection %d", BLI_listbase_count(&collection_parent->children) + 1);
 +      }
 +      else {
 +              const int number = BLI_listbase_count(&collection_parent->children) + 1;
 +              const int digits = integer_digits_i(number);
 +              const int max_len =
 +                      sizeof(collection_parent->id.name) - 1 /* NULL terminator */ - (1 + digits) /* " %d" */ - 2 /* ID */;
 +              name = BLI_sprintfN("%.*s %d", max_len, collection_parent->id.name + 2, number);
 +      }
 +
 +      BLI_strncpy(rname, name, MAX_NAME);
 +      MEM_freeN(name);
 +}
 +
 +/**
 + * The name to show in the interface.
 + */
 +const char *BKE_collection_ui_name_get(struct Collection *collection)
 +{
 +      if (collection->flag & COLLECTION_IS_MASTER) {
 +              return IFACE_("Scene Collection");
 +      }
 +      else {
 +              return collection->id.name + 2;
 +      }
 +}
 +
 +/* **************** Object List Cache *******************/
 +
 +static void collection_object_cache_fill(ListBase *lb, Collection *collection, int parent_restrict)
 +{
 +      int child_restrict = collection->flag | parent_restrict;
 +
 +      for (CollectionObject *cob = collection->gobject.first; cob; cob = cob->next) {
 +              Base *base = BLI_findptr(lb, cob->ob, offsetof(Base, object));
 +
 +              if (base == NULL) {
 +                      base = MEM_callocN(sizeof(Base), "Object Base");
 +                      base->object = cob->ob;
 +                      BLI_addtail(lb, base);
 +              }
 +
 +              int object_restrict = base->object->restrictflag;
 +
 +              if (((child_restrict & COLLECTION_RESTRICT_VIEW) == 0) &&
 +                  ((object_restrict & OB_RESTRICT_VIEW) == 0))
 +              {
 +                      base->flag |= BASE_ENABLED_VIEWPORT;
 +              }
 +
 +              if (((child_restrict & COLLECTION_RESTRICT_RENDER) == 0) &&
 +                  ((object_restrict & OB_RESTRICT_RENDER) == 0))
 +              {
 +                      base->flag |= BASE_ENABLED_RENDER;
 +              }
 +      }
 +
 +      for (CollectionChild *child = collection->children.first; child; child = child->next) {
 +              collection_object_cache_fill(lb, child->collection, child_restrict);
 +      }
 +}
 +
 +ListBase BKE_collection_object_cache_get(Collection *collection)
 +{
 +      if (!(collection->flag & COLLECTION_HAS_OBJECT_CACHE)) {
 +              static ThreadMutex cache_lock = BLI_MUTEX_INITIALIZER;
 +
 +              BLI_mutex_lock(&cache_lock);
 +              if (!(collection->flag & COLLECTION_HAS_OBJECT_CACHE)) {
 +                      collection_object_cache_fill(&collection->object_cache, collection, 0);
 +                      collection->flag |= COLLECTION_HAS_OBJECT_CACHE;
 +              }
 +              BLI_mutex_unlock(&cache_lock);
 +      }
 +
 +      return collection->object_cache;
 +}
 +
 +static void collection_object_cache_free(Collection *collection)
 +{
 +      /* Clear own cache an for all parents, since those are affected by changes as well. */
 +      collection->flag &= ~COLLECTION_HAS_OBJECT_CACHE;
 +      BLI_freelistN(&collection->object_cache);
 +
 +      for (CollectionParent *parent = collection->parents.first; parent; parent = parent->next) {
 +              collection_object_cache_free(parent->collection);
 +      }
 +}
 +
 +void BKE_collection_object_cache_free(Collection *collection)
 +{
 +      collection_object_cache_free(collection);
 +}
 +
 +Base *BKE_collection_or_layer_objects(const ViewLayer *view_layer, Collection *collection)
 +{
 +      if (collection) {
 +              return BKE_collection_object_cache_get(collection).first;
 +      }
 +      else {
 +              return FIRSTBASE(view_layer);
 +      }
 +}
 +
 +/*********************** Scene Master Collection ***************/
 +
 +Collection *BKE_collection_master_add()
 +{
 +      /* Not an actual datablock, but owned by scene. */
 +      Collection *master_collection = MEM_callocN(sizeof(Collection), "Master Collection");
 +      STRNCPY(master_collection->id.name, "GRMaster Collection");
 +      master_collection->flag |= COLLECTION_IS_MASTER;
 +      return master_collection;
 +}
 +
 +Collection *BKE_collection_master(const Scene *scene)
 +{
 +      return scene->master_collection;
 +}
 +
 +/*********************** Cyclic Checks ************************/
 +
 +static bool collection_object_cyclic_check_internal(Object *object, Collection *collection)
 +{
 +      if (object->dup_group) {
 +              Collection *dup_collection = object->dup_group;
 +              if ((dup_collection->id.tag & LIB_TAG_DOIT) == 0) {
 +                      /* Cycle already exists in collections, let's prevent further crappyness */
 +                      return true;
 +              }
 +              /* flag the object to identify cyclic dependencies in further dupli collections */
 +              dup_collection->id.tag &= ~LIB_TAG_DOIT;
 +
 +              if (dup_collection == collection) {
 +                      return true;
 +              }
 +              else {
 +                      FOREACH_COLLECTION_OBJECT_RECURSIVE_BEGIN(dup_collection, collection_object)
 +                      {
 +                              if (collection_object_cyclic_check_internal(collection_object, dup_collection)) {
 +                                      return true;
 +                              }
 +                      }
 +                      FOREACH_COLLECTION_OBJECT_RECURSIVE_END;
 +              }
 +
 +              /* un-flag the object, it's allowed to have the same collection multiple times in parallel */
 +              dup_collection->id.tag |= LIB_TAG_DOIT;
 +      }
 +
 +      return false;
 +}
 +
 +bool BKE_collection_object_cyclic_check(Main *bmain, Object *object, Collection *collection)
 +{
 +      /* first flag all collections */
 +      BKE_main_id_tag_listbase(&bmain->collection, LIB_TAG_DOIT, true);
 +
 +      return collection_object_cyclic_check_internal(object, collection);
 +}
 +
 +/******************* Collection Object Membership *******************/
 +
 +bool BKE_collection_has_object(Collection *collection, Object *ob)
 +{
 +      if (ELEM(NULL, collection, ob)) {
 +              return false;
 +      }
 +
 +      return (BLI_findptr(&collection->gobject, ob, offsetof(CollectionObject, ob)));
 +}
 +
 +bool BKE_collection_has_object_recursive(Collection *collection, Object *ob)
 +{
 +      if (ELEM(NULL, collection, ob)) {
 +              return false;
 +      }
 +
 +      const ListBase objects = BKE_collection_object_cache_get(collection);
 +      return (BLI_findptr(&objects, ob, offsetof(Base, object)));
 +}
 +
 +Collection *BKE_collection_object_find(Main *bmain, Collection *collection, Object *ob)
 +{
 +      if (collection)
 +              collection = collection->id.next;
 +      else
 +              collection = bmain->collection.first;
 +
 +      while (collection) {
 +              if (BKE_collection_has_object(collection, ob))
 +                      return collection;
 +              collection = collection->id.next;
 +      }
 +      return NULL;
 +}
 +
 +bool BKE_collection_is_empty(Collection *collection)
 +{
 +      return BLI_listbase_is_empty(&collection->gobject) && BLI_listbase_is_empty(&collection->children);
 +}
 +
 +/********************** Collection Objects *********************/
 +
 +static bool collection_object_add(Main *bmain, Collection *collection, Object *ob, int flag, const bool add_us)
 +{
 +      if (ob->dup_group) {
 +              /* Cyclic dependency check. */
 +              if (collection_find_child_recursive(ob->dup_group, collection)) {
 +                      return false;
 +              }
 +      }
 +
 +      CollectionObject *cob = BLI_findptr(&collection->gobject, ob, offsetof(CollectionObject, ob));
 +      if (cob) {
 +              return false;
 +      }
 +
 +      cob = MEM_callocN(sizeof(CollectionObject), __func__);
 +      cob->ob = ob;
 +      BLI_addtail(&collection->gobject, cob);
 +      BKE_collection_object_cache_free(collection);
 +
 +      if (add_us && (flag & LIB_ID_CREATE_NO_USER_REFCOUNT) == 0) {
 +              id_us_plus(&ob->id);
 +      }
 +
 +      if ((flag & LIB_ID_CREATE_NO_MAIN) == 0) {
 +              DEG_id_tag_update_ex(bmain, &collection->id, ID_RECALC_COPY_ON_WRITE);
 +      }
 +
 +      return true;
 +}
 +
 +static bool collection_object_remove(Main *bmain, Collection *collection, Object *ob, const bool free_us)
 +{
 +      CollectionObject *cob = BLI_findptr(&collection->gobject, ob, offsetof(CollectionObject, ob));
 +      if (cob == NULL) {
 +              return false;
 +      }
 +
 +      BLI_freelinkN(&collection->gobject, cob);
 +      BKE_collection_object_cache_free(collection);
 +
 +      if (free_us) {
 +              BKE_libblock_free_us(bmain, ob);
 +      }
 +      else {
 +              id_us_min(&ob->id);
 +      }
 +
 +      DEG_id_tag_update_ex(bmain, &collection->id, ID_RECALC_COPY_ON_WRITE);
 +
 +      return true;
 +}
 +
 +/**
 + * Add object to collection
 + */
 +bool BKE_collection_object_add(Main *bmain, Collection *collection, Object *ob)
 +{
 +      if (ELEM(NULL, collection, ob)) {
 +              return false;
 +      }
 +
 +      if (!collection_object_add(bmain, collection, ob, 0, true)) {
 +              return false;
 +      }
 +
 +      if (BKE_collection_is_in_scene(collection)) {
 +              BKE_main_collection_sync(bmain);
 +      }
 +
 +      return true;
 +}
 +
 +/**
 + * Add object to all scene collections that reference objects is in
 + * (used to copy objects)
 + */
 +void BKE_collection_object_add_from(Main *bmain, Scene *scene, Object *ob_src, Object *ob_dst)
 +{
 +      FOREACH_SCENE_COLLECTION_BEGIN(scene, collection)
 +      {
 +              if (BKE_collection_has_object(collection, ob_src)) {
 +                      collection_object_add(bmain, collection, ob_dst, 0, true);
 +              }
 +      }
 +      FOREACH_SCENE_COLLECTION_END;
 +
 +      BKE_main_collection_sync(bmain);
 +}
 +
 +/**
 + * Remove object from collection.
 + */
 +bool BKE_collection_object_remove(Main *bmain, Collection *collection, Object *ob, const bool free_us)
 +{
 +      if (ELEM(NULL, collection, ob)) {
 +              return false;
 +      }
 +
 +      if (!collection_object_remove(bmain, collection, ob, free_us)) {
 +              return false;
 +      }
 +
 +      if (BKE_collection_is_in_scene(collection)) {
 +              BKE_main_collection_sync(bmain);
 +      }
 +
 +      return true;
 +}
 +
 +/**
 + * Remove object from all collections of scene
 + * \param scene_collection_skip: Don't remove base from this collection.
 + */
 +static bool scene_collections_object_remove(Main *bmain, Scene *scene, Object *ob, const bool free_us,
 +                                      Collection *collection_skip)
 +{
 +      bool removed = false;
 +
 +      BKE_scene_remove_rigidbody_object(bmain, scene, ob);
 +
 +      FOREACH_SCENE_COLLECTION_BEGIN(scene, collection)
 +      {
 +              if (collection != collection_skip) {
 +                      removed |= collection_object_remove(bmain, collection, ob, free_us);
 +              }
 +      }
 +      FOREACH_SCENE_COLLECTION_END;
 +
 +      BKE_main_collection_sync(bmain);
 +
 +      return removed;
 +}
 +
 +/**
 + * Remove object from all collections of scene
 + */
 +bool BKE_scene_collections_object_remove(Main *bmain, Scene *scene, Object *ob, const bool free_us)
 +{
 +      return scene_collections_object_remove(bmain, scene, ob, free_us, NULL);
 +}
 +
 +/*
 + * Remove all NULL objects from collections.
 + * This is used for library remapping, where these pointers have been set to NULL.
 + * Otherwise this should never happen.
 + */
 +static void collection_object_remove_nulls(Collection *collection)
 +{
 +      bool changed = false;
 +
 +      for (CollectionObject *cob = collection->gobject.first, *cob_next = NULL; cob; cob = cob_next) {
 +              cob_next = cob->next;
 +
 +              if (cob->ob == NULL) {
 +                      BLI_freelinkN(&collection->gobject, cob);
 +                      changed = true;
 +              }
 +      }
 +
 +      if (changed) {
 +              BKE_collection_object_cache_free(collection);
 +      }
 +}
 +
 +void BKE_collections_object_remove_nulls(Main *bmain)
 +{
 +      for (Scene *scene = bmain->scene.first; scene; scene = scene->id.next) {
 +              collection_object_remove_nulls(scene->master_collection);
 +      }
 +
 +      for (Collection *collection = bmain->collection.first; collection; collection = collection->id.next) {
 +              collection_object_remove_nulls(collection);
 +      }
 +}
 +
 +static void collection_null_children_remove(Collection *collection)
 +{
 +      for (CollectionChild *child = collection->children.first, *child_next = NULL; child; child = child_next) {
 +              child_next = child->next;
 +
 +              if (child->collection == NULL) {
 +                      BLI_freelinkN(&collection->children, child);
 +              }
 +      }
 +}
 +
 +static void collection_missing_parents_remove(Collection *collection)
 +{
 +      for (CollectionParent *parent = collection->parents.first, *parent_next; parent != NULL; parent = parent_next) {
 +              parent_next = parent->next;
 +
 +              if (!collection_find_child(parent->collection, collection)) {
 +                      BLI_freelinkN(&collection->parents, parent);
 +              }
 +      }
 +}
 +
 +/**
 + * Remove all NULL children from parent collections of changed \a collection.
 + * This is used for library remapping, where these pointers have been set to NULL.
 + * Otherwise this should never happen.
 + *
 + * \note caller must ensure BKE_main_collection_sync_remap() is called afterwards!
 + *
++ * \param collection: may be \a NULL, in which case whole \a bmain database of collections is checked.
 + */
 +void BKE_collections_child_remove_nulls(Main *bmain, Collection *collection)
 +{
 +      if (collection == NULL) {
 +              /* We need to do the checks in two steps when more than one collection may be involved,
 +               * otherwise we can miss some cases...
 +               * Also, master collections are not in bmain, so we also need to loop over scenes.
 +               */
 +              for (collection = bmain->collection.first; collection != NULL; collection = collection->id.next) {
 +                      collection_null_children_remove(collection);
 +              }
 +              for (Scene *scene = bmain->scene.first; scene != NULL; scene = scene->id.next) {
 +                      collection_null_children_remove(BKE_collection_master(scene));
 +              }
 +
 +              for (collection = bmain->collection.first; collection != NULL; collection = collection->id.next) {
 +                      collection_missing_parents_remove(collection);
 +              }
 +              for (Scene *scene = bmain->scene.first; scene != NULL; scene = scene->id.next) {
 +                      collection_missing_parents_remove(BKE_collection_master(scene));
 +              }
 +      }
 +      else {
 +              for (CollectionParent *parent = collection->parents.first, *parent_next; parent; parent = parent_next) {
 +                      parent_next = parent->next;
 +
 +                      collection_null_children_remove(parent->collection);
 +
 +                      if (!collection_find_child(parent->collection, collection)) {
 +                              BLI_freelinkN(&collection->parents, parent);
 +                      }
 +              }
 +      }
 +}
 +
 +/**
 + * Move object from a collection into another
 + *
 + * If source collection is NULL move it from all the existing collections.
 + */
 +void BKE_collection_object_move(
 +        Main *bmain, Scene *scene, Collection *collection_dst, Collection *collection_src, Object *ob)
 +{
 +      /* In both cases we first add the object, then remove it from the other collections.
 +       * Otherwise we lose the original base and whether it was active and selected. */
 +      if (collection_src != NULL) {
 +              if (BKE_collection_object_add(bmain, collection_dst, ob)) {
 +                      BKE_collection_object_remove(bmain, collection_src, ob, false);
 +              }
 +      }
 +      else {
 +              /* Adding will fail if object is already in collection.
 +               * However we still need to remove it from the other collections. */
 +              BKE_collection_object_add(bmain, collection_dst, ob);
 +              scene_collections_object_remove(bmain, scene, ob, false, collection_dst);
 +      }
 +}
 +
 +/***************** Collection Scene Membership ****************/
 +
 +bool BKE_collection_is_in_scene(Collection *collection)
 +{
 +      if (collection->flag & COLLECTION_IS_MASTER) {
 +              return true;
 +      }
 +
 +      for (CollectionParent *cparent = collection->parents.first; cparent; cparent = cparent->next) {
 +              if (BKE_collection_is_in_scene(cparent->collection)) {
 +                      return true;
 +              }
 +      }
 +
 +      return false;
 +}
 +
 +void BKE_collections_after_lib_link(Main *bmain)
 +{
 +      /* Update view layer collections to match any changes in linked
 +       * collections after file load. */
 +      BKE_main_collection_sync(bmain);
 +}
 +
 +/********************** Collection Children *******************/
 +
 +bool BKE_collection_find_cycle(Collection *new_ancestor, Collection *collection)
 +{
 +      if (collection == new_ancestor) {
 +              return true;
 +      }
 +
 +      for (CollectionParent *parent = new_ancestor->parents.first; parent; parent = parent->next) {
 +              if (BKE_collection_find_cycle(parent->collection, collection)) {
 +                      return true;
 +              }
 +      }
 +
 +      return false;
 +}
 +
 +static CollectionChild *collection_find_child(Collection *parent, Collection *collection)
 +{
 +      return BLI_findptr(&parent->children, collection, offsetof(CollectionChild, collection));
 +}
 +
 +static bool collection_find_child_recursive(Collection *parent, Collection *collection)
 +{
 +      for (CollectionChild *child = parent->children.first; child; child = child->next) {
 +              if (child->collection == collection) {
 +                      return true;
 +              }
 +
 +              if (collection_find_child_recursive(child->collection, collection)) {
 +                      return true;
 +              }
 +      }
 +
 +      return false;
 +}
 +
 +static CollectionParent *collection_find_parent(Collection *child, Collection *collection)
 +{
 +      return BLI_findptr(&child->parents, collection, offsetof(CollectionParent, collection));
 +}
 +
 +static bool collection_child_add(Collection *parent, Collection *collection, const int flag, const bool add_us)
 +{
 +      CollectionChild *child = collection_find_child(parent, collection);
 +      if (child) {
 +              return false;
 +      }
 +      if (BKE_collection_find_cycle(parent, collection)) {
 +              return false;
 +      }
 +
 +      child = MEM_callocN(sizeof(CollectionChild), "CollectionChild");
 +      child->collection = collection;
 +      BLI_addtail(&parent->children, child);
 +
 +      /* Don't add parent links for depsgraph datablocks, these are not kept in sync. */
 +      if ((flag & LIB_ID_CREATE_NO_MAIN) == 0) {
 +              CollectionParent *cparent = MEM_callocN(sizeof(CollectionParent), "CollectionParent");
 +              cparent->collection = parent;
 +              BLI_addtail(&collection->parents, cparent);
 +      }
 +
 +      if (add_us) {
 +              id_us_plus(&collection->id);
 +      }
 +
 +      BKE_collection_object_cache_free(parent);
 +
 +      return true;
 +}
 +
 +static bool collection_child_remove(Collection *parent, Collection *collection)
 +{
 +      CollectionChild *child = collection_find_child(parent, collection);
 +      if (child == NULL) {
 +              return false;
 +      }
 +
 +      CollectionParent *cparent = collection_find_parent(collection, parent);
 +      BLI_freelinkN(&collection->parents, cparent);
 +      BLI_freelinkN(&parent->children, child);
 +
 +      id_us_min(&collection->id);
 +
 +      BKE_collection_object_cache_free(parent);
 +
 +      return true;
 +}
 +
 +bool BKE_collection_child_add(Main *bmain, Collection *parent, Collection *child)
 +{
 +      if (!collection_child_add(parent, child, 0, true)) {
 +              return false;
 +      }
 +
 +      BKE_main_collection_sync(bmain);
 +      return true;
 +}
 +
 +bool BKE_collection_child_remove(Main *bmain, Collection *parent, Collection *child)
 +{
 +      if (!collection_child_remove(parent, child)) {
 +              return false;
 +      }
 +
 +      BKE_main_collection_sync(bmain);
 +      return true;
 +}
 +
 +/********************** Collection index *********************/
 +
 +static Collection *collection_from_index_recursive(Collection *collection, const int index, int *index_current)
 +{
 +      if (index == (*index_current)) {
 +              return collection;
 +      }
 +
 +      (*index_current)++;
 +
 +      for (CollectionChild *child = collection->children.first; child; child = child->next) {
 +              Collection *nested = collection_from_index_recursive(child->collection, index, index_current);
 +              if (nested != NULL) {
 +                      return nested;
 +              }
 +      }
 +      return NULL;
 +}
 +
 +/**
 + * Return Scene Collection for a given index.
 + *
 + * The index is calculated from top to bottom counting the children before the siblings.
 + */
 +Collection *BKE_collection_from_index(Scene *scene, const int index)
 +{
 +      int index_current = 0;
 +      Collection *master_collection = BKE_collection_master(scene);
 +      return collection_from_index_recursive(master_collection, index, &index_current);
 +}
 +
 +static bool collection_objects_select(ViewLayer *view_layer, Collection *collection, bool deselect)
 +{
 +      bool changed = false;
 +
 +      if (collection->flag & COLLECTION_RESTRICT_SELECT) {
 +              return false;
 +      }
 +
 +      for (CollectionObject *cob = collection->gobject.first; cob; cob = cob->next) {
 +              Base *base = BKE_view_layer_base_find(view_layer, cob->ob);
 +
 +              if (base) {
 +                      if (deselect) {
 +                              if (base->flag & BASE_SELECTED) {
 +                                      base->flag &= ~BASE_SELECTED;
 +                                      changed = true;
 +                              }
 +                      }
 +                      else {
 +                              if ((base->flag & BASE_SELECTABLE) && !(base->flag & BASE_SELECTED)) {
 +                                      base->flag |= BASE_SELECTED;
 +                                      changed = true;
 +                              }
 +                      }
 +              }
 +      }
 +
 +      for (CollectionChild *child = collection->children.first; child; child = child->next) {
 +              if (collection_objects_select(view_layer, collection, deselect)) {
 +                      changed = true;
 +              }
 +      }
 +
 +      return changed;
 +}
 +
 +/**
 + * Select all the objects in this Collection (and its nested collections) for this ViewLayer.
 + * Return true if any object was selected.
 + */
 +bool BKE_collection_objects_select(ViewLayer *view_layer, Collection *collection, bool deselect)
 +{
 +      LayerCollection *layer_collection = BKE_layer_collection_first_from_scene_collection(view_layer, collection);
 +
 +      if (layer_collection != NULL) {
 +              return BKE_layer_collection_objects_select(view_layer, layer_collection, deselect);
 +      }
 +      else {
 +              return collection_objects_select(view_layer, collection, deselect);
 +      }
 +}
 +
 +/***************** Collection move (outliner drag & drop) *********************/
 +
 +bool BKE_collection_move(Main *bmain,
 +                         Collection *to_parent,
 +                         Collection *from_parent,
 +                         Collection *relative,
 +                         bool relative_after,
 +                         Collection *collection)
 +{
 +      if (collection->flag & COLLECTION_IS_MASTER) {
 +              return false;
 +      }
 +      if (BKE_collection_find_cycle(to_parent, collection)) {
 +              return false;
 +      }
 +
 +      /* Move to new parent collection */
 +      if (from_parent) {
 +              collection_child_remove(from_parent, collection);
 +      }
 +
 +      collection_child_add(to_parent, collection, 0, true);
 +
 +      /* Move to specified location under parent. */
 +      if (relative) {
 +              CollectionChild *child = collection_find_child(to_parent, collection);
 +              CollectionChild *relative_child = collection_find_child(to_parent, relative);
 +
 +              if (relative_child) {
 +                      BLI_remlink(&to_parent->children, child);
 +
 +                      if (relative_after) {
 +                              BLI_insertlinkafter(&to_parent->children, relative_child, child);
 +                      }
 +                      else {
 +                              BLI_insertlinkbefore(&to_parent->children, relative_child, child);
 +                      }
 +
 +                      BKE_collection_object_cache_free(to_parent);
 +              }
 +      }
 +
 +      BKE_main_collection_sync(bmain);
 +
 +      return true;
 +}
 +
 +/**************************** Iterators ******************************/
 +
 +/* scene collection iteractor */
 +
 +typedef struct CollectionsIteratorData {
 +      Scene *scene;
 +      void **array;
 +      int tot, cur;
 +} CollectionsIteratorData;
 +
 +static void scene_collection_callback(Collection *collection, BKE_scene_collections_Cb callback, void *data)
 +{
 +      callback(collection, data);
 +
 +      for (CollectionChild *child = collection->children.first; child; child = child->next) {
 +              scene_collection_callback(child->collection, callback, data);
 +      }
 +}
 +
 +static void scene_collections_count(Collection *UNUSED(collection), void *data)
 +{
 +      int *tot = data;
 +      (*tot)++;
 +}
 +
 +static void scene_collections_build_array(Collection *collection, void *data)
 +{
 +      Collection ***array = data;
 +      **array = collection;
 +      (*array)++;
 +}
 +
 +static void scene_collections_array(Scene *scene, Collection ***collections_array, int *tot)
 +{
 +      Collection *collection;
 +      Collection **array;
 +
 +      *collections_array = NULL;
 +      *tot = 0;
 +
 +      if (scene == NULL) {
 +              return;
 +      }
 +
 +      collection = BKE_collection_master(scene);
 +      BLI_assert(collection != NULL);
 +      scene_collection_callback(collection, scene_collections_count, tot);
 +
 +      if (*tot == 0)
 +              return;
 +
 +      *collections_array = array = MEM_mallocN(sizeof(Collection *) * (*tot), "CollectionArray");
 +      scene_collection_callback(collection, scene_collections_build_array, &array);
 +}
 +
 +/**
 + * Only use this in non-performance critical situations
 + * (it iterates over all scene collections twice)
 + */
 +void BKE_scene_collections_iterator_begin(BLI_Iterator *iter, void *data_in)
 +{
 +      Scene *scene = data_in;
 +      CollectionsIteratorData *data = MEM_callocN(sizeof(CollectionsIteratorData), __func__);
 +
 +      data->scene = scene;
 +      iter->data = data;
 +      iter->valid = true;
 +
 +      scene_collections_array(scene, (Collection ***)&data->array, &data->tot);
 +      BLI_assert(data->tot != 0);
 +
 +      data->cur = 0;
 +      iter->current = data->array[data->cur];
 +}
 +
 +void BKE_scene_collections_iterator_next(struct BLI_Iterator *iter)
 +{
 +      CollectionsIteratorData *data = iter->data;
 +
 +      if (++data->cur < data->tot) {
 +              iter->current = data->array[data->cur];
 +      }
 +      else {
 +              iter->valid = false;
 +      }
 +}
 +
 +void BKE_scene_collections_iterator_end(struct BLI_Iterator *iter)
 +{
 +      CollectionsIteratorData *data = iter->data;
 +
 +      if (data) {
 +              if (data->array) {
 +                      MEM_freeN(data->array);
 +              }
 +              MEM_freeN(data);
 +      }
 +      iter->valid = false;
 +}
 +
 +
 +/* scene objects iteractor */
 +
 +typedef struct SceneObjectsIteratorData {
 +      GSet *visited;
 +      CollectionObject *cob_next;
 +      BLI_Iterator scene_collection_iter;
 +} SceneObjectsIteratorData;
 +
 +void BKE_scene_objects_iterator_begin(BLI_Iterator *iter, void *data_in)
 +{
 +      Scene *scene = data_in;
 +      SceneObjectsIteratorData *data = MEM_callocN(sizeof(SceneObjectsIteratorData), __func__);
 +      iter->data = data;
 +
 +      /* lookup list ot make sure each object is object called once */
 +      data->visited = BLI_gset_ptr_new(__func__);
 +
 +      /* we wrap the scenecollection iterator here to go over the scene collections */
 +      BKE_scene_collections_iterator_begin(&data->scene_collection_iter, scene);
 +
 +      Collection *collection = data->scene_collection_iter.current;
 +      data->cob_next = collection->gobject.first;
 +
 +      BKE_scene_objects_iterator_next(iter);
 +}
 +
 +/**
 + * Ensures we only get each object once, even when included in several collections.
 + */
 +static CollectionObject *object_base_unique(GSet *gs, CollectionObject *cob)
 +{
 +      for (; cob != NULL; cob = cob->next) {
 +              Object *ob = cob->ob;
 +              void **ob_key_p;
 +              if (!BLI_gset_ensure_p_ex(gs, ob, &ob_key_p)) {
 +                      *ob_key_p = ob;
 +                      return cob;
 +              }
 +      }
 +      return NULL;
 +}
 +
 +void BKE_scene_objects_iterator_next(BLI_Iterator *iter)
 +{
 +      SceneObjectsIteratorData *data = iter->data;
 +      CollectionObject *cob = data->cob_next ? object_base_unique(data->visited, data->cob_next) : NULL;
 +
 +      if (cob) {
 +              data->cob_next = cob->next;
 +              iter->current = cob->ob;
 +      }
 +      else {
 +              /* if this is the last object of this ListBase look at the next Collection */
 +              Collection *collection;
 +              BKE_scene_collections_iterator_next(&data->scene_collection_iter);
 +              do {
 +                      collection = data->scene_collection_iter.current;
 +                      /* get the first unique object of this collection */
 +                      CollectionObject *new_cob = object_base_unique(data->visited, collection->gobject.first);
 +                      if (new_cob) {
 +                              data->cob_next = new_cob->next;
 +                              iter->current = new_cob->ob;
 +                              return;
 +                      }
 +                      BKE_scene_collections_iterator_next(&data->scene_collection_iter);
 +              } while (data->scene_collection_iter.valid);
 +
 +              if (!data->scene_collection_iter.valid) {
 +                      iter->valid = false;
 +              }
 +      }
 +}
 +
 +void BKE_scene_objects_iterator_end(BLI_Iterator *iter)
 +{
 +      SceneObjectsIteratorData *data = iter->data;
 +      if (data) {
 +              BKE_scene_collections_iterator_end(&data->scene_collection_iter);
 +              BLI_gset_free(data->visited, NULL);
 +              MEM_freeN(data);
 +      }
 +}
@@@ -960,70 -925,15 +960,70 @@@ static void curvemapping_evaluateRGBF_f
   *
   * Use in conjunction with #curvemapping_set_black_white_ex
   *
-  * \param black Use instead of cumap->black
-  * \param bwmul Use instead of cumap->bwmul
+  * \param black: Use instead of cumap->black
+  * \param bwmul: Use instead of cumap->bwmul
   */
 -void curvemapping_evaluate_premulRGBF_ex(const CurveMapping *cumap, float vecout[3], const float vecin[3],
 -                                         const float black[3], const float bwmul[3])
 +void curvemapping_evaluate_premulRGBF_ex(
 +        const CurveMapping *cumap, float vecout[3], const float vecin[3],
 +        const float black[3], const float bwmul[3])
  {
 -      vecout[0] = curvemap_evaluateF(&cumap->cm[0], (vecin[0] - black[0]) * bwmul[0]);
 -      vecout[1] = curvemap_evaluateF(&cumap->cm[1], (vecin[1] - black[1]) * bwmul[1]);
 -      vecout[2] = curvemap_evaluateF(&cumap->cm[2], (vecin[2] - black[2]) * bwmul[2]);
 +      const float r = (vecin[0] - black[0]) * bwmul[0];
 +      const float g = (vecin[1] - black[1]) * bwmul[1];
 +      const float b = (vecin[2] - black[2]) * bwmul[2];
 +
 +      switch (cumap->tone) {
 +              default:
 +              case CURVE_TONE_STANDARD:
 +              {
 +                      vecout[0] = curvemap_evaluateF(&cumap->cm[0], r);
 +                      vecout[1] = curvemap_evaluateF(&cumap->cm[1], g);
 +                      vecout[2] = curvemap_evaluateF(&cumap->cm[2], b);
 +                      break;
 +              }
 +              case CURVE_TONE_FILMLIKE:
 +              {
 +                      if (r >= g) {
 +                              if (g > b) {
 +                                      /* Case 1: r >= g >  b */
 +                                      const int shuffeled_channels[] = {0, 1, 2};
 +                                      curvemapping_evaluateRGBF_filmlike(cumap, vecout, vecin, shuffeled_channels);
 +                              }
 +                              else if (b > r) {
 +                                      /* Case 2: b >  r >= g */
 +                                      const int shuffeled_channels[] = {2, 0, 1};
 +                                      curvemapping_evaluateRGBF_filmlike(cumap, vecout, vecin, shuffeled_channels);
 +                              }
 +                              else if (b > g) {
 +                                      /* Case 3: r >= b >  g */
 +                                      const int shuffeled_channels[] = {0, 2, 1};
 +                                      curvemapping_evaluateRGBF_filmlike(cumap, vecout, vecin, shuffeled_channels);
 +                              }
 +                              else {
 +                                      /* Case 4: r >= g == b */
 +                                      copy_v2_fl2(vecout, curvemap_evaluateF(&cumap->cm[0], r), curvemap_evaluateF(&cumap->cm[1], g));
 +                                      vecout[2] = vecout[1];
 +                              }
 +                      }
 +                      else {
 +                              if (r >= b) {
 +                                      /* Case 5: g >  r >= b */
 +                                      const int shuffeled_channels[] = {1, 0, 2};
 +                                      curvemapping_evaluateRGBF_filmlike(cumap, vecout, vecin, shuffeled_channels);
 +                              }
 +                              else if (b >  g) {
 +                                      /* Case 6: b >  g >  r */
 +                                      const int shuffeled_channels[] = {2, 1, 0};
 +                                      curvemapping_evaluateRGBF_filmlike(cumap, vecout, vecin, shuffeled_channels);
 +                              }
 +                              else {
 +                                      /* Case 7: g >= b >  r */
 +                                      const int shuffeled_channels[] = {1, 2, 0};
 +                                      curvemapping_evaluateRGBF_filmlike(cumap, vecout, vecin, shuffeled_channels);
 +                              }
 +                      }
 +                      break;
 +              }
 +      }
  }
  
  /* RGB with black/white points and premult. tables are checked */
@@@ -399,145 -360,288 +399,145 @@@ bGPDlayer *BKE_gpencil_layer_addnew(bGP
        return gpl;
  }
  
 -/* add a new gp-palette and make it the active */
 -bGPDpalette *BKE_gpencil_palette_addnew(bGPdata *gpd, const char *name, bool setactive)
 +/* add a new gp-datablock */
 +bGPdata *BKE_gpencil_data_addnew(Main *bmain, const char name[])
  {
 -      bGPDpalette *palette;
 -
 -      /* check that list is ok */
 -      if (gpd == NULL) {
 -              return NULL;
 -      }
 -
 -      /* allocate memory and add to end of list */
 -      palette = MEM_callocN(sizeof(bGPDpalette), "bGPDpalette");
 +      bGPdata *gpd;
  
 -      /* add to datablock */
 -      BLI_addtail(&gpd->palettes, palette);
 +      /* allocate memory for a new block */
 +      gpd = BKE_libblock_alloc(bmain, ID_GD, name, 0);
  
 -      /* set basic settings */
 -      /* auto-name */
 -      BLI_strncpy(palette->info, name, sizeof(palette->info));
 -      BLI_uniquename(&gpd->palettes, palette, DATA_("GP_Palette"), '.', offsetof(bGPDpalette, info),
 -                     sizeof(palette->info));
 +      /* initial settings */
 +      gpd->flag = (GP_DATA_DISPINFO | GP_DATA_EXPAND);
  
 -      /* make this one the active one */
 -      /* NOTE: Always make this active if there's nothing else yet (T50123) */
 -      if ((setactive) || (gpd->palettes.first == gpd->palettes.last)) {
 -              BKE_gpencil_palette_setactive(gpd, palette);
 -      }
 +      /* general flags */
 +      gpd->flag |= GP_DATA_VIEWALIGN;
 +      gpd->flag |= GP_DATA_STROKE_FORCE_RECALC;
 +      /* always enable object onion skin swith */
 +      gpd->flag |= GP_DATA_SHOW_ONIONSKINS;
 +      /* GP object specific settings */
 +      ARRAY_SET_ITEMS(gpd->line_color, 0.6f, 0.6f, 0.6f, 0.5f);
 +
 +      gpd->xray_mode = GP_XRAY_3DSPACE;
 +      gpd->pixfactor = GP_DEFAULT_PIX_FACTOR;
 +
 +      /* grid settings */
 +      ARRAY_SET_ITEMS(gpd->grid.color, 0.5f, 0.5f, 0.5f); // Color
 +      ARRAY_SET_ITEMS(gpd->grid.scale, 1.0f, 1.0f); // Scale
 +      gpd->grid.lines = GP_DEFAULT_GRID_LINES; // Number of lines
 +
 +      /* onion-skinning settings (datablock level) */
 +      gpd->onion_flag |= (GP_ONION_GHOST_PREVCOL | GP_ONION_GHOST_NEXTCOL);
 +      gpd->onion_flag |= GP_ONION_FADE;
 +      gpd->onion_mode = GP_ONION_MODE_RELATIVE;
 +      gpd->onion_factor = 0.5f;
 +      ARRAY_SET_ITEMS(gpd->gcolor_prev, 0.145098f, 0.419608f, 0.137255f); /* green */
 +      ARRAY_SET_ITEMS(gpd->gcolor_next, 0.125490f, 0.082353f, 0.529412f); /* blue */
 +      gpd->gstep = 1;
 +      gpd->gstep_next = 1;
  
 -      /* return palette */
 -      return palette;
 +      return gpd;
  }
  
 -/* create a set of default drawing brushes with predefined presets */
 -void BKE_gpencil_brush_init_presets(ToolSettings *ts)
 -{
 -      bGPDbrush *brush;
 -      /* Basic brush */
 -      brush = BKE_gpencil_brush_addnew(ts, "Basic", true);
 -      brush->thickness = 3.0f;
 -      brush->flag &= ~GP_BRUSH_USE_RANDOM_PRESSURE;
 -      brush->draw_sensitivity = 1.0f;
 -      brush->flag |= GP_BRUSH_USE_PRESSURE;
 -
 -      brush->flag &= ~GP_BRUSH_USE_RANDOM_STRENGTH;
 -      brush->draw_strength = 1.0f;
 -      brush->flag |= ~GP_BRUSH_USE_STENGTH_PRESSURE;
 -
 -      brush->draw_random_press = 0.0f;
 -
 -      brush->draw_jitter = 0.0f;
 -      brush->flag |= GP_BRUSH_USE_JITTER_PRESSURE;
 -
 -      brush->draw_angle = 0.0f;
 -      brush->draw_angle_factor = 0.0f;
 -
 -      brush->draw_smoothfac = 0.0f;
 -      brush->draw_smoothlvl = 1;
 -      brush->sublevel = 0;
 -      brush->draw_random_sub = 0.0f;
 -
 -      /* Pencil brush */
 -      brush = BKE_gpencil_brush_addnew(ts, "Pencil", false);
 -      brush->thickness = 7.0f;
 -      brush->flag &= ~GP_BRUSH_USE_RANDOM_PRESSURE;
 -      brush->draw_sensitivity = 1.0f;
 -      brush->flag |= GP_BRUSH_USE_PRESSURE;
 -
 -      brush->flag &= ~GP_BRUSH_USE_RANDOM_STRENGTH;
 -      brush->draw_strength = 0.7f;
 -      brush->flag |= GP_BRUSH_USE_STENGTH_PRESSURE;
 -
 -      brush->draw_random_press = 0.0f;
  
 -      brush->draw_jitter = 0.0f;
 -      brush->flag |= GP_BRUSH_USE_JITTER_PRESSURE;
 -
 -      brush->draw_angle = 0.0f;
 -      brush->draw_angle_factor = 0.0f;
 -
 -      brush->draw_smoothfac = 1.0f;
 -      brush->draw_smoothlvl = 2;
 -      brush->sublevel = 2;
 -      brush->draw_random_sub = 0.0f;
 -
 -      /* Ink brush */
 -      brush = BKE_gpencil_brush_addnew(ts, "Ink", false);
 -      brush->thickness = 7.0f;
 -      brush->flag &= ~GP_BRUSH_USE_RANDOM_PRESSURE;
 -      brush->draw_sensitivity = 1.6f;
 -      brush->flag |= GP_BRUSH_USE_PRESSURE;
 -
 -      brush->flag &= ~GP_BRUSH_USE_RANDOM_STRENGTH;
 -      brush->draw_strength = 1.0f;
 -      brush->flag &= ~GP_BRUSH_USE_STENGTH_PRESSURE;
 -
 -      brush->draw_random_press = 0.0f;
 -
 -      brush->draw_jitter = 0.0f;
 -      brush->flag |= GP_BRUSH_USE_JITTER_PRESSURE;
 -
 -      brush->draw_angle = 0.0f;
 -      brush->draw_angle_factor = 0.0f;
 -
 -      brush->draw_smoothfac = 1.1f;
 -      brush->draw_smoothlvl = 2;
 -      brush->sublevel = 2;
 -      brush->draw_random_sub = 0.0f;
 -
 -      /* Ink Noise brush */
 -      brush = BKE_gpencil_brush_addnew(ts, "Ink noise", false);
 -      brush->thickness = 6.0f;
 -      brush->flag |= GP_BRUSH_USE_RANDOM_PRESSURE;
 -      brush->draw_sensitivity = 1.611f;
 -      brush->flag |= GP_BRUSH_USE_PRESSURE;
 -
 -      brush->flag &= ~GP_BRUSH_USE_RANDOM_STRENGTH;
 -      brush->draw_strength = 1.0f;
 -      brush->flag |= GP_BRUSH_USE_STENGTH_PRESSURE;
 -
 -      brush->draw_random_press = 1.0f;
 -
 -      brush->draw_jitter = 0.0f;
 -      brush->flag |= GP_BRUSH_USE_JITTER_PRESSURE;
 -
 -      brush->draw_angle = 0.0f;
 -      brush->draw_angle_factor = 0.0f;
 -
 -      brush->draw_smoothfac = 1.1f;
 -      brush->draw_smoothlvl = 2;
 -      brush->sublevel = 2;
 -      brush->draw_random_sub = 0.0f;
 -
 -      /* Marker brush */
 -      brush = BKE_gpencil_brush_addnew(ts, "Marker", false);
 -      brush->thickness = 10.0f;
 -      brush->flag &= ~GP_BRUSH_USE_RANDOM_PRESSURE;
 -      brush->draw_sensitivity = 2.0f;
 -      brush->flag &= ~GP_BRUSH_USE_PRESSURE;
 -
 -      brush->flag &= ~GP_BRUSH_USE_RANDOM_STRENGTH;
 -      brush->draw_strength = 1.0f;
 -      brush->flag &= ~GP_BRUSH_USE_STENGTH_PRESSURE;
 +/* ************************************************** */
 +/* Primitive Creation */
 +/* Utilities for easier bulk-creation of geometry */
  
 -      brush->draw_random_press = 0.0f;
 +/**
 + * Populate stroke with point data from data buffers
 + *
-  * \param array Flat array of point data values. Each entry has GP_PRIM_DATABUF_SIZE values
-  * \param mat   4x4 transform matrix to transform points into the right coordinate space
++ * \param array: Flat array of point data values. Each entry has GP_PRIM_DATABUF_SIZE values
++ * \param mat: 4x4 transform matrix to transform points into the right coordinate space
 + */
 +void BKE_gpencil_stroke_add_points(bGPDstroke *gps, const float *array, const int totpoints, const float mat[4][4])
 +{
 +      for (int i = 0; i < totpoints; i++) {
 +              bGPDspoint *pt = &gps->points[i];
 +              const int x = GP_PRIM_DATABUF_SIZE * i;
  
 -      brush->draw_jitter = 0.0f;
 -      brush->flag |= GP_BRUSH_USE_JITTER_PRESSURE;
 +              pt->x = array[x];
 +              pt->y = array[x + 1];
 +              pt->z = array[x + 2];
 +              mul_m4_v3(mat, &pt->x);
  
 -      brush->draw_angle = M_PI_4; /* 45 degrees */
 -      brush->draw_angle_factor = 1.0f;
 +              pt->pressure = array[x + 3];
 +              pt->strength = array[x + 4];
 +      }
 +}
  
 -      brush->draw_smoothfac = 1.0f;
 -      brush->draw_smoothlvl = 2;
 -      brush->sublevel = 2;
 -      brush->draw_random_sub = 0.0f;
 +/* Create a new stroke, with pre-allocated data buffers */
 +bGPDstroke *BKE_gpencil_add_stroke(bGPDframe *gpf, int mat_idx, int totpoints, short thickness)
 +{
 +      /* allocate memory for a new stroke */
 +      bGPDstroke *gps = MEM_callocN(sizeof(bGPDstroke), "gp_stroke");
  
 -      /* Crayon brush */
 -      brush = BKE_gpencil_brush_addnew(ts, "Crayon", false);
 -      brush->thickness = 10.0f;
 -      brush->flag &= ~GP_BRUSH_USE_RANDOM_PRESSURE;
 -      brush->draw_sensitivity = 3.0f;
 -      brush->flag &= ~GP_BRUSH_USE_PRESSURE;
 +      gps->thickness = thickness * 25;
 +      gps->inittime = 0;
  
 -      brush->flag &= ~GP_BRUSH_USE_RANDOM_STRENGTH;
 -      brush->draw_strength = 0.140f;
 -      brush->flag |= GP_BRUSH_USE_STENGTH_PRESSURE;
 +      /* enable recalculation flag by default */
 +      gps->flag = GP_STROKE_RECALC_CACHES | GP_STROKE_3DSPACE;
  
 -      brush->draw_random_press = 0.0f;
 +      gps->totpoints = totpoints;
 +      gps->points = MEM_callocN(sizeof(bGPDspoint) * gps->totpoints, "gp_stroke_points");
  
 -      brush->draw_jitter = 0.0f;
 -      brush->flag |= GP_BRUSH_USE_JITTER_PRESSURE;
 +      /* initialize triangle memory to dummy data */
 +      gps->triangles = MEM_callocN(sizeof(bGPDtriangle), "GP Stroke triangulation");
 +      gps->flag |= GP_STROKE_RECALC_CACHES;
 +      gps->tot_triangles = 0;
  
 -      brush->draw_angle = 0.0f;
 -      brush->draw_angle_factor = 0.0f;
 +      gps->mat_nr = mat_idx;
  
 -      brush->draw_smoothfac = 0.0f;
 -      brush->draw_smoothlvl = 1;
 -      brush->sublevel = 2;
 -      brush->draw_random_sub = 0.5f;
 +      /* add to frame */
 +      BLI_addtail(&gpf->strokes, gps);
  
 +      return gps;
  }
  
 -/* add a new gp-brush and make it the active */
 -bGPDbrush *BKE_gpencil_brush_addnew(ToolSettings *ts, const char *name, bool setactive)
 -{
 -      bGPDbrush *brush;
  
 -      /* check that list is ok */
 -      if (ts == NULL) {
 -              return NULL;
 -      }
 -
 -      /* allocate memory and add to end of list */
 -      brush = MEM_callocN(sizeof(bGPDbrush), "bGPDbrush");
 -
 -      /* add to datablock */
 -      BLI_addtail(&ts->gp_brushes, brush);
 -
 -      /* set basic settings */
 -      brush->thickness = 3;
 -      brush->draw_smoothlvl = 1;
 -      brush->flag |= GP_BRUSH_USE_PRESSURE;
 -      brush->draw_sensitivity = 1.0f;
 -      brush->draw_strength = 1.0f;
 -      brush->flag |= GP_BRUSH_USE_STENGTH_PRESSURE;
 -      brush->draw_jitter = 0.0f;
 -      brush->flag |= GP_BRUSH_USE_JITTER_PRESSURE;
 -
 -      /* curves */
 -      brush->cur_sensitivity = curvemapping_add(1, 0.0f, 0.0f, 1.0f, 1.0f);
 -      brush->cur_strength = curvemapping_add(1, 0.0f, 0.0f, 1.0f, 1.0f);
 -      brush->cur_jitter = curvemapping_add(1, 0.0f, 0.0f, 1.0f, 1.0f);
 -
 -      /* auto-name */
 -      BLI_strncpy(brush->info, name, sizeof(brush->info));
 -      BLI_uniquename(&ts->gp_brushes, brush, DATA_("GP_Brush"), '.', offsetof(bGPDbrush, info), sizeof(brush->info));
 +/* ************************************************** */
 +/* Data Duplication */
  
 -      /* make this one the active one */
 -      if (setactive) {
 -              BKE_gpencil_brush_setactive(ts, brush);
 +/* make a copy of a given gpencil weights */
 +void BKE_gpencil_stroke_weights_duplicate(bGPDstroke *gps_src, bGPDstroke *gps_dst)
 +{
 +      if (gps_src == NULL) {
 +              return;
        }
 +      BLI_assert(gps_src->totpoints == gps_dst->totpoints);
  
 -      /* return brush */
 -      return brush;
 +      BKE_defvert_array_copy(gps_dst->dvert, gps_src->dvert, gps_src->totpoints);
  }
  
 -/* add a new gp-palettecolor and make it the active */
 -bGPDpalettecolor *BKE_gpencil_palettecolor_addnew(bGPDpalette *palette, const char *name, bool setactive)
 +/* make a copy of a given gpencil stroke */
 +bGPDstroke *BKE_gpencil_stroke_duplicate(bGPDstroke *gps_src)
  {
 -      bGPDpalettecolor *palcolor;
 +      bGPDstroke *gps_dst = NULL;
  
 -      /* check that list is ok */
 -      if (palette == NULL) {
 -              return NULL;
 -      }
 +      gps_dst = MEM_dupallocN(gps_src);
 +      gps_dst->prev = gps_dst->next = NULL;
  
 -      /* allocate memory and add to end of list */
 -      palcolor = MEM_callocN(sizeof(bGPDpalettecolor), "bGPDpalettecolor");
 +      gps_dst->points = MEM_dupallocN(gps_src->points);
  
 -      /* add to datablock */
 -      BLI_addtail(&palette->colors, palcolor);
 -
 -      /* set basic settings */
 -      palcolor->flag |= PC_COLOR_HQ_FILL;
 -      copy_v4_v4(palcolor->color, U.gpencil_new_layer_col);
 -      ARRAY_SET_ITEMS(palcolor->fill, 1.0f, 1.0f, 1.0f);
 -
 -      /* auto-name */
 -      BLI_strncpy(palcolor->info, name, sizeof(palcolor->info));
 -      BLI_uniquename(&palette->colors, palcolor, DATA_("Color"), '.', offsetof(bGPDpalettecolor, info),
 -                     sizeof(palcolor->info));
 -
 -      /* make this one the active one */
 -      if (setactive) {
 -              BKE_gpencil_palettecolor_setactive(palette, palcolor);
 +      if (gps_src->dvert != NULL) {
 +              gps_dst->dvert = MEM_dupallocN(gps_src->dvert);
 +              BKE_gpencil_stroke_weights_duplicate(gps_src, gps_dst);
 +      }
 +      else {
 +              gps_dst->dvert = NULL;
        }
  
 -      /* return palette color */
 -      return palcolor;
 -}
 -
 -/* add a new gp-datablock */
 -bGPdata *BKE_gpencil_data_addnew(Main *bmain, const char name[])
 -{
 -      bGPdata *gpd;
 -
 -      /* allocate memory for a new block */
 -      gpd = BKE_libblock_alloc(bmain, ID_GD, name, 0);
 -
 -      /* initial settings */
 -      gpd->flag = (GP_DATA_DISPINFO | GP_DATA_EXPAND);
 -
 -      /* for now, stick to view is also enabled by default
 -       * since this is more useful...
 +      /* Don't clear triangles, so that modifier evaluation can just use
 +       * this without extra work first. Most places that need to force
 +       * this data to get recalculated will destroy the data anyway though.
         */
 -      gpd->flag |= GP_DATA_VIEWALIGN;
 +      gps_dst->triangles = MEM_dupallocN(gps_dst->triangles);
 +      /* gps_dst->flag |= GP_STROKE_RECALC_CACHES; */
  
 -      return gpd;
 +      /* return new stroke */
 +      return gps_dst;
  }
  
 -/* -------- Data Duplication ---------- */
 -
  /* make a copy of a given gpencil frame */
  bGPDframe *BKE_gpencil_frame_duplicate(const bGPDframe *gpf_src)
  {
@@@ -621,15 -759,10 +621,15 @@@ bGPDlayer *BKE_gpencil_layer_duplicate(
   *
   * WARNING! This function will not handle ID user count!
   *
-  * \param flag  Copying options (see BKE_library.h's LIB_ID_COPY_... flags for more).
+  * \param flag: Copying options (see BKE_library.h's LIB_ID_COPY_... flags for more).
   */
 -void BKE_gpencil_copy_data(Main *UNUSED(bmain), bGPdata *gpd_dst, const bGPdata *gpd_src, const int UNUSED(flag))
 +void BKE_gpencil_copy_data(bGPdata *gpd_dst, const bGPdata *gpd_src, const int UNUSED(flag))
  {
 +      /* duplicate material array */
 +      if (gpd_src->mat) {
 +              gpd_dst->mat = MEM_dupallocN(gpd_src->mat);
 +      }
 +
        /* copy layers */
        BLI_listbase_clear(&gpd_dst->layers);
        for (const bGPDlayer *gpl_src = gpd_src->layers.first; gpl_src; gpl_src = gpl_src->next) {
@@@ -1222,340 -1244,114 +1222,340 @@@ void BKE_gpencil_vgroup_remove(Object *
  }
  
  
 -/* get the active gp-palettecolor for editing */
 -bGPDpalettecolor *BKE_gpencil_palettecolor_getactive(bGPDpalette *palette)
 +void BKE_gpencil_dvert_ensure(bGPDstroke *gps)
  {
 -      bGPDpalettecolor *palcolor;
 +      if (gps->dvert == NULL) {
 +              gps->dvert = MEM_callocN(sizeof(MDeformVert) * gps->totpoints, "gp_stroke_weights");
 +      }
 +}
  
 -      /* error checking */
 -      if (ELEM(NULL, palette, palette->colors.first)) {
 -              return NULL;
 +/* ************************************************** */
 +
 +/**
 + * Apply smooth to stroke point
-  * \param gps              Stroke to smooth
-  * \param i                Point index
-  * \param inf              Amount of smoothing to apply
++ * \param gps: Stroke to smooth
++ * \param i: Point index
++ * \param inf: Amount of smoothing to apply
 + */
 +bool BKE_gpencil_smooth_stroke(bGPDstroke *gps, int i, float inf)
 +{
 +      bGPDspoint *pt = &gps->points[i];
 +      // float pressure = 0.0f;
 +      float sco[3] = { 0.0f };
 +
 +      /* Do nothing if not enough points to smooth out */
 +      if (gps->totpoints <= 2) {
 +              return false;
        }
  
 -      /* loop over colors until found (assume only one active) */
 -      for (palcolor = palette->colors.first; palcolor; palcolor = palcolor->next) {
 -              if (palcolor->flag & PC_COLOR_ACTIVE) {
 -                      return palcolor;
 +      /* Only affect endpoints by a fraction of the normal strength,
 +       * to prevent the stroke from shrinking too much
 +       */
 +      if ((i == 0) || (i == gps->totpoints - 1)) {
 +              inf *= 0.1f;
 +      }
 +
 +      /* Compute smoothed coordinate by taking the ones nearby */
 +      /* XXX: This is potentially slow, and suffers from accumulation error as earlier points are handled before later ones */
 +      {
 +              // XXX: this is hardcoded to look at 2 points on either side of the current one (i.e. 5 items total)
 +              const int   steps = 2;
 +              const float average_fac = 1.0f / (float)(steps * 2 + 1);
 +              int step;
 +
 +              /* add the point itself */
 +              madd_v3_v3fl(sco, &pt->x, average_fac);
 +
 +              /* n-steps before/after current point */
 +              // XXX: review how the endpoints are treated by this algorithm
 +              // XXX: falloff measures should also introduce some weighting variations, so that further-out points get less weight
 +              for (step = 1; step <= steps; step++) {
 +                      bGPDspoint *pt1, *pt2;
 +                      int before = i - step;
 +                      int after = i + step;
 +
 +                      CLAMP_MIN(before, 0);
 +                      CLAMP_MAX(after, gps->totpoints - 1);
 +
 +                      pt1 = &gps->points[before];
 +                      pt2 = &gps->points[after];
 +
 +                      /* add both these points to the average-sum (s += p[i]/n) */
 +                      madd_v3_v3fl(sco, &pt1->x, average_fac);
 +                      madd_v3_v3fl(sco, &pt2->x, average_fac);
 +
                }
        }
  
 -      /* no active color found */
 -      return NULL;
 +      /* Based on influence factor, blend between original and optimal smoothed coordinate */
 +      interp_v3_v3v3(&pt->x, &pt->x, sco, inf);
 +
 +      return true;
  }
 -/* get the gp-palettecolor looking for name */
 -bGPDpalettecolor *BKE_gpencil_palettecolor_getbyname(bGPDpalette *palette, char *name)
 +
 +/**
 + * Apply smooth for strength to stroke point */
 +bool BKE_gpencil_smooth_stroke_strength(bGPDstroke *gps, int point_index, float influence)
  {
 -      /* error checking */
 -      if (ELEM(NULL, palette, name)) {
 -              return NULL;
 +      bGPDspoint *ptb = &gps->points[point_index];
 +
 +      /* Do nothing if not enough points */
 +      if (gps->totpoints <= 2) {
 +              return false;
        }
  
 -      return BLI_findstring(&palette->colors, name, offsetof(bGPDpalettecolor, info));
 +      /* Compute theoretical optimal value using distances */
 +      bGPDspoint *pta, *ptc;
 +      int before = point_index - 1;
 +      int after = point_index + 1;
 +
 +      CLAMP_MIN(before, 0);
 +      CLAMP_MAX(after, gps->totpoints - 1);
 +
 +      pta = &gps->points[before];
 +      ptc = &gps->points[after];
 +
 +      /* the optimal value is the corresponding to the interpolation of the strength
 +       * at the distance of point b
 +       */
 +      float fac = line_point_factor_v3(&ptb->x, &pta->x, &ptc->x);
 +      /* sometimes the factor can be wrong due stroke geometry, so use middle point */
 +      if ((fac < 0.0f) || (fac > 1.0f)) {
 +              fac = 0.5f;
 +      }
 +      const float optimal = (1.0f - fac) * pta->strength + fac * ptc->strength;
 +
 +      /* Based on influence factor, blend between original and optimal */
 +      ptb->strength = (1.0f - influence) * ptb->strength + influence * optimal;
 +
 +      return true;
  }
  
 -/* Change color name in all strokes */
 -void BKE_gpencil_palettecolor_changename(bGPdata *gpd, char *oldname, const char *newname)
 +/**
 + * Apply smooth for thickness to stroke point (use pressure) */
 +bool BKE_gpencil_smooth_stroke_thickness(bGPDstroke *gps, int point_index, float influence)
  {
 -      bGPDlayer *gpl;
 -      bGPDframe *gpf;
 -      bGPDstroke *gps;
 +      bGPDspoint *ptb = &gps->points[point_index];
  
 -      /* Sanity checks (gpd may not be set in the RNA pointers sometimes) */
 -      if (ELEM(NULL, gpd, oldname, newname))
 -              return;
 +      /* Do nothing if not enough points */
 +      if ((gps->totpoints <= 2) || (point_index < 1)) {
 +              return false;
 +      }
  
 -      for (gpl = gpd->layers.first; gpl; gpl = gpl->next) {
 -              for (gpf = gpl->frames.first; gpf; gpf = gpf->next) {
 -                      for (gps = gpf->strokes.first; gps; gps = gps->next) {
 -                              if (STREQ(gps->colorname, oldname)) {
 -                                      BLI_strncpy(gps->colorname, newname, sizeof(gps->colorname));
 -                              }
 -                      }
 -              }
 +      /* Compute theoretical optimal value using distances */
 +      bGPDspoint *pta, *ptc;
 +      int before = point_index - 1;
 +      int after = point_index + 1;
 +
 +      CLAMP_MIN(before, 0);
 +      CLAMP_MAX(after, gps->totpoints - 1);
 +
 +      pta = &gps->points[before];
 +      ptc = &gps->points[after];
 +
 +      /* the optimal value is the corresponding to the interpolation of the pressure
 +       * at the distance of point b
 +       */
 +      float fac = line_point_factor_v3(&ptb->x, &pta->x, &ptc->x);
 +      /* sometimes the factor can be wrong due stroke geometry, so use middle point */
 +      if ((fac < 0.0f) || (fac > 1.0f)) {
 +              fac = 0.5f;
        }
 +      float optimal = interpf(ptc->pressure, pta->pressure, fac);
 +
 +      /* Based on influence factor, blend between original and optimal */
 +      ptb->pressure = interpf(optimal, ptb->pressure, influence);
  
 +      return true;
  }
  
 -/* Delete all strokes of the color */
 -void BKE_gpencil_palettecolor_delete_strokes(struct bGPdata *gpd, char *name)
 +/**
 +* Apply smooth for UV rotation to stroke point (use pressure) */
 +bool BKE_gpencil_smooth_stroke_uv(bGPDstroke *gps, int point_index, float influence)
  {
 -      bGPDlayer *gpl;
 -      bGPDframe *gpf;
 -      bGPDstroke *gps, *gpsn;
 +      bGPDspoint *ptb = &gps->points[point_index];
  
 -      /* Sanity checks (gpd may not be set in the RNA pointers sometimes) */
 -      if (ELEM(NULL, gpd, name))
 -              return;
 +      /* Do nothing if not enough points */
 +      if (gps->totpoints <= 2) {
 +              return false;
 +      }
  
 -      for (gpl = gpd->layers.first; gpl; gpl = gpl->next) {
 -              for (gpf = gpl->frames.first; gpf; gpf = gpf->next) {
 -                      for (gps = gpf->strokes.first; gps; gps = gpsn) {
 -                              gpsn = gps->next;
 -
 -                              if (STREQ(gps->colorname, name)) {
 -                                      if (gps->points) MEM_freeN(gps->points);
 -                                      if (gps->triangles) MEM_freeN(gps->triangles);
 -                                      BLI_freelinkN(&gpf->strokes, gps);
 -                              }
 +      /* Compute theoretical optimal value */
 +      bGPDspoint *pta, *ptc;
 +      int before = point_index - 1;
 +      int after = point_index + 1;
 +
 +      CLAMP_MIN(before, 0);
 +      CLAMP_MAX(after, gps->totpoints - 1);
 +
 +      pta = &gps->points[before];
 +      ptc = &gps->points[after];
 +
 +      /* the optimal value is the corresponding to the interpolation of the pressure
 +       * at the distance of point b
 +       */
 +      float fac = line_point_factor_v3(&ptb->x, &pta->x, &ptc->x);
 +      /* sometimes the factor can be wrong due stroke geometry, so use middle point */
 +      if ((fac < 0.0f) || (fac > 1.0f)) {
 +              fac = 0.5f;
 +      }
 +      float optimal = interpf(ptc->uv_rot, pta->uv_rot, fac);
 +
 +      /* Based on influence factor, blend between original and optimal */
 +      ptb->uv_rot = interpf(optimal, ptb->uv_rot, influence);
 +      CLAMP(ptb->uv_rot, -M_PI_2, M_PI_2);
 +
 +      return true;
 +}
 +
 +/**
 + * Get range of selected frames in layer.
 + * Always the active frame is considered as selected, so if no more selected the range
 + * will be equal to the current active frame.
-  * \param gpl              Layer
-  * \param r_initframe      Number of first selected frame
-  * \param r_endframe       Number of last selected frame
++ * \param gpl: Layer
++ * \param r_initframe: Number of first selected frame
++ * \param r_endframe: Number of last selected frame
 + */
 +void BKE_gpencil_get_range_selected(bGPDlayer *gpl, int *r_initframe, int *r_endframe)
 +{
 +      *r_initframe = gpl->actframe->framenum;
 +      *r_endframe = gpl->actframe->framenum;
 +
 +      for (bGPDframe *gpf = gpl->frames.first; gpf; gpf = gpf->next) {
 +              if (gpf->flag & GP_FRAME_SELECT) {
 +                      if (gpf->framenum < *r_initframe) {
 +                              *r_initframe = gpf->framenum;
 +                      }
 +                      if (gpf->framenum > *r_endframe) {
 +                              *r_endframe = gpf->framenum;
                        }
                }
        }
 +}
  
-  * \param gpf          Frame
-  * \param actnum       Number of active frame in layer
-  * \param f_init       Number of first selected frame
-  * \param f_end        Number of last selected frame
-  * \param cur_falloff  Curve with falloff factors
 +/**
 + * Get Falloff factor base on frame range
++ * \param gpf: Frame
++ * \param actnum: Number of active frame in layer
++ * \param f_init: Number of first selected frame
++ * \param f_end: Number of last selected frame
++ * \param cur_falloff: Curve with falloff factors
 + */
 +float BKE_gpencil_multiframe_falloff_calc(bGPDframe *gpf, int actnum, int f_init, int f_end, CurveMapping *cur_falloff)
 +{
 +      float fnum = 0.5f; /* default mid curve */
 +      float value;
 +
 +      /* check curve is available */
 +      if (cur_falloff == NULL) {
 +              return 1.0f;
 +      }
 +
 +      /* frames to the right of the active frame */
 +      if (gpf->framenum < actnum) {
 +              fnum = (float)(gpf->framenum - f_init) / (actnum - f_init);
 +              fnum *= 0.5f;
 +              value = curvemapping_evaluateF(cur_falloff, 0, fnum);
 +      }
 +      /* frames to the left of the active frame */
 +      else if (gpf->framenum > actnum) {
 +              fnum = (float)(gpf->framenum - actnum) / (f_end - actnum);
 +              fnum *= 0.5f;
 +              value = curvemapping_evaluateF(cur_falloff, 0, fnum + 0.5f);
 +      }
 +      else {
 +              value = 1.0f;
 +      }
 +
 +      return value;
  }
  
 -/* set the active gp-palettecolor */
 -void BKE_gpencil_palettecolor_setactive(bGPDpalette *palette, bGPDpalettecolor *active)
 +/* remove strokes using a material */
 +void BKE_gpencil_material_index_remove(bGPdata *gpd, int index)
  {
 -      bGPDpalettecolor *palcolor;
 +      bGPDstroke *gps, *gpsn;
  
 -      /* error checking */
 -      if (ELEM(NULL, palette, palette->colors.first, active)) {
 -              return;
 +              for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) {
 +                      for (bGPDframe *gpf = gpl->frames.first; gpf; gpf = gpf->next) {
 +                              for (gps = gpf->strokes.first; gps; gps = gpsn) {
 +                                      gpsn = gps->next;
 +                                      if (gps->mat_nr == index) {
 +                                              if (gps->points) {
 +                                                      MEM_freeN(gps->points);
 +                                              }
 +                                              if (gps->dvert) {
 +                                                      BKE_gpencil_free_stroke_weights(gps);
 +                                                      MEM_freeN(gps->dvert);
 +                                              }
 +                                              if (gps->triangles) MEM_freeN(gps->triangles);
 +                                              BLI_freelinkN(&gpf->strokes, gps);
 +                                      }
 +                                      else {
 +                                              /* reassign strokes */
 +                                              if (gps->mat_nr > index) {
 +                                                      gps->mat_nr--;
 +                                              }
 +                                      }
 +                              }
 +                      }
 +              }
 +}
 +
 +void BKE_gpencil_material_remap(struct bGPdata *gpd, const unsigned int *remap, unsigned int remap_len)
 +{
 +      const short remap_len_short = (short)remap_len;
 +
 +#define MAT_NR_REMAP(n) \
 +      if (n < remap_len_short) { \
 +              BLI_assert(n >= 0 && remap[n] < remap_len_short); \
 +              n = remap[n]; \
 +      } ((void)0)
 +
 +      for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) {
 +              for (bGPDframe *gpf = gpl->frames.first; gpf; gpf = gpf->next) {
 +                      for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) {
 +                              /* reassign strokes */
 +                              MAT_NR_REMAP(gps->mat_nr);
 +                      }
 +              }
        }
  
 -      /* loop over colors deactivating all */
 -      for (palcolor = palette->colors.first; palcolor; palcolor = palcolor->next) {
 -              palcolor->flag &= ~PC_COLOR_ACTIVE;
 +#undef MAT_NR_REMAP
 +
 +}
 +
 +/* statistics functions */
 +void BKE_gpencil_stats_update(bGPdata *gpd)
 +{
 +      gpd->totlayer = 0;
 +      gpd->totframe = 0;
 +      gpd->totstroke = 0;
 +      gpd->totpoint = 0;
 +
 +      for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) {
 +              gpd->totlayer++;
 +              for (bGPDframe *gpf = gpl->frames.first; gpf; gpf = gpf->next) {
 +                      gpd->totframe++;
 +                      for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) {
 +                              gpd->totstroke++;
 +                              gpd->totpoint += gps->totpoints;
 +                      }
 +              }
        }
  
 -      /* set as active one */
 -      active->flag |= PC_COLOR_ACTIVE;
  }
  
 -/* delete the active gp-palettecolor */
 -void BKE_gpencil_palettecolor_delete(bGPDpalette *palette, bGPDpalettecolor *palcolor)
 +/* get material index */
 +int BKE_gpencil_get_material_index(Object *ob, Material *ma)
  {
 -      /* error checking */
 -      if (ELEM(NULL, palette, palcolor)) {
 -              return;
 +      short *totcol = give_totcolp(ob);
 +      Material *read_ma = NULL;
 +      for (short i = 0; i < *totcol; i++) {
 +              read_ma = give_current_material(ob, i + 1);
 +              if (ma == read_ma) {
 +                      return i + 1;
 +              }
        }
  
 -      /* free */
 -      BLI_freelinkN(&palette->colors, palcolor);
 +      return 0;
  }
index bee9155,0000000..3fedd38
mode 100644,000000..100644
--- /dev/null
@@@ -1,1543 -1,0 +1,1543 @@@
-  * \param flag  Copying options (see BKE_library.h's LIB_ID_COPY_... flags for more).
 +/*
 + * ***** 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 <string.h>
 +
 +#include "BLI_array.h"
 +#include "BLI_listbase.h"
 +#include "BLI_string.h"
 +#include "BLI_string_utf8.h"
 +#include "BLI_string_utils.h"
 +#include "BLI_threads.h"
 +#include "BLT_translation.h"
 +
 +#include "BKE_animsys.h"
 +#include "BKE_collection.h"
 +#include "BKE_freestyle.h"
 +#include "BKE_global.h"
 +#include "BKE_idprop.h"
 +#include "BKE_layer.h"
 +#include "BKE_main.h"
 +#include "BKE_node.h"
 +#include "BKE_object.h"
 +
 +#include "DNA_ID.h"
 +#include "DNA_space_types.h"
 +#include "DNA_collection_types.h"
 +#include "DNA_layer_types.h"
 +#include "DNA_object_types.h"
 +#include "DNA_node_types.h"
 +#include "DNA_scene_types.h"
 +#include "DNA_windowmanager_types.h"
 +#include "DNA_workspace_types.h"
 +
 +#include "DEG_depsgraph.h"
 +#include "DEG_depsgraph_debug.h"
 +#include "DEG_depsgraph_query.h"
 +
 +#include "DRW_engine.h"
 +
 +#include "MEM_guardedalloc.h"
 +
 +
 +/* prototype */
 +static void object_bases_iterator_next(BLI_Iterator *iter, const int flag);
 +
 +
 +/*********************** Layer Collections and bases *************************/
 +
 +static LayerCollection *layer_collection_add(ListBase *lb_parent, Collection *collection)
 +{
 +      LayerCollection *lc = MEM_callocN(sizeof(LayerCollection), "Collection Base");
 +      lc->collection = collection;
 +      BLI_addtail(lb_parent, lc);
 +
 +      return lc;
 +}
 +
 +static void layer_collection_free(ViewLayer *view_layer, LayerCollection *lc)
 +{
 +      if (lc == view_layer->active_collection) {
 +              view_layer->active_collection = NULL;
 +      }
 +
 +      for (LayerCollection *nlc = lc->layer_collections.first; nlc; nlc = nlc->next) {
 +              layer_collection_free(view_layer, nlc);
 +      }
 +
 +      BLI_freelistN(&lc->layer_collections);
 +}
 +
 +static Base *object_base_new(Object *ob)
 +{
 +      Base *base = MEM_callocN(sizeof(Base), "Object Base");
 +      base->object = ob;
 +      return base;
 +}
 +
 +/********************************* View Layer ********************************/
 +
 +
 +/* RenderLayer */
 +
 +/* Returns the default view layer to view in workspaces if there is
 + * none linked to the workspace yet. */
 +ViewLayer *BKE_view_layer_default_view(const Scene *scene)
 +{
 +      for (ViewLayer *view_layer = scene->view_layers.first; view_layer; view_layer = view_layer->next) {
 +              if (!(view_layer->flag & VIEW_LAYER_RENDER)) {
 +                      return view_layer;
 +              }
 +      }
 +
 +      BLI_assert(scene->view_layers.first);
 +      return scene->view_layers.first;
 +}
 +
 +/* Returns the default view layer to render if we need to render just one. */
 +ViewLayer *BKE_view_layer_default_render(const Scene *scene)
 +{
 +      for (ViewLayer *view_layer = scene->view_layers.first; view_layer; view_layer = view_layer->next) {
 +              if (view_layer->flag & VIEW_LAYER_RENDER) {
 +                      return view_layer;
 +              }
 +      }
 +
 +      BLI_assert(scene->view_layers.first);
 +      return scene->view_layers.first;
 +}
 +
 +/* Returns view layer with matching name, or NULL if not found. */
 +ViewLayer *BKE_view_layer_find(const Scene *scene, const char *layer_name)
 +{
 +      for (ViewLayer *view_layer = scene->view_layers.first; view_layer; view_layer = view_layer->next) {
 +              if (STREQ(view_layer->name, layer_name)) {
 +                      return view_layer;
 +              }
 +      }
 +
 +      return NULL;
 +}
 +
 +/**
 + * This is a placeholder to know which areas of the code need to be addressed
 + * for the Workspace changes. Never use this, you should typically get the
 + * active layer from the context or window.
 + */
 +ViewLayer *BKE_view_layer_context_active_PLACEHOLDER(const Scene *scene)
 +{
 +      BLI_assert(scene->view_layers.first);
 +      return scene->view_layers.first;
 +}
 +
 +static ViewLayer *view_layer_add(const char *name)
 +{
 +      if (!name) {
 +              name = DATA_("View Layer");
 +      }
 +
 +      ViewLayer *view_layer = MEM_callocN(sizeof(ViewLayer), "View Layer");
 +      view_layer->flag = VIEW_LAYER_RENDER | VIEW_LAYER_FREESTYLE;
 +
 +      BLI_strncpy_utf8(view_layer->name, name, sizeof(view_layer->name));
 +
 +      /* Pure rendering pipeline settings. */
 +      view_layer->layflag = 0x7FFF;   /* solid ztra halo edge strand */
 +      view_layer->passflag = SCE_PASS_COMBINED | SCE_PASS_Z;
 +      view_layer->pass_alpha_threshold = 0.5f;
 +      BKE_freestyle_config_init(&view_layer->freestyle_config);
 +
 +      return view_layer;
 +}
 +
 +/**
 + * Add a new view layer
 + * by default, a view layer has the master collection
 + */
 +ViewLayer *BKE_view_layer_add(Scene *scene, const char *name)
 +{
 +      ViewLayer *view_layer = view_layer_add(name);
 +
 +      BLI_addtail(&scene->view_layers, view_layer);
 +
 +      /* unique name */
 +      BLI_uniquename(
 +              &scene->view_layers, view_layer, DATA_("ViewLayer"), '.',
 +              offsetof(ViewLayer, name), sizeof(view_layer->name));
 +
 +      BKE_layer_collection_sync(scene, view_layer);
 +
 +      return view_layer;
 +}
 +
 +void BKE_view_layer_free(ViewLayer *view_layer)
 +{
 +      BKE_view_layer_free_ex(view_layer, true);
 +}
 +
 +/**
 + * Free (or release) any data used by this ViewLayer.
 + */
 +void BKE_view_layer_free_ex(ViewLayer *view_layer, const bool do_id_user)
 +{
 +      view_layer->basact = NULL;
 +
 +      BLI_freelistN(&view_layer->object_bases);
 +
 +      if (view_layer->object_bases_hash) {
 +              BLI_ghash_free(view_layer->object_bases_hash, NULL, NULL);
 +      }
 +
 +      for (LayerCollection *lc = view_layer->layer_collections.first; lc; lc = lc->next) {
 +              layer_collection_free(view_layer, lc);
 +      }
 +      BLI_freelistN(&view_layer->layer_collections);
 +
 +      for (ViewLayerEngineData *sled = view_layer->drawdata.first; sled; sled = sled->next) {
 +              if (sled->storage) {
 +                      if (sled->free) {
 +                              sled->free(sled->storage);
 +                      }
 +                      MEM_freeN(sled->storage);
 +              }
 +      }
 +      BLI_freelistN(&view_layer->drawdata);
 +
 +      MEM_SAFE_FREE(view_layer->stats);
 +
 +      BKE_freestyle_config_free(&view_layer->freestyle_config, do_id_user);
 +
 +      if (view_layer->id_properties) {
 +              IDP_FreeProperty(view_layer->id_properties);
 +              MEM_freeN(view_layer->id_properties);
 +      }
 +
 +      MEM_SAFE_FREE(view_layer->object_bases_array);
 +
 +      MEM_freeN(view_layer);
 +}
 +
 +/**
 + * Tag all the selected objects of a renderlayer
 + */
 +void BKE_view_layer_selected_objects_tag(ViewLayer *view_layer, const int tag)
 +{
 +      for (Base *base = view_layer->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;
 +}
 +
 +/**
 + * Fallback for when a Scene has no camera to use
 + *
 + * \param view_layer: in general you want to use the same ViewLayer that is used
 + * for depsgraph. If rendering you pass the scene active layer, when viewing in the viewport
 + * you want to get ViewLayer from context.
 + */
 +Object *BKE_view_layer_camera_find(ViewLayer *view_layer)
 +{
 +      for (Base *base = view_layer->object_bases.first; base; base = base->next) {
 +              if (base->object->type == OB_CAMERA) {
 +                      return base->object;
 +              }
 +      }
 +
 +      return NULL;
 +}
 +
 +/**
 + * Find the ViewLayer a LayerCollection belongs to
 + */
 +ViewLayer *BKE_view_layer_find_from_collection(const Scene *scene, LayerCollection *lc)
 +{
 +      for (ViewLayer *view_layer = scene->view_layers.first; view_layer; view_layer = view_layer->next) {
 +              if (find_scene_collection_in_scene_collections(&view_layer->layer_collections, lc)) {
 +                      return view_layer;
 +              }
 +      }
 +
 +      return NULL;
 +}
 +
 +/* Base */
 +
 +static void view_layer_bases_hash_create(ViewLayer *view_layer)
 +{
 +      static ThreadMutex hash_lock = BLI_MUTEX_INITIALIZER;
 +
 +      if (view_layer->object_bases_hash == NULL) {
 +              BLI_mutex_lock(&hash_lock);
 +
 +              if (view_layer->object_bases_hash == NULL) {
 +                      view_layer->object_bases_hash = BLI_ghash_new(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp, __func__);
 +
 +                      for (Base *base = view_layer->object_bases.first; base; base = base->next) {
 +                              if (base->object) {
 +                                      BLI_ghash_insert(view_layer->object_bases_hash, base->object, base);
 +                              }
 +                      }
 +              }
 +
 +              BLI_mutex_unlock(&hash_lock);
 +      }
 +}
 +
 +Base *BKE_view_layer_base_find(ViewLayer *view_layer, Object *ob)
 +{
 +      if (!view_layer->object_bases_hash) {
 +              view_layer_bases_hash_create(view_layer);
 +      }
 +
 +      return BLI_ghash_lookup(view_layer->object_bases_hash, ob);
 +}
 +
 +void BKE_view_layer_base_deselect_all(ViewLayer *view_layer)
 +{
 +      Base *base;
 +
 +      for (base = view_layer->object_bases.first; base; base = base->next) {
 +              base->flag &= ~BASE_SELECTED;
 +      }
 +}
 +
 +void BKE_view_layer_base_select(Base *selbase)
 +{
 +      if ((selbase->flag & BASE_SELECTABLE) != 0) {
 +              selbase->flag |= BASE_SELECTED;
 +      }
 +}
 +
 +void BKE_view_layer_base_select_and_set_active(struct ViewLayer *view_layer, Base *selbase)
 +{
 +      view_layer->basact = selbase;
 +      BKE_view_layer_base_select(selbase);
 +}
 +
 +/**************************** Copy View Layer and Layer Collections ***********************/
 +
 +static void layer_collections_copy_data(
 +        ViewLayer *view_layer_dst, const ViewLayer *view_layer_src,
 +        ListBase *layer_collections_dst, const ListBase *layer_collections_src)
 +{
 +      BLI_duplicatelist(layer_collections_dst, layer_collections_src);
 +
 +      LayerCollection *layer_collection_dst = layer_collections_dst->first;
 +      const LayerCollection *layer_collection_src = layer_collections_src->first;
 +
 +      while (layer_collection_dst != NULL) {
 +              layer_collections_copy_data(
 +                      view_layer_dst,
 +                      view_layer_src,
 +                      &layer_collection_dst->layer_collections,
 +                      &layer_collection_src->layer_collections);
 +
 +              if (layer_collection_src == view_layer_src->active_collection) {
 +                      view_layer_dst->active_collection = layer_collection_dst;
 +              }
 +
 +              layer_collection_dst = layer_collection_dst->next;
 +              layer_collection_src = layer_collection_src->next;
 +      }
 +}
 +
 +/**
 + * Only copy internal data of ViewLayer from source to already allocated/initialized destination.
 + *
++ * \param flag: Copying options (see BKE_library.h's LIB_ID_COPY_... flags for more).
 + */
 +void BKE_view_layer_copy_data(
 +        Scene *scene_dst, const Scene *UNUSED(scene_src),
 +        ViewLayer *view_layer_dst, const ViewLayer *view_layer_src,
 +        const int flag)
 +{
 +      if (view_layer_dst->id_properties != NULL) {
 +              view_layer_dst->id_properties = IDP_CopyProperty_ex(view_layer_dst->id_properties, flag);
 +      }
 +      BKE_freestyle_config_copy(&view_layer_dst->freestyle_config, &view_layer_src->freestyle_config, flag);
 +
 +      view_layer_dst->stats = NULL;
 +
 +      /* Clear temporary data. */
 +      BLI_listbase_clear(&view_layer_dst->drawdata);
 +      view_layer_dst->object_bases_array = NULL;
 +      view_layer_dst->object_bases_hash = NULL;
 +
 +      /* Copy layer collections and object bases. */
 +      /* Inline 'BLI_duplicatelist' and update the active base. */
 +      BLI_listbase_clear(&view_layer_dst->object_bases);
 +      for (Base *base_src = view_layer_src->object_bases.first; base_src; base_src = base_src->next) {
 +              Base *base_dst = MEM_dupallocN(base_src);
 +              BLI_addtail(&view_layer_dst->object_bases, base_dst);
 +              if (view_layer_src->basact == base_src) {
 +                      view_layer_dst->basact = base_dst;
 +              }
 +      }
 +
 +      view_layer_dst->active_collection = NULL;
 +      layer_collections_copy_data(
 +              view_layer_dst,
 +              view_layer_src,
 +              &view_layer_dst->layer_collections,
 +              &view_layer_src->layer_collections);
 +
 +      LayerCollection *lc_scene_dst = view_layer_dst->layer_collections.first;
 +      lc_scene_dst->collection = scene_dst->master_collection;
 +}
 +
 +void BKE_view_layer_rename(Main *bmain, Scene *scene, ViewLayer *view_layer, const char *newname)
 +{
 +      char oldname[sizeof(view_layer->name)];
 +
 +      BLI_strncpy(oldname, view_layer->name, sizeof(view_layer->name));
 +
 +      BLI_strncpy_utf8(view_layer->name, newname, sizeof(view_layer->name));
 +      BLI_uniquename(&scene->view_layers, view_layer, DATA_("ViewLayer"), '.', offsetof(ViewLayer, name), sizeof(view_layer->name));
 +
 +      if (scene->nodetree) {
 +              bNode *node;
 +              int index = BLI_findindex(&scene->view_layers, view_layer);
 +
 +              for (node = scene->nodetree->nodes.first; node; node = node->next) {
 +                      if (node->type == CMP_NODE_R_LAYERS && node->id == NULL) {
 +                              if (node->custom1 == index)
 +                                      BLI_strncpy(node->name, view_layer->name, NODE_MAXSTR);
 +                      }
 +              }
 +      }
 +
 +      /* Fix all the animation data and windows which may link to this. */
 +      BKE_animdata_fix_paths_rename_all(NULL, "view_layers", oldname, view_layer->name);
 +
 +      /* WM can be missing on startup. */
 +      wmWindowManager *wm = bmain->wm.first;
 +      if (wm) {
 +              for (wmWindow *win = wm->windows.first; win; win = win->next) {
 +                      if (win->scene == scene && STREQ(win->view_layer_name, oldname)) {
 +                              STRNCPY(win->view_layer_name, view_layer->name);
 +                      }
 +              }
 +      }
 +
 +      /* Dependency graph uses view layer name based lookups. */
 +      DEG_id_tag_update(&scene->id, 0);
 +}
 +
 +/* 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)++;
 +      }
 +
 +      for (LayerCollection *lc = lb->first; lc; lc = lc->next) {
 +              LayerCollection *lc_nested = collection_from_index(&lc->layer_collections, number, i);
 +              if (lc_nested) {
 +                      return lc_nested;
 +              }
 +      }
 +      return NULL;
 +}
 +
 +/**
 + * Get the collection for a given index
 + */
 +LayerCollection *BKE_layer_collection_from_index(ViewLayer *view_layer, const int index)
 +{
 +      int i = 0;
 +      return collection_from_index(&view_layer->layer_collections, index, &i);
 +}
 +
 +/**
 + * Get the active collection
 + */
 +LayerCollection *BKE_layer_collection_get_active(ViewLayer *view_layer)
 +{
 +      return view_layer->active_collection;
 +}
 +
 +/*
 + * Activate collection
 + */
 +bool BKE_layer_collection_activate(ViewLayer *view_layer, LayerCollection *lc)
 +{
 +      if (lc->flag & LAYER_COLLECTION_EXCLUDE) {
 +              return false;
 +      }
 +
 +      view_layer->active_collection = lc;
 +      return true;
 +}
 +
 +/**
 + * Activate first parent collection
 + */
 +LayerCollection *BKE_layer_collection_activate_parent(ViewLayer *view_layer, LayerCollection *lc)
 +{
 +      CollectionParent *parent = lc->collection->parents.first;
 +
 +      if (parent) {
 +              lc = BKE_layer_collection_first_from_scene_collection(view_layer, parent->collection);
 +      }
 +      else {
 +              lc = NULL;
 +      }
 +
 +      if (lc && (lc->flag & LAYER_COLLECTION_EXCLUDE)) {
 +              /* Don't activate excluded collections. */
 +              return BKE_layer_collection_activate_parent(view_layer, lc);
 +      }
 +
 +      if (!lc) {
 +              lc = view_layer->layer_collections.first;
 +      }
 +
 +      view_layer->active_collection = lc;
 +      return lc;
 +}
 +
 +/**
 + * 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(ViewLayer *view_layer)
 +{
 +      return collection_count(&view_layer->layer_collections);
 +}
 +
 +/**
 + * Recursively get the index for a given collection
 + */
 +static int index_from_collection(ListBase *lb, const LayerCollection *lc, int *i)
 +{
 +      for (LayerCollection *lcol = lb->first; lcol; lcol = lcol->next) {
 +              if (lcol == lc) {
 +                      return *i;
 +              }
 +
 +              (*i)++;
 +      }
 +
 +      for (LayerCollection *lcol = lb->first; lcol; lcol = lcol->next) {
 +              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(ViewLayer *view_layer, const LayerCollection *lc)
 +{
 +      int i = 0;
 +      return index_from_collection(&view_layer->layer_collections, lc, &i);
 +}
 +
 +/*********************************** Syncing *********************************
 + *
 + * The layer collection tree mirrors the scene collection tree. Whenever that
 + * changes we need to synchronize them so that there is a corresponding layer
 + * collection for each collection. Note that the scene collection tree can
 + * contain link or override collections, and so this is also called on .blend
 + * file load to ensure any new or removed collections are synced.
 + *
 + * The view layer also contains a list of bases for each object that exists
 + * in at least one layer collection. That list is also synchronized here, and
 + * stores state like selection. */
 +
 +static short layer_collection_sync(
 +        ViewLayer *view_layer, const ListBase *lb_scene,
 +        ListBase *lb_layer, ListBase *new_object_bases,
 +        short parent_exclude, short parent_restrict)
 +{
 +      /* TODO: support recovery after removal of intermediate collections, reordering, ..
 +       * For local edits we can make editing operating do the appropriate thing, but for
 +       * linking we can only sync after the fact. */
 +
 +      /* Remove layer collections that no longer have a corresponding scene collection. */
 +      for (LayerCollection *lc = lb_layer->first; lc;) {
 +              /* Note ID remap can set lc->collection to NULL when deleting collections. */
 +              LayerCollection *lc_next = lc->next;
 +              Collection *collection = (lc->collection) ?
 +                      BLI_findptr(lb_scene, lc->collection, offsetof(CollectionChild, collection)) : NULL;
 +
 +              if (!collection) {
 +                      if (lc == view_layer->active_collection) {
 +                              view_layer->active_collection = NULL;
 +                      }
 +
 +                      /* Free recursively. */
 +                      layer_collection_free(view_layer, lc);
 +                      BLI_freelinkN(lb_layer, lc);
 +              }
 +
 +              lc = lc_next;
 +      }
 +
 +      /* Add layer collections for any new scene collections, and ensure order is the same. */
 +      ListBase new_lb_layer = {NULL, NULL};
 +      short runtime_flag = 0;
 +
 +      for (const CollectionChild *child = lb_scene->first; child; child = child->next) {
 +              Collection *collection = child->collection;
 +              LayerCollection *lc = BLI_findptr(lb_layer, collection, offsetof(LayerCollection, collection));
 +
 +              if (lc) {
 +                      BLI_remlink(lb_layer, lc);
 +                      BLI_addtail(&new_lb_layer, lc);
 +              }
 +              else {
 +                      lc = layer_collection_add(&new_lb_layer, collection);
 +                      lc->flag = parent_exclude;
 +              }
 +
 +              /* Collection restrict is inherited. */
 +              short child_restrict = parent_restrict;
 +              if (!(collection->flag & COLLECTION_IS_MASTER)) {
 +                      child_restrict |= collection->flag;
 +              }
 +
 +              /* Sync child collections. */
 +              short child_runtime_flag = layer_collection_sync(
 +                      view_layer, &collection->children,
 +                      &lc->layer_collections, new_object_bases,
 +                      lc->flag, child_restrict);
 +
 +              /* Layer collection exclude is not inherited. */
 +              if (lc->flag & LAYER_COLLECTION_EXCLUDE) {
 +                      lc->runtime_flag = 0;
 +                      continue;
 +              }
 +              else {
 +                      lc->runtime_flag = child_runtime_flag;
 +              }
 +
 +              /* Sync objects, except if collection was excluded. */
 +              for (CollectionObject *cob = collection->gobject.first; cob; cob = cob->next) {
 +                      if (cob->ob == NULL) {
 +                              continue;
 +                      }
 +
 +                      void **base_p;
 +                      Base  *base;
 +                      if (BLI_ghash_ensure_p(view_layer->object_bases_hash, cob->ob, &base_p)) {
 +                              /* Move from old base list to new base list. Base might have already
 +                               * been moved to the new base list and the first/last test ensure that
 +                               * case also works. */
 +                              base = *base_p;
 +                              if (!ELEM(base, new_object_bases->first, new_object_bases->last)) {
 +                                      BLI_remlink(&view_layer->object_bases, base);
 +                                      BLI_addtail(new_object_bases, base);
 +                              }
 +                      }
 +                      else {
 +                              /* Create new base. */
 +                              base = object_base_new(cob->ob);
 +                              *base_p = base;
 +                              BLI_addtail(new_object_bases, base);
 +                      }
 +
 +                      int object_restrict = base->object->restrictflag;
 +
 +                      if (((child_restrict & COLLECTION_RESTRICT_VIEW) == 0) &&
 +                          ((object_restrict & OB_RESTRICT_VIEW) == 0))
 +                      {
 +                              base->flag |= BASE_VISIBLE | BASE_ENABLED | BASE_ENABLED_VIEWPORT;
 +
 +                              if (((child_restrict & COLLECTION_RESTRICT_SELECT) == 0) &&
 +                                  ((object_restrict & OB_RESTRICT_SELECT) == 0))
 +                              {
 +                                      base->flag |= BASE_SELECTABLE;
 +                              }
 +                      }
 +
 +                      if (((child_restrict & COLLECTION_RESTRICT_RENDER) == 0) &&
 +                          ((object_restrict & OB_RESTRICT_RENDER) == 0))
 +
 +                      {
 +                              base->flag |= BASE_ENABLED_RENDER;
 +                      }
 +
 +                      /* Update runtime flags used for display and tools. */
 +                      if (base->flag & BASE_VISIBLE) {
 +                              lc->runtime_flag |= LAYER_COLLECTION_HAS_ENABLED_OBJECTS;
 +                      }
 +
 +                      if (base->flag & BASE_HIDDEN) {
 +                              view_layer->runtime_flag |= VIEW_LAYER_HAS_HIDE;
 +                              lc->runtime_flag |= LAYER_COLLECTION_HAS_HIDDEN_OBJECTS;
 +                      }
 +                      else if (base->flag & BASE_VISIBLE) {
 +                              lc->runtime_flag |= LAYER_COLLECTION_HAS_VISIBLE_OBJECTS;
 +                      }
 +
 +                      /* Holdout and indirect only */
 +                      if (lc->flag & LAYER_COLLECTION_HOLDOUT) {
 +                              base->flag |= BASE_HOLDOUT;
 +                      }
 +                      if (lc->flag & LAYER_COLLECTION_INDIRECT_ONLY) {
 +                              base->flag |= BASE_INDIRECT_ONLY;
 +                      }
 +
 +                      lc->runtime_flag |= LAYER_COLLECTION_HAS_OBJECTS;
 +              }
 +
 +              runtime_flag |= lc->runtime_flag;
 +      }
 +
 +      /* Replace layer collection list with new one. */
 +      *lb_layer = new_lb_layer;
 +      BLI_assert(BLI_listbase_count(lb_scene) == BLI_listbase_count(lb_layer));
 +
 +      return runtime_flag;
 +}
 +
 +/**
 + * Update view layer collection tree from collections used in the scene.
 + * This is used when collections are removed or added, both while editing
 + * and on file loaded in case linked data changed or went missing.
 + */
 +void BKE_layer_collection_sync(const Scene *scene, ViewLayer *view_layer)
 +{
 +      if (!scene->master_collection) {
 +              /* Happens for old files that don't have versioning applied yet. */
 +              return;
 +      }
 +
 +      /* Free cache. */
 +      MEM_SAFE_FREE(view_layer->object_bases_array);
 +
 +      /* Create object to base hash if it does not exist yet. */
 +      if (!view_layer->object_bases_hash) {
 +              view_layer_bases_hash_create(view_layer);
 +      }
 +
 +      /* Clear visible and selectable flags to be reset. */
 +      for (Base *base = view_layer->object_bases.first; base; base = base->next) {
 +              base->flag &= ~(BASE_VISIBLE |
 +                              BASE_ENABLED |
 +                              BASE_SELECTABLE |
 +                              BASE_ENABLED_VIEWPORT |
 +                              BASE_ENABLED_RENDER |
 +                              BASE_HOLDOUT |
 +                              BASE_INDIRECT_ONLY);
 +      }
 +
 +      view_layer->runtime_flag = 0;
 +
 +      /* Generate new layer connections and object bases when collections changed. */
 +      CollectionChild child = {NULL, NULL, scene->master_collection};
 +      const ListBase collections = {&child, &child};
 +      ListBase new_object_bases = {NULL, NULL};
 +
 +      const short parent_exclude = 0, parent_restrict = 0;
 +      layer_collection_sync(
 +              view_layer, &collections,
 +              &view_layer->layer_collections, &new_object_bases,
 +              parent_exclude, parent_restrict);
 +
 +      /* Any remaning object bases are to be removed. */
 +      for (Base *base = view_layer->object_bases.first; base; base = base->next) {
 +              if (view_layer->basact == base) {
 +                      view_layer->basact = NULL;
 +              }
 +
 +              if (base->object) {
 +                      BLI_ghash_remove(view_layer->object_bases_hash, base->object, NULL, NULL);
 +              }
 +      }
 +
 +      BLI_freelistN(&view_layer->object_bases);
 +      view_layer->object_bases = new_object_bases;
 +
 +      /* Always set a valid active collection. */
 +      LayerCollection *active = view_layer->active_collection;
 +
 +      if (active && (active->flag & LAYER_COLLECTION_EXCLUDE)) {
 +              BKE_layer_collection_activate_parent(view_layer, active);
 +      }
 +      else if (active == NULL) {
 +              view_layer->active_collection = view_layer->layer_collections.first;
 +      }
 +}
 +
 +void BKE_scene_collection_sync(const Scene *scene)
 +{
 +      for (ViewLayer *view_layer = scene->view_layers.first; view_layer; view_layer = view_layer->next) {
 +              BKE_layer_collection_sync(scene, view_layer);
 +      }
 +}
 +
 +void BKE_main_collection_sync(const Main *bmain)
 +{
 +      /* TODO: if a single collection changed, figure out which
 +       * scenes it belongs to and only update those. */
 +
 +      /* TODO: optimize for file load so only linked collections get checked? */
 +
 +      for (const Scene *scene = bmain->scene.first; scene; scene = scene->id.next) {
 +              BKE_scene_collection_sync(scene);
 +      }
 +}
 +
 +void BKE_main_collection_sync_remap(const Main *bmain)
 +{
 +      /* On remapping of object or collection pointers free caches. */
 +      /* TODO: try to make this faster */
 +
 +      for (const Scene *scene = bmain->scene.first; scene; scene = scene->id.next) {
 +              for (ViewLayer *view_layer = scene->view_layers.first; view_layer; view_layer = view_layer->next) {
 +                      MEM_SAFE_FREE(view_layer->object_bases_array);
 +
 +                      if (view_layer->object_bases_hash) {
 +                              BLI_ghash_free(view_layer->object_bases_hash, NULL, NULL);
 +                              view_layer->object_bases_hash = NULL;
 +                      }
 +              }
 +      }
 +
 +      for (Collection *collection = bmain->collection.first; collection; collection = collection->id.next) {
 +              BKE_collection_object_cache_free(collection);
 +              DEG_id_tag_update_ex((Main *)bmain, &collection->id, ID_RECALC_COPY_ON_WRITE);
 +      }
 +
 +      BKE_main_collection_sync(bmain);
 +}
 +
 +/* ---------------------------------------------------------------------- */
 +
 +/**
 + * Select all the objects of this layer collection
 + *
 + * It also select the objects that are in nested collections.
 + * \note Recursive
 + */
 +bool BKE_layer_collection_objects_select(ViewLayer *view_layer, LayerCollection *lc, bool deselect)
 +{
 +      if (lc->collection->flag & COLLECTION_RESTRICT_SELECT) {
 +              return false;
 +      }
 +
 +      bool changed = false;
 +
 +      if (!(lc->flag & LAYER_COLLECTION_EXCLUDE)) {
 +              for (CollectionObject *cob = lc->collection->gobject.first; cob; cob = cob->next) {
 +                      Base *base = BKE_view_layer_base_find(view_layer, cob->ob);
 +
 +                      if (base) {
 +                              if (deselect) {
 +                                      if (base->flag & BASE_SELECTED) {
 +                                              base->flag &= ~BASE_SELECTED;
 +                                              changed = true;
 +                                      }
 +                              }
 +                              else {
 +                                      if ((base->flag & BASE_SELECTABLE) && !(base->flag & BASE_SELECTED)) {
 +                                              base->flag |= BASE_SELECTED;
 +                                              changed = true;
 +                                      }
 +                              }
 +                      }
 +              }
 +      }
 +
 +      for (LayerCollection *iter = lc->layer_collections.first; iter; iter = iter->next) {
 +              changed |= BKE_layer_collection_objects_select(view_layer, iter, deselect);
 +      }
 +
 +      return changed;
 +}
 +
 +bool BKE_layer_collection_has_selected_objects(ViewLayer *view_layer, LayerCollection *lc)
 +{
 +      if (lc->collection->flag & COLLECTION_RESTRICT_SELECT) {
 +              return false;
 +      }
 +
 +      if (!(lc->flag & LAYER_COLLECTION_EXCLUDE)) {
 +              for (CollectionObject *cob = lc->collection->gobject.first; cob; cob = cob->next) {
 +                      Base *base = BKE_view_layer_base_find(view_layer, cob->ob);
 +
 +                      if (base && (base->flag & BASE_SELECTED)) {
 +                              return true;
 +                      }
 +              }
 +      }
 +
 +      for (LayerCollection *iter = lc->layer_collections.first; iter; iter = iter->next) {
 +              if (BKE_layer_collection_has_selected_objects(view_layer, iter)) {
 +                      return true;
 +              }
 +      }
 +
 +      return false;
 +}
 +
 +/* ---------------------------------------------------------------------- */
 +
 +/* Update after toggling visibility of an object base. */
 +void BKE_base_set_visible(Scene *scene, ViewLayer *view_layer, Base *base, bool extend)
 +{
 +      if (!extend) {
 +              /* Make only one base visible. */
 +              for (Base *other = view_layer->object_bases.first; other; other = other->next) {
 +                      other->flag |= BASE_HIDDEN;
 +              }
 +
 +              base->flag &= ~BASE_HIDDEN;
 +      }
 +      else {
 +              /* Toggle visibility of one base. */
 +              base->flag ^= BASE_HIDDEN;
 +      }
 +
 +      BKE_layer_collection_sync(scene, view_layer);
 +}
 +
 +void BKE_layer_collection_set_visible(Scene *scene, ViewLayer *view_layer, LayerCollection *lc, bool extend)
 +{
 +      if (!extend) {
 +              /* Make only objects from one collection visible. */
 +              for (Base *base = view_layer->object_bases.first; base; base = base->next) {
 +                      base->flag |= BASE_HIDDEN;
 +              }
 +
 +              FOREACH_COLLECTION_OBJECT_RECURSIVE_BEGIN(lc->collection, ob)
 +              {
 +                      Base *base = BLI_ghash_lookup(view_layer->object_bases_hash, ob);
 +
 +                      if (base) {
 +                              base->flag &= ~BASE_HIDDEN;
 +                      }
 +              }
 +              FOREACH_COLLECTION_OBJECT_RECURSIVE_END;
 +
 +              BKE_layer_collection_activate(view_layer, lc);
 +      }
 +      else {
 +              /* Toggle visibility of objects from collection. */
 +              bool hide = (lc->runtime_flag & LAYER_COLLECTION_HAS_VISIBLE_OBJECTS) != 0;
 +
 +              FOREACH_COLLECTION_OBJECT_RECURSIVE_BEGIN(lc->collection, ob)
 +              {
 +                      Base *base = BLI_ghash_lookup(view_layer->object_bases_hash, ob);
 +
 +                      if (base) {
 +                              if (hide) {
 +                                      base->flag |= BASE_HIDDEN;
 +                              }
 +                              else {
 +                                      base->flag &= ~BASE_HIDDEN;
 +                              }
 +                      }
 +              }
 +              FOREACH_COLLECTION_OBJECT_RECURSIVE_END;
 +      }
 +
 +      BKE_layer_collection_sync(scene, view_layer);
 +}
 +
 +/* ---------------------------------------------------------------------- */
 +
 +static LayerCollection *find_layer_collection_by_scene_collection(LayerCollection *lc, const Collection *collection)
 +{
 +      if (lc->collection == collection) {
 +              return lc;
 +      }
 +
 +      for (LayerCollection *nlc = lc->layer_collections.first; nlc; nlc = nlc->next) {
 +              LayerCollection *found = find_layer_collection_by_scene_collection(nlc, collection);
 +              if (found) {
 +                      return found;
 +              }
 +      }
 +      return NULL;
 +}
 +
 +/**
 + * Return the first matching LayerCollection in the ViewLayer for the Collection.
 + */
 +LayerCollection *BKE_layer_collection_first_from_scene_collection(ViewLayer *view_layer, const Collection *collection)
 +{
 +      for (LayerCollection *layer_collection = view_layer->layer_collections.first;
 +           layer_collection != NULL;
 +           layer_collection = layer_collection->next)
 +      {
 +              LayerCollection *found = find_layer_collection_by_scene_collection(layer_collection, collection);
 +
 +              if (found != NULL) {
 +                      return found;
 +              }
 +      }
 +      return NULL;
 +}
 +
 +/**
 + * See if view layer has the scene collection linked directly, or indirectly (nested)
 + */
 +bool BKE_view_layer_has_collection(ViewLayer *view_layer, const Collection *collection)
 +{
 +      return BKE_layer_collection_first_from_scene_collection(view_layer, collection) != NULL;
 +}
 +
 +/**
 + * See if the object is in any of the scene layers of the scene
 + */
 +bool BKE_scene_has_object(Scene *scene, Object *ob)
 +{
 +      for (ViewLayer *view_layer = scene->view_layers.first; view_layer; view_layer = view_layer->next) {
 +              Base *base = BKE_view_layer_base_find(view_layer, ob);
 +              if (base) {
 +                      return true;
 +              }
 +      }
 +      return false;
 +}
 +
 +/* ---------------------------------------------------------------------- */
 +/* Override */
 +
 +/**
 + * Add a new datablock override
 + */
 +void BKE_override_view_layer_datablock_add(
 +        ViewLayer *view_layer, int id_type, const char *data_path, const ID *owner_id)
 +{
 +      UNUSED_VARS(view_layer, id_type, data_path, owner_id);
 +      TODO_LAYER_OVERRIDE;
 +}
 +
 +/**
 + * Add a new int override
 + */
 +void BKE_override_view_layer_int_add(
 +        ViewLayer *view_layer, int id_type, const char *data_path, const int value)
 +{
 +      UNUSED_VARS(view_layer, id_type, data_path, value);
 +      TODO_LAYER_OVERRIDE;
 +}
 +
 +/**
 + * Add a new boolean override
 + */
 +void BKE_override_layer_collection_boolean_add(
 +        struct LayerCollection *layer_collection, int id_type, const char *data_path, const bool value)
 +{
 +      UNUSED_VARS(layer_collection, id_type, data_path, value);
 +      TODO_LAYER_OVERRIDE;
 +}
 +
 +/** \} */
 +
 +/* Iterators */
 +
 +/* -------------------------------------------------------------------- */
 +/** \name Private Iterator Helpers
 + * \{ */
 +
 +typedef struct LayerObjectBaseIteratorData {
 +      View3D *v3d;
 +      Base *base;
 +} LayerObjectBaseIteratorData;
 +
 +static bool object_bases_iterator_is_valid_ex(View3D *v3d, Base *base, const int flag)
 +{
 +      if (v3d != NULL) {
 +              BLI_assert(v3d->spacetype == SPACE_VIEW3D);
 +              if ((v3d->object_type_exclude_viewport & (1 << base->object->type)) != 0) {
 +                      return false;
 +              }
 +
 +              if (v3d->localvd && ((base->local_view_bits & v3d->local_view_uuid) == 0)) {
 +                      return false;
 +              }
 +      }
 +
 +      if ((base->flag & flag) == 0) {
 +              return false;
 +      }
 +
 +      return true;
 +}
 +
 +static bool object_bases_iterator_is_valid(View3D *v3d, Base *base)
 +{
 +      return object_bases_iterator_is_valid_ex(v3d, base, ~(0));
 +}
 +
 +static void object_bases_iterator_begin(BLI_Iterator *iter, void *data_in_v, const int flag)
 +{
 +      ObjectsVisibleIteratorData *data_in = data_in_v;
 +      ViewLayer *view_layer = data_in->view_layer;
 +      View3D *v3d = data_in->v3d;
 +      Base *base = view_layer->object_bases.first;
 +
 +      /* when there are no objects */
 +      if (base ==  NULL) {
 +              iter->data = NULL;
 +              iter->valid = false;
 +              return;
 +      }
 +
 +      LayerObjectBaseIteratorData *data = MEM_callocN(sizeof(LayerObjectBaseIteratorData), __func__);
 +      iter->data = data;
 +
 +      data->v3d = v3d;
 +      data->base = base;
 +
 +      if (object_bases_iterator_is_valid_ex(v3d, base, flag) == false) {
 +              object_bases_iterator_next(iter, flag);
 +      }
 +      else {
 +              iter->current = base;
 +      }
 +}
 +
 +static void object_bases_iterator_next(BLI_Iterator *iter, const int flag)
 +{
 +      LayerObjectBaseIteratorData *data = iter->data;
 +      Base *base = data->base->next;
 +
 +      while (base) {
 +              if (object_bases_iterator_is_valid_ex(data->v3d, base, flag)) {
 +                      iter->current = base;
 +                      data->base = base;
 +                      return;
 +              }
 +              base = base->next;
 +      }
 +
 +      iter->valid = false;
 +}
 +
 +static void object_bases_iterator_end(BLI_Iterator *iter)
 +{
 +      MEM_SAFE_FREE(iter->data);
 +}
 +
 +static void objects_iterator_begin(BLI_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(BLI_Iterator *iter, const int flag)
 +{
 +      object_bases_iterator_next(iter, flag);
 +
 +      if (iter->valid) {
 +              iter->current = ((Base *)iter->current)->object;
 +      }
 +}
 +
 +static void objects_iterator_end(BLI_Iterator *iter)
 +{
 +      object_bases_iterator_end(iter);
 +}
 +
 +/* -------------------------------------------------------------------- */
 +/** \name BKE_view_layer_selected_objects_iterator
 + * See: #FOREACH_SELECTED_OBJECT_BEGIN
 + * \{ */
 +
 +void BKE_view_layer_selected_objects_iterator_begin(BLI_Iterator *iter, void *data_in)
 +{
 +      objects_iterator_begin(iter, data_in, BASE_SELECTED);
 +}
 +
 +void BKE_view_layer_selected_objects_iterator_next(BLI_Iterator *iter)
 +{
 +      objects_iterator_next(iter, BASE_SELECTED);
 +}
 +
 +void BKE_view_layer_selected_objects_iterator_end(BLI_Iterator *iter)
 +{
 +      objects_iterator_end(iter);
 +}
 +
 +/** \} */
 +
 +/* -------------------------------------------------------------------- */
 +/** \name BKE_view_layer_visible_objects_iterator
 + * \{ */
 +
 +void BKE_view_layer_visible_objects_iterator_begin(BLI_Iterator *iter, void *data_in)
 +{
 +      objects_iterator_begin(iter, data_in, BASE_VISIBLE);
 +}
 +
 +void BKE_view_layer_visible_objects_iterator_next(BLI_Iterator *iter)
 +{
 +      objects_iterator_next(iter, BASE_VISIBLE);
 +}
 +
 +void BKE_view_layer_visible_objects_iterator_end(BLI_Iterator *iter)
 +{
 +      objects_iterator_end(iter);
 +}
 +
 +/** \} */
 +
 +/* -------------------------------------------------------------------- */
 +/** \name BKE_view_layer_selected_editable_objects_iterator
 + * \{ */
 +
 +void BKE_view_layer_selected_editable_objects_iterator_begin(BLI_Iterator *iter, void *data_in)
 +{
 +      objects_iterator_begin(iter, data_in, BASE_SELECTED);
 +      if (iter->valid) {
 +              if (BKE_object_is_libdata((Object *)iter->current) == false) {
 +                      // First object is valid (selectable and not libdata) -> all good.
 +                      return;
 +              }
 +              else {
 +                      // Object is selectable but not editable -> search for another one.
 +                      BKE_view_layer_selected_editable_objects_iterator_next(iter);
 +              }
 +      }
 +}
 +
 +void BKE_view_layer_selected_editable_objects_iterator_next(BLI_Iterator *iter)
 +{
 +      // Search while there are objects and the one we have is not editable (editable = not libdata).
 +      do {
 +              objects_iterator_next(iter, BASE_SELECTED);
 +      } while (iter->valid && BKE_object_is_libdata((Object *)iter->current) != false);
 +}
 +
 +void BKE_view_layer_selected_editable_objects_iterator_end(BLI_Iterator *iter)
 +{
 +      objects_iterator_end(iter);
 +}
 +
 +/** \} */
 +
 +/* -------------------------------------------------------------------- */
 +/** \name BKE_view_layer_selected_bases_iterator
 + * \{ */
 +
 +void BKE_view_layer_selected_bases_iterator_begin(BLI_Iterator *iter, void *data_in)
 +{
 +      objects_iterator_begin(iter, data_in, BASE_SELECTED);
 +}
 +
 +void BKE_view_layer_selected_bases_iterator_next(BLI_Iterator *iter)
 +{
 +      object_bases_iterator_next(iter, BASE_SELECTED);
 +}
 +
 +void BKE_view_layer_selected_bases_iterator_end(BLI_Iterator *iter)
 +{
 +      object_bases_iterator_end(iter);
 +}
 +
 +/** \} */
 +
 +/* -------------------------------------------------------------------- */
 +/** \name BKE_view_layer_visible_bases_iterator
 + * \{ */
 +
 +void BKE_view_layer_visible_bases_iterator_begin(BLI_Iterator *iter, void *data_in)
 +{
 +      object_bases_iterator_begin(iter, data_in, BASE_VISIBLE);
 +}
 +
 +void BKE_view_layer_visible_bases_iterator_next(BLI_Iterator *iter)
 +{
 +      object_bases_iterator_next(iter, BASE_VISIBLE);
 +}
 +
 +void BKE_view_layer_visible_bases_iterator_end(BLI_Iterator *iter)
 +{
 +      object_bases_iterator_end(iter);
 +}
 +
 +/** \} */
 +
 +/* -------------------------------------------------------------------- */
 +/** \name BKE_view_layer_renderable_objects_iterator
 + * \{ */
 +
 +void BKE_view_layer_renderable_objects_iterator_begin(BLI_Iterator *iter, void *data_in)
 +{
 +      struct 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;
 +                      }
 +              }
 +      }
 +
 +      ViewLayer *view_layer = data->scene->view_layers.first;
 +      data->iter.view_layer = view_layer;
 +
 +      data->base_temp.next = view_layer->object_bases.first;
 +      data->iter.base = &data->base_temp;
 +
 +      data->iter.set = NULL;
 +
 +      iter->data = data_in;
 +      BKE_view_layer_renderable_objects_iterator_next(iter);
 +}
 +
 +void BKE_view_layer_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;
 +
 +      struct ObjectsRenderableIteratorData *data = iter->data;
 +      Base *base = data->iter.base->next;
 +
 +      /* There is still a base in the current scene layer. */
 +      if (base != NULL) {
 +              Object *ob = base->object;
 +
 +              /* 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 (ob->id.flag & LIB_TAG_DOIT) {
 +                      ob->id.flag &= ~LIB_TAG_DOIT;
 +
 +                      if ((base->flag & BASE_VISIBLE) != 0) {
 +                              iter->skip = false;
 +                              iter->current = ob;
 +                      }
 +              }
 +              return;
 +      }
 +
 +      /* Time to go to the next scene layer. */
 +      if (data->iter.set == NULL) {
 +              while ((data->iter.view_layer = data->iter.view_layer->next)) {
 +                      ViewLayer *view_layer = data->iter.view_layer;
 +                      if (view_layer->flag & VIEW_LAYER_RENDER) {
 +                              data->base_temp.next = view_layer->object_bases.first;
 +                              data->iter.base = &data->base_temp;
 +                              return;
 +                      }
 +              }
 +
 +              /* Setup the "set" for the next iteration. */
 +              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_default_render(data->iter.set);
 +              data->base_temp.next = view_layer->object_bases.first;
 +              data->iter.base = &data->base_temp;
 +              return;
 +      }
 +
 +      iter->valid = false;
 +}
 +
 +void BKE_view_layer_renderable_objects_iterator_end(BLI_Iterator *UNUSED(iter))
 +{
 +      /* Do nothing - iter->data was static allocated, we can't free it. */
 +}
 +
 +/** \} */
 +
 +/* -------------------------------------------------------------------- */
 +/** \name BKE_view_layer_bases_in_mode_iterator
 + * \{ */
 +
 +static bool base_is_in_mode(struct ObjectsInModeIteratorData *data, Base *base)
 +{
 +      return (base->object->type == data->object_type) && (base->object->mode & data->object_mode) != 0;
 +}
 +
 +void BKE_view_layer_bases_in_mode_iterator_begin(BLI_Iterator *iter, void *data_in)
 +{
 +      struct ObjectsInModeIteratorData *data = data_in;
 +      Base *base = data->base_active;
 +
 +      /* when there are no objects */
 +      if (base == NULL) {
 +              iter->valid = false;
 +              return;
 +      }
 +      iter->data = data_in;
 +      iter->current = base;
 +
 +      /* default type is active object type */
 +      if (data->object_type < 0) {
 +              data->object_type = base->object->type;
 +      }
 +
 +      if (object_bases_iterator_is_valid(data->v3d, base) == false || !base_is_in_mode(data, base)) {
 +              BKE_view_layer_bases_in_mode_iterator_next(iter);
 +      }
 +}
 +
 +void BKE_view_layer_bases_in_mode_iterator_next(BLI_Iterator *iter)
 +{
 +      struct ObjectsInModeIteratorData *data = iter->data;
 +      Base *base = iter->current;
 +
 +      if (base == data->base_active) {
 +              /* first step */
 +              base = data->view_layer->object_bases.first;
 +              if (base == data->base_active) {
 +                      base = base->next;
 +              }
 +      }
 +      else {
 +              base = base->next;
 +      }
 +
 +      while (base) {
 +              if ((base != data->base_active) &&
 +                  base_is_in_mode(data, base) &&
 +                  object_bases_iterator_is_valid(data->v3d, base))
 +              {
 +                      iter->current = base;
 +                      return;
 +              }
 +              base = base->next;
 +      }
 +      iter->valid = false;
 +}
 +
 +void BKE_view_layer_bases_in_mode_iterator_end(BLI_Iterator *UNUSED(iter))
 +{
 +      /* do nothing */
 +}
 +
 +/** \} */
 +
 +/* Evaluation  */
 +
 +void BKE_layer_eval_view_layer(
 +        struct Depsgraph *depsgraph,
 +        struct Scene *UNUSED(scene),
 +        ViewLayer *view_layer)
 +{
 +      DEG_debug_print_eval(depsgraph, __func__, view_layer->name, view_layer);
 +
 +      /* Visibility based on depsgraph mode. */
 +      const eEvaluationMode mode = DEG_get_mode(depsgraph);
 +      const int base_visible_flag = (mode == DAG_EVAL_VIEWPORT)
 +              ? BASE_ENABLED_VIEWPORT
 +              : BASE_ENABLED_RENDER;
 +      /* Create array of bases, for fast index-based lookup. */
 +      const int num_object_bases = BLI_listbase_count(&view_layer->object_bases);
 +      MEM_SAFE_FREE(view_layer->object_bases_array);
 +      view_layer->object_bases_array = MEM_malloc_arrayN(
 +              num_object_bases, sizeof(Base *), "view_layer->object_bases_array");
 +      int base_index = 0;
 +      for (Base *base = view_layer->object_bases.first; base; base = base->next) {
 +              /* Compute visibility for depsgraph evaluation mode. */
 +              if (base->flag & base_visible_flag) {
 +                      base->flag |= BASE_ENABLED | BASE_VISIBLE;
 +                      if (mode == DAG_EVAL_VIEWPORT && (base->flag & BASE_HIDDEN)) {
 +                              base->flag &= ~BASE_VISIBLE;
 +                      }
 +              }
 +              else {
 +                      base->flag &= ~(BASE_ENABLED | BASE_VISIBLE | BASE_SELECTABLE);
 +              }
 +              /* If base is not selectabled, clear select. */
 +              if ((base->flag & BASE_SELECTABLE) == 0) {
 +                      base->flag &= ~BASE_SELECTED;
 +              }
 +              view_layer->object_bases_array[base_index++] = base;
 +      }
 +      /* Flush back base flag to the original view layer for editing. */
 +      if (view_layer == DEG_get_evaluated_view_layer(depsgraph)) {
 +              ViewLayer *view_layer_orig = DEG_get_input_view_layer(depsgraph);
 +              Base *base_orig = view_layer_orig->object_bases.first;
 +              const Base *base_eval = view_layer->object_bases.first;
 +              while (base_orig != NULL) {
 +                      if (base_orig->flag & base_visible_flag) {
 +                              base_orig->flag = base_eval->flag;
 +                              base_eval = base_eval->next;
 +                      }
 +                      base_orig = base_orig->next;
 +              }
 +      }
 +}
 +
 +void BKE_layer_eval_view_layer_indexed(
 +        struct Depsgraph *depsgraph,
 +        struct Scene *scene,
 +        int view_layer_index)
 +{
 +      BLI_assert(view_layer_index >= 0);
 +      ViewLayer *view_layer = BLI_findlink(&scene->view_layers, view_layer_index);
 +      BLI_assert(view_layer != NULL);
 +      BKE_layer_eval_view_layer(depsgraph, scene, view_layer);
 +}
@@@ -1238,39 -1275,16 +1238,39 @@@ void BKE_libblock_init_empty(ID *id
        }
  }
  
 -/* by spec, animdata is first item after ID */
 -/* and, trust that BKE_animdata_from_id() will only find AnimData for valid ID-types */
 -static void id_copy_animdata(Main *bmain, ID *id, const bool do_action)
 +/** Generic helper to create a new empty datablock of given type in given \a bmain database.
 + *
-  * \param name can be NULL, in which case we get default name for this ID type. */
++ * \param name: can be NULL, in which case we get default name for this ID type. */
 +void *BKE_id_new(Main *bmain, const short type, const char *name)
  {
 -      AnimData *adt = BKE_animdata_from_id(id);
 +      BLI_assert(bmain != NULL);
  
 -      if (adt) {
 -              IdAdtTemplate *iat = (IdAdtTemplate *)id;
 -              iat->adt = BKE_animdata_copy(bmain, iat->adt, do_action); /* could be set to false, need to investigate */
 +      if (name == NULL) {
 +              name = DATA_(BKE_idcode_to_name(type));
        }
-  * \param name can be NULL, in which case we get default name for this ID type. */
 +
 +      ID *id = BKE_libblock_alloc(bmain, type, name, 0);
 +      BKE_libblock_init_empty(id);
 +
 +      return id;
 +}
 +
 +/** Generic helper to create a new temporary empty datablock of given type, *outside* of any Main database.
 + *
++ * \param name: can be NULL, in which case we get default name for this ID type. */
 +void *BKE_id_new_nomain(const short type, const char *name)
 +{
 +      if (name == NULL) {
 +              name = DATA_(BKE_idcode_to_name(type));
 +      }
 +
 +      ID *id = BKE_libblock_alloc(NULL, type, name,
 +                                  LIB_ID_CREATE_NO_MAIN |
 +                                  LIB_ID_CREATE_NO_USER_REFCOUNT |
 +                                  LIB_ID_CREATE_NO_DEG_TAG);
 +      BKE_libblock_init_empty(id);
 +
 +      return id;
  }
  
  void BKE_libblock_copy_ex(Main *bmain, const ID *id, ID **r_newid, const int flag)
@@@ -2134,39 -2368,12 +2134,39 @@@ void BKE_libblock_rename(Main *bmain, I
  }
  
  /**
 - * Returns in name the name of the block, with a 3-character prefix prepended
 - * indicating whether it comes from a library, has a fake user, or no users.
 + * Generate full name of the data-block (without ID code, but with library if any).
 + *
 + * \note Result is unique to a given ID type in a given Main database.
 + *
-  * \param name An allocated string of minimal length MAX_ID_FULL_NAME, will be filled with generated string.
++ * \param name: An allocated string of minimal length MAX_ID_FULL_NAME, will be filled with generated string.
   */
 -void BKE_id_ui_prefix(char name[MAX_ID_NAME + 1], const ID *id)
 +void BKE_id_full_name_get(char name[MAX_ID_FULL_NAME], const ID *id)
  {
 -      name[0] = id->lib ? (ID_MISSING(id) ? 'M' : 'L') : ' ';
 +      strcpy(name, id->name + 2);
 +
 +      if (id->lib != NULL) {
 +              const size_t idname_len = strlen(id->name + 2);
 +              const size_t libname_len = strlen(id->lib->id.name + 2);
 +
 +              name[idname_len] = ' ';
 +              name[idname_len + 1] = '[';
 +              strcpy(name + idname_len + 2, id->lib->id.name + 2);
 +              name[idname_len + 2 + libname_len] = ']';
 +              name[idname_len + 2 + libname_len + 1] = '\0';
 +      }
 +}
 +
 +/**
 + * Generate full name of the data-block (without ID code, but with library if any), with a 3-character prefix prepended
 + * indicating whether it comes from a library, is overriding, has a fake or no user, etc.
 + *
 + * \note Result is unique to a given ID type in a given Main database.
 + *
-  * \param name An allocated string of minimal length MAX_ID_FULL_NAME_UI, will be filled with generated string.
++ * \param name: An allocated string of minimal length MAX_ID_FULL_NAME_UI, will be filled with generated string.
 + */
 +void BKE_id_full_name_ui_prefix_get(char name[MAX_ID_FULL_NAME_UI], const ID *id)
 +{
 +      name[0] = id->lib ? (ID_MISSING(id) ? 'M' : 'L') : ID_IS_STATIC_OVERRIDE(id) ? 'O' : ' ';
        name[1] = (id->flag & LIB_FAKEUSER) ? 'F' : ((id->us == 0) ? '0' : ' ');
        name[2] = ' ';
  
index 4cb2b45,0000000..5017827
mode 100644,000000..100644
--- /dev/null
@@@ -1,101 -1,0 +1,101 @@@
-  * \param flag  Copying options (see BKE_library.h's LIB_ID_COPY_... flags for more).
 +/*
 + * ***** 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.
 + *
 + * The Original Code is Copyright (C) Blender Foundation.
 + * All rights reserved.
 + *
 + * The Original Code is: all of this file.
 + *
 + * Contributor(s): none yet.
 + *
 + * ***** END GPL LICENSE BLOCK *****
 + */
 +
 +/** \file blender/blenkernel/intern/lightprobe.c
 + *  \ingroup bke
 + */
 +
 +#include "DNA_object_types.h"
 +#include "DNA_lightprobe_types.h"
 +
 +#include "BLI_math.h"
 +#include "BLI_utildefines.h"
 +
 +#include "BKE_animsys.h"
 +#include "BKE_global.h"
 +#include "BKE_library.h"
 +#include "BKE_lightprobe.h"
 +#include "BKE_main.h"
 +
 +void BKE_lightprobe_init(LightProbe *probe)
 +{
 +      BLI_assert(MEMCMP_STRUCT_OFS_IS_ZERO(probe, id));
 +
 +      probe->grid_resolution_x = probe->grid_resolution_y = probe->grid_resolution_z = 4;
 +      probe->distinf = 2.5f;
 +      probe->distpar = 2.5f;
 +      probe->falloff = 0.2f;
 +      probe->clipsta = 0.8f;
 +      probe->clipend = 40.0f;
 +      probe->vis_bias = 1.0f;
 +      probe->vis_blur = 0.2f;
 +      probe->intensity = 1.0f;
 +
 +      probe->flag = LIGHTPROBE_FLAG_SHOW_INFLUENCE | LIGHTPROBE_FLAG_SHOW_DATA;
 +}
 +
 +void *BKE_lightprobe_add(Main *bmain, const char *name)
 +{
 +      LightProbe *probe;
 +
 +      probe =  BKE_libblock_alloc(bmain, ID_LP, name, 0);
 +
 +      BKE_lightprobe_init(probe);
 +
 +      return probe;
 +}
 +
 +/**
 + * Only copy internal data of LightProbe ID from source to already allocated/initialized destination.
 + * You probably nerver want to use that directly, use id_copy or BKE_id_copy_ex for typical needs.
 + *
 + * WARNING! This function will not handle ID user count!
 + *
++ * \param flag: Copying options (see BKE_library.h's LIB_ID_COPY_... flags for more).
 + */
 +void BKE_lightprobe_copy_data(
 +        Main *UNUSED(bmain), LightProbe *UNUSED(probe_dst), const LightProbe *UNUSED(probe_src), const int UNUSED(flag))
 +{
 +      /* Nothing to do here. */
 +}
 +
 +LightProbe *BKE_lightprobe_copy(Main *bmain, const LightProbe *probe)
 +{
 +      LightProbe *probe_copy;
 +      BKE_id_copy_ex(bmain, &probe->id, (ID **)&probe_copy, 0, false);
 +      return probe_copy;
 +}
 +
 +void BKE_lightprobe_make_local(Main *bmain, LightProbe *probe, const bool lib_local)
 +{
 +      BKE_id_make_local_generic(bmain, &probe->id, true, lib_local);
 +}
 +
 +void BKE_lightprobe_free(LightProbe *probe)
 +{
 +      BKE_animdata_free((ID *)probe, false);
 +}
index c063af4,0000000..e40a1eb
mode 100644,000000..100644
--- /dev/null
@@@ -1,445 -1,0 +1,445 @@@
-  * \param bmain If not NULL, also store generated data in this Main.
-  * \param img ImBuf image to generate thumbnail data from.
 +/*
 + * ***** 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.
 + *
 + * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
 + * All rights reserved.
 + *
 + * The Original Code is: all of this file.
 + *
 + * Contributor(s): none yet.
 + *
 + * ***** END GPL LICENSE BLOCK *****
 + */
 +
 +/** \file blender/blenkernel/intern/main.c
 + *  \ingroup bke
 + *
 + * Contains management of Main database itself.
 + */
 +
 +#include <string.h>
 +
 +#include "MEM_guardedalloc.h"
 +
 +#include "BLI_blenlib.h"
 +#include "BLI_ghash.h"
 +#include "BLI_mempool.h"
 +#include "BLI_threads.h"
 +
 +#include "DNA_ID.h"
 +
 +#include "BKE_global.h"
 +#include "BKE_library.h"
 +#include "BKE_library_query.h"
 +#include "BKE_main.h"
 +
 +#include "IMB_imbuf.h"
 +#include "IMB_imbuf_types.h"
 +
 +Main *BKE_main_new(void)
 +{
 +      Main *bmain = MEM_callocN(sizeof(Main), "new main");
 +      bmain->lock = MEM_mallocN(sizeof(SpinLock), "main lock");
 +      BLI_spin_init((SpinLock *)bmain->lock);
 +      return bmain;
 +}
 +
 +void BKE_main_free(Main *mainvar)
 +{
 +      /* also call when reading a file, erase all, etc */
 +      ListBase *lbarray[MAX_LIBARRAY];
 +      int a;
 +
 +      MEM_SAFE_FREE(mainvar->blen_thumb);
 +
 +      a = set_listbasepointers(mainvar, lbarray);
 +      while (a--) {
 +              ListBase *lb = lbarray[a];
 +              ID *id;
 +
 +              while ( (id = lb->first) ) {
 +#if 1
 +                      BKE_libblock_free_ex(mainvar, id, false, false);
 +#else
 +                      /* errors freeing ID's can be hard to track down,
 +                       * enable this so valgrind will give the line number in its error log */
 +                      switch (a) {
 +                              case   0: BKE_libblock_free_ex(mainvar, id, false, false); break;
 +                              case   1: BKE_libblock_free_ex(mainvar, id, false, false); break;
 +                              case   2: BKE_libblock_free_ex(mainvar, id, false, false); break;
 +                              case   3: BKE_libblock_free_ex(mainvar, id, false, false); break;
 +                              case   4: BKE_libblock_free_ex(mainvar, id, false, false); break;
 +                              case   5: BKE_libblock_free_ex(mainvar, id, false, false); break;
 +                              case   6: BKE_libblock_free_ex(mainvar, id, false, false); break;
 +                              case   7: BKE_libblock_free_ex(mainvar, id, false, false); break;
 +                              case   8: BKE_libblock_free_ex(mainvar, id, false, false); break;
 +                              case   9: BKE_libblock_free_ex(mainvar, id, false, false); break;
 +                              case  10: BKE_libblock_free_ex(mainvar, id, false, false); break;
 +                              case  11: BKE_libblock_free_ex(mainvar, id, false, false); break;
 +                              case  12: BKE_libblock_free_ex(mainvar, id, false, false); break;
 +                              case  13: BKE_libblock_free_ex(mainvar, id, false, false); break;
 +                              case  14: BKE_libblock_free_ex(mainvar, id, false, false); break;
 +                              case  15: BKE_libblock_free_ex(mainvar, id, false, false); break;
 +                              case  16: BKE_libblock_free_ex(mainvar, id, false, false); break;
 +                              case  17: BKE_libblock_free_ex(mainvar, id, false, false); break;
 +                              case  18: BKE_libblock_free_ex(mainvar, id, false, false); break;
 +                              case  19: BKE_libblock_free_ex(mainvar, id, false, false); break;
 +                              case  20: BKE_libblock_free_ex(mainvar, id, false, false); break;
 +                              case  21: BKE_libblock_free_ex(mainvar, id, false, false); break;
 +                              case  22: BKE_libblock_free_ex(mainvar, id, false, false); break;
 +                              case  23: BKE_libblock_free_ex(mainvar, id, false, false); break;
 +                              case  24: BKE_libblock_free_ex(mainvar, id, false, false); break;
 +                              case  25: BKE_libblock_free_ex(mainvar, id, false, false); break;
 +                              case  26: BKE_libblock_free_ex(mainvar, id, false, false); break;
 +                              case  27: BKE_libblock_free_ex(mainvar, id, false, false); break;
 +                              case  28: BKE_libblock_free_ex(mainvar, id, false, false); break;
 +                              case  29: BKE_libblock_free_ex(mainvar, id, false, false); break;
 +                              case  30: BKE_libblock_free_ex(mainvar, id, false, false); break;
 +                              case  31: BKE_libblock_free_ex(mainvar, id, false, false); break;
 +                              case  32: BKE_libblock_free_ex(mainvar, id, false, false); break;
 +                              case  33: BKE_libblock_free_ex(mainvar, id, false, false); break;
 +                              case  34: BKE_libblock_free_ex(mainvar, id, false, false); break;
 +                              default:
 +                                      BLI_assert(0);
 +                                      break;
 +                      }
 +#endif
 +              }
 +      }
 +
 +      if (mainvar->relations) {
 +              BKE_main_relations_free(mainvar);
 +      }
 +
 +      BLI_spin_end((SpinLock *)mainvar->lock);
 +      MEM_freeN(mainvar->lock);
 +      MEM_freeN(mainvar);
 +}
 +
 +void BKE_main_lock(struct Main *bmain)
 +{
 +      BLI_spin_lock((SpinLock *) bmain->lock);
 +}
 +
 +void BKE_main_unlock(struct Main *bmain)
 +{
 +      BLI_spin_unlock((SpinLock *) bmain->lock);
 +}
 +
 +
 +static int main_relations_create_cb(void *user_data, ID *id_self, ID **id_pointer, int cb_flag)
 +{
 +      MainIDRelations *rel = user_data;
 +
 +      if (*id_pointer) {
 +              MainIDRelationsEntry *entry, **entry_p;
 +
 +              entry = BLI_mempool_alloc(rel->entry_pool);
 +              if (BLI_ghash_ensure_p(rel->id_user_to_used, id_self, (void ***)&entry_p)) {
 +                      entry->next = *entry_p;
 +              }
 +              else {
 +                      entry->next = NULL;
 +              }
 +              entry->id_pointer = id_pointer;
 +              entry->usage_flag = cb_flag;
 +              *entry_p = entry;
 +
 +              entry = BLI_mempool_alloc(rel->entry_pool);
 +              if (BLI_ghash_ensure_p(rel->id_used_to_user, *id_pointer, (void ***)&entry_p)) {
 +                      entry->next = *entry_p;
 +              }
 +              else {
 +                      entry->next = NULL;
 +              }
 +              entry->id_pointer = (ID **)id_self;
 +              entry->usage_flag = cb_flag;
 +              *entry_p = entry;
 +      }
 +
 +      return IDWALK_RET_NOP;
 +}
 +
 +/** Generate the mappings between used IDs and their users, and vice-versa. */
 +void BKE_main_relations_create(Main *bmain)
 +{
 +      ListBase *lbarray[MAX_LIBARRAY];
 +      ID *id;
 +      int a;
 +
 +      if (bmain->relations != NULL) {
 +              BKE_main_relations_free(bmain);
 +      }
 +
 +      bmain->relations = MEM_mallocN(sizeof(*bmain->relations), __func__);
 +      bmain->relations->id_used_to_user = BLI_ghash_new(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp, __func__);
 +      bmain->relations->id_user_to_used = BLI_ghash_new(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp, __func__);
 +      bmain->relations->entry_pool = BLI_mempool_create(sizeof(MainIDRelationsEntry), 128, 128, BLI_MEMPOOL_NOP);
 +
 +      for (a = set_listbasepointers(bmain, lbarray); a--; ) {
 +              for (id = lbarray[a]->first; id; id = id->next) {
 +                      BKE_library_foreach_ID_link(NULL, id, main_relations_create_cb, bmain->relations, IDWALK_READONLY);
 +              }
 +      }
 +}
 +
 +void BKE_main_relations_free(Main *bmain)
 +{
 +      if (bmain->relations) {
 +              if (bmain->relations->id_used_to_user) {
 +                      BLI_ghash_free(bmain->relations->id_used_to_user, NULL, NULL);
 +              }
 +              if (bmain->relations->id_user_to_used) {
 +                      BLI_ghash_free(bmain->relations->id_user_to_used, NULL, NULL);
 +              }
 +              BLI_mempool_destroy(bmain->relations->entry_pool);
 +              MEM_freeN(bmain->relations);
 +              bmain->relations = NULL;
 +      }
 +}
 +
 +/**
 + * Generates a raw .blend file thumbnail data from given image.
 + *
-  * \param bmain Use this bmain->blen_thumb data if given \a data is NULL.
-  * \param data Raw .blend file thumbnail data.
++ * \param bmain: If not NULL, also store generated data in this Main.
++ * \param img: ImBuf image to generate thumbnail data from.
 + * \return The generated .blend file raw thumbnail data.
 + */
 +BlendThumbnail *BKE_main_thumbnail_from_imbuf(Main *bmain, ImBuf *img)
 +{
 +      BlendThumbnail *data = NULL;
 +
 +      if (bmain) {
 +              MEM_SAFE_FREE(bmain->blen_thumb);
 +      }
 +
 +      if (img) {
 +              const size_t sz = BLEN_THUMB_MEMSIZE(img->x, img->y);
 +              data = MEM_mallocN(sz, __func__);
 +
 +              IMB_rect_from_float(img);  /* Just in case... */
 +              data->width = img->x;
 +              data->height = img->y;
 +              memcpy(data->rect, img->rect, sz - sizeof(*data));
 +      }
 +
 +      if (bmain) {
 +              bmain->blen_thumb = data;
 +      }
 +      return data;
 +}
 +
 +/**
 + * Generates an image from raw .blend file thumbnail \a data.
 + *
++ * \param bmain: Use this bmain->blen_thumb data if given \a data is NULL.
++ * \param data: Raw .blend file thumbnail data.
 + * \return An ImBuf from given data, or NULL if invalid.
 + */
 +ImBuf *BKE_main_thumbnail_to_imbuf(Main *bmain, BlendThumbnail *data)
 +{
 +      ImBuf *img = NULL;
 +
 +      if (!data && bmain) {
 +              data = bmain->blen_thumb;
 +      }
 +
 +      if (data) {
 +              /* Note: we cannot use IMB_allocFromBuffer(), since it tries to dupalloc passed buffer, which will fail
 +               *       here (we do not want to pass the first two ints!). */
 +              img = IMB_allocImBuf((unsigned int)data->width, (unsigned int)data->height, 32, IB_rect | IB_metadata);
 +              memcpy(img->rect, data->rect, BLEN_THUMB_MEMSIZE(data->width, data->height) - sizeof(*data));
 +      }
 +
 +      return img;
 +}
 +
 +/**
 + * Generates an empty (black) thumbnail for given Main.
 + */
 +void BKE_main_thumbnail_create(struct Main *bmain)
 +{
 +      MEM_SAFE_FREE(bmain->blen_thumb);
 +
 +      bmain->blen_thumb = MEM_callocN(BLEN_THUMB_MEMSIZE(BLEN_THUMB_SIZE, BLEN_THUMB_SIZE), __func__);
 +      bmain->blen_thumb->width = BLEN_THUMB_SIZE;
 +      bmain->blen_thumb->height = BLEN_THUMB_SIZE;
 +}
 +
 +/**
 + * Return filepath of given \a main.
 + */
 +const char *BKE_main_blendfile_path(const Main *bmain)
 +{
 +      return bmain->name;
 +}
 +
 +/**
 + * Return filepath of global main (G_MAIN).
 + *
 + * \warning Usage is not recommended, you should always try to get a valid Main pointer from context...
 + */
 +const char *BKE_main_blendfile_path_from_global(void)
 +{
 +      return BKE_main_blendfile_path(G_MAIN);
 +}
 +
 +/**
 + * \return A pointer to the \a ListBase of given \a bmain for requested \a type ID type.
 + */
 +ListBase *which_libbase(Main *bmain, short type)
 +{
 +      switch ((ID_Type)type) {
 +              case ID_SCE:
 +                      return &(bmain->scene);
 +              case ID_LI:
 +                      return &(bmain->library);
 +              case ID_OB:
 +                      return &(bmain->object);
 +              case ID_ME:
 +                      return &(bmain->mesh);
 +              case ID_CU:
 +                      return &(bmain->curve);
 +              case ID_MB:
 +                      return &(bmain->mball);
 +              case ID_MA:
 +                      return &(bmain->mat);
 +              case ID_TE:
 +                      return &(bmain->tex);
 +              case ID_IM:
 +                      return &(bmain->image);
 +              case ID_LT:
 +                      return &(bmain->latt);
 +              case ID_LA:
 +                      return &(bmain->lamp);
 +              case ID_CA:
 +                      return &(bmain->camera);
 +              case ID_IP:
 +                      return &(bmain->ipo);
 +              case ID_KE:
 +                      return &(bmain->key);
 +              case ID_WO:
 +                      return &(bmain->world);
 +              case ID_SCR:
 +                      return &(bmain->screen);
 +              case ID_VF:
 +                      return &(bmain->vfont);
 +              case ID_TXT:
 +                      return &(bmain->text);
 +              case ID_SPK:
 +                      return &(bmain->speaker);
 +              case ID_LP:
 +                      return &(bmain->lightprobe);
 +              case ID_SO:
 +                      return &(bmain->sound);
 +              case ID_GR:
 +                      return &(bmain->collection);
 +              case ID_AR:
 +                      return &(bmain->armature);
 +              case ID_AC:
 +                      return &(bmain->action);
 +              case ID_NT:
 +                      return &(bmain->nodetree);
 +              case ID_BR:
 +                      return &(bmain->brush);
 +              case ID_PA:
 +                      return &(bmain->particle);
 +              case ID_WM:
 +                      return &(bmain->wm);
 +              case ID_GD:
 +                      return &(bmain->gpencil);
 +              case ID_MC:
 +                      return &(bmain->movieclip);
 +              case ID_MSK:
 +                      return &(bmain->mask);
 +              case ID_LS:
 +                      return &(bmain->linestyle);
 +              case ID_PAL:
 +                      return &(bmain->palettes);
 +              case ID_PC:
 +                      return &(bmain->paintcurves);
 +              case ID_CF:
 +                      return &(bmain->cachefiles);
 +              case ID_WS:
 +                      return &(bmain->workspaces);
 +      }
 +      return NULL;
 +}
 +
 +/**
 + * puts into array *lb pointers to all the ListBase structs in main,
 + * and returns the number of them as the function result. This is useful for
 + * generic traversal of all the blocks in a Main (by traversing all the
 + * lists in turn), without worrying about block types.
 + *
 + * \note MAX_LIBARRAY define should match this code */
 +int set_listbasepointers(Main *bmain, ListBase **lb)
 +{
 +      /* BACKWARDS! also watch order of free-ing! (mesh<->mat), first items freed last.
 +       * This is important because freeing data decreases usercounts of other datablocks,
 +       * if this data is its self freed it can crash. */
 +      lb[INDEX_ID_LI] = &(bmain->library);  /* Libraries may be accessed from pretty much any other ID... */
 +      lb[INDEX_ID_IP] = &(bmain->ipo);
 +      lb[INDEX_ID_AC] = &(bmain->action); /* moved here to avoid problems when freeing with animato (aligorith) */
 +      lb[INDEX_ID_KE] = &(bmain->key);
 +      lb[INDEX_ID_PAL] = &(bmain->palettes); /* referenced by gpencil, so needs to be before that to avoid crashes */
 +      lb[INDEX_ID_GD] = &(bmain->gpencil); /* referenced by nodes, objects, view, scene etc, before to free after. */
 +      lb[INDEX_ID_NT] = &(bmain->nodetree);
 +      lb[INDEX_ID_IM] = &(bmain->image);
 +      lb[INDEX_ID_TE] = &(bmain->tex);
 +      lb[INDEX_ID_MA] = &(bmain->mat);
 +      lb[INDEX_ID_VF] = &(bmain->vfont);
 +
 +      /* Important!: When adding a new object type,
 +       * the specific data should be inserted here
 +       */
 +
 +      lb[INDEX_ID_AR] = &(bmain->armature);
 +
 +      lb[INDEX_ID_CF] = &(bmain->cachefiles);
 +      lb[INDEX_ID_ME] = &(bmain->mesh);
 +      lb[INDEX_ID_CU] = &(bmain->curve);
 +      lb[INDEX_ID_MB] = &(bmain->mball);
 +
 +      lb[INDEX_ID_LT] = &(bmain->latt);
 +      lb[INDEX_ID_LA] = &(bmain->lamp);
 +      lb[INDEX_ID_CA] = &(bmain->camera);
 +
 +      lb[INDEX_ID_TXT] = &(bmain->text);
 +      lb[INDEX_ID_SO]  = &(bmain->sound);
 +      lb[INDEX_ID_GR]  = &(bmain->collection);
 +      lb[INDEX_ID_PAL] = &(bmain->palettes);
 +      lb[INDEX_ID_PC]  = &(bmain->paintcurves);
 +      lb[INDEX_ID_BR]  = &(bmain->brush);
 +      lb[INDEX_ID_PA]  = &(bmain->particle);
 +      lb[INDEX_ID_SPK] = &(bmain->speaker);
 +      lb[INDEX_ID_LP]  = &(bmain->lightprobe);
 +
 +      lb[INDEX_ID_WO]  = &(bmain->world);
 +      lb[INDEX_ID_MC]  = &(bmain->movieclip);
 +      lb[INDEX_ID_SCR] = &(bmain->screen);
 +      lb[INDEX_ID_OB]  = &(bmain->object);
 +      lb[INDEX_ID_LS]  = &(bmain->linestyle); /* referenced by scenes */
 +      lb[INDEX_ID_SCE] = &(bmain->scene);
 +      lb[INDEX_ID_WS]  = &(bmain->workspaces); /* before wm, so it's freed after it! */
 +      lb[INDEX_ID_WM]  = &(bmain->wm);
 +      lb[INDEX_ID_MSK] = &(bmain->mask);
 +
 +      lb[INDEX_ID_NULL] = NULL;
 +
 +      return (MAX_LIBARRAY - 1);
 +}
index 5324c39,0000000..b88fd5d
mode 100644,000000..100644
--- /dev/null
@@@ -1,685 -1,0 +1,685 @@@
-  * \param vtargetmap  The table that maps vertices to target vertices.  a value of -1
 +/*
 + * ***** 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.
 + *
 + * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
 + * All rights reserved.
 + *
 + * Contributor(s): Blender Foundation
 + *
 + * ***** END GPL LICENSE BLOCK *****
 + */
 +
 +/** \file blender/blenkernel/intern/mesh_merge.c
 + *  \ingroup bke
 + */
 +#include <string.h> // for memcpy
 +
 +#include "MEM_guardedalloc.h"
 +
 +#include "DNA_mesh_types.h"
 +#include "DNA_meshdata_types.h"
 +
 +#include "BLI_utildefines.h"
 +#include "BLI_utildefines_stack.h"
 +#include "BLI_edgehash.h"
 +#include "BLI_ghash.h"
 +
 +#include "BKE_customdata.h"
 +#include "BKE_library.h"
 +#include "BKE_mesh.h"
 +#include "BKE_mesh_mapping.h"
 +
 +
 +/**
 + * Poly compare with vtargetmap
 + * Function used by #BKE_mesh_merge_verts.
 + * The function compares poly_source after applying vtargetmap, with poly_target.
 + * The two polys are identical if they share the same vertices in the same order, or in reverse order,
 + * but starting position loopstart may be different.
 + * The function is called with direct_reverse=1 for same order (i.e. same normal),
 + * and may be called again with direct_reverse=-1 for reverse order.
 + * \return 1 if polys are identical,  0 if polys are different.
 + */
 +static int cddm_poly_compare(
 +        MLoop *mloop_array,
 +        MPoly *mpoly_source, MPoly *mpoly_target,
 +        const int *vtargetmap, const int direct_reverse)
 +{
 +      int vert_source, first_vert_source, vert_target;
 +      int i_loop_source;
 +      int i_loop_target, i_loop_target_start, i_loop_target_offset, i_loop_target_adjusted;
 +      bool compare_completed = false;
 +      bool same_loops = false;
 +
 +      MLoop *mloop_source, *mloop_target;
 +
 +      BLI_assert(direct_reverse == 1 || direct_reverse == -1);
 +
 +      i_loop_source = 0;
 +      mloop_source = mloop_array + mpoly_source->loopstart;
 +      vert_source = mloop_source->v;
 +
 +      if (vtargetmap[vert_source] != -1) {
 +              vert_source = vtargetmap[vert_source];
 +      }
 +      else {
 +              /* All source loop vertices should be mapped */
 +              BLI_assert(false);
 +      }
 +
 +      /* Find same vertex within mpoly_target's loops */
 +      mloop_target = mloop_array + mpoly_target->loopstart;
 +      for (i_loop_target = 0; i_loop_target < mpoly_target->totloop; i_loop_target++, mloop_target++) {
 +              if (mloop_target->v == vert_source) {
 +                      break;
 +              }
 +      }
 +
 +      /* If same vertex not found, then polys cannot be equal */
 +      if (i_loop_target >= mpoly_target->totloop) {
 +              return false;
 +      }
 +
 +      /* Now mloop_source and m_loop_target have one identical vertex */
 +      /* mloop_source is at position 0, while m_loop_target has advanced to find identical vertex */
 +      /* Go around the loop and check that all vertices match in same order */
 +      /* Skipping source loops when consecutive source vertices are mapped to same target vertex */
 +
 +      i_loop_target_start = i_loop_target;
 +      i_loop_target_offset = 0;
 +      first_vert_source = vert_source;
 +
 +      compare_completed = false;
 +      same_loops = false;
 +
 +      while (!compare_completed) {
 +
 +              vert_target = mloop_target->v;
 +
 +              /* First advance i_loop_source, until it points to different vertex, after mapping applied */
 +              do {
 +                      i_loop_source++;
 +
 +                      if (i_loop_source == mpoly_source->totloop) {
 +                              /* End of loops for source, must match end of loop for target.  */
 +                              if (i_loop_target_offset == mpoly_target->totloop - 1) {
 +                                      compare_completed = true;
 +                                      same_loops = true;
 +                                      break;  /* Polys are identical */
 +                              }
 +                              else {
 +                                      compare_completed = true;
 +                                      same_loops = false;
 +                                      break;  /* Polys are different */
 +                              }
 +                      }
 +
 +                      mloop_source++;
 +                      vert_source = mloop_source->v;
 +
 +                      if (vtargetmap[vert_source] != -1) {
 +                              vert_source = vtargetmap[vert_source];
 +                      }
 +                      else {
 +                              /* All source loop vertices should be mapped */
 +                              BLI_assert(false);
 +                      }
 +
 +              } while (vert_source == vert_target);
 +
 +              if (compare_completed) {
 +                      break;
 +              }
 +
 +              /* Now advance i_loop_target as well */
 +              i_loop_target_offset++;
 +
 +              if (i_loop_target_offset == mpoly_target->totloop) {
 +                      /* End of loops for target only, that means no match */
 +                      /* except if all remaining source vertices are mapped to first target */
 +                      for (; i_loop_source < mpoly_source->totloop; i_loop_source++, mloop_source++) {
 +                              vert_source = vtargetmap[mloop_source->v];
 +                              if (vert_source != first_vert_source) {
 +                                      compare_completed = true;
 +                                      same_loops = false;
 +                                      break;
 +                              }
 +                      }
 +                      if (!compare_completed) {
 +                              same_loops = true;
 +                      }
 +                      break;
 +              }
 +
 +              /* Adjust i_loop_target for cycling around and for direct/reverse order defined by delta = +1 or -1 */
 +              i_loop_target_adjusted = (i_loop_target_start + direct_reverse * i_loop_target_offset) % mpoly_target->totloop;
 +              if (i_loop_target_adjusted < 0) {
 +                      i_loop_target_adjusted += mpoly_target->totloop;
 +              }
 +              mloop_target = mloop_array + mpoly_target->loopstart + i_loop_target_adjusted;
 +              vert_target = mloop_target->v;
 +
 +              if (vert_target != vert_source) {
 +                      same_loops = false;  /* Polys are different */
 +                      break;
 +              }
 +      }
 +      return same_loops;
 +}
 +
 +
 +/* Utility stuff for using GHash with polys, used by vertex merging. */
 +
 +typedef struct PolyKey {
 +      int poly_index;   /* index of the MPoly within the derived mesh */
 +      int totloops;     /* number of loops in the poly */
 +      unsigned int hash_sum;  /* Sum of all vertices indices */
 +      unsigned int hash_xor;  /* Xor of all vertices indices */
 +} PolyKey;
 +
 +
 +static unsigned int poly_gset_hash_fn(const void *key)
 +{
 +      const PolyKey *pk = key;
 +      return pk->hash_sum;
 +}
 +
 +static bool poly_gset_compare_fn(const void *k1, const void *k2)
 +{
 +      const PolyKey *pk1 = k1;
 +      const PolyKey *pk2 = k2;
 +      if ((pk1->hash_sum == pk2->hash_sum) &&
 +          (pk1->hash_xor == pk2->hash_xor) &&
 +          (pk1->totloops == pk2->totloops))
 +      {
 +              /* Equality - note that this does not mean equality of polys */
 +              return false;
 +      }
 +      else {
 +              return true;
 +      }
 +}
 +
 +/**
 + * Merge Verts
 + *
 + * This frees the given mesh and returns a new mesh.
 + *
-  * \param tot_vtargetmap  The number of non '-1' values in vtargetmap. (not the size)
++ * \param vtargetmap: The table that maps vertices to target vertices.  a value of -1
 + * indicates a vertex is a target, and is to be kept.
 + * This array is aligned with 'mesh->totvert'
 + * \warning \a vtargetmap must **not** contain any chained mapping (v1 -> v2 -> v3 etc.), this is not supported
 + * and will likely generate corrupted geometry.
 + *
-  * \param merge_mode enum with two modes.
++ * \param tot_vtargetmap: The number of non '-1' values in vtargetmap. (not the size)
 + *
++ * \param merge_mode: enum with two modes.
 + * - #MESH_MERGE_VERTS_DUMP_IF_MAPPED
 + * When called by the Mirror Modifier,
 + * In this mode it skips any faces that have all vertices merged (to avoid creating pairs
 + * of faces sharing the same set of vertices)
 + * - #MESH_MERGE_VERTS_DUMP_IF_EQUAL
 + * When called by the Array Modifier,
 + * In this mode, faces where all vertices are merged are double-checked,
 + * to see whether all target vertices actually make up a poly already.
 + * Indeed it could be that all of a poly's vertices are merged,
 + * but merged to vertices that do not make up a single poly,
 + * in which case the original poly should not be dumped.
 + * Actually this later behavior could apply to the Mirror Modifier as well, but the additional checks are
 + * costly and not necessary in the case of mirror, because each vertex is only merged to its own mirror.
 + *
 + * \note #BKE_mesh_recalc_tessellation has to run on the returned DM if you want to access tessfaces.
 + */
 +Mesh *BKE_mesh_merge_verts(Mesh *mesh, const int *vtargetmap, const int tot_vtargetmap, const int merge_mode)
 +{
 +      /* This was commented out back in 2013, see commit f45d8827bafe6b9eaf9de42f4054e9d84a21955d. */
 +// #define USE_LOOPS
 +
 +      Mesh *result = NULL;
 +
 +      const int totvert = mesh->totvert;
 +      const int totedge = mesh->totedge;
 +      const int totloop = mesh->totloop;
 +      const int totpoly = mesh->totpoly;
 +
 +      const int totvert_final = totvert - tot_vtargetmap;
 +
 +      MVert *mv, *mvert = MEM_malloc_arrayN(totvert_final, sizeof(*mvert), __func__);
 +      int *oldv         = MEM_malloc_arrayN(totvert_final, sizeof(*oldv), __func__);
 +      int *newv         = MEM_malloc_arrayN(totvert, sizeof(*newv), __func__);
 +      STACK_DECLARE(mvert);
 +      STACK_DECLARE(oldv);
 +
 +      /* Note: create (totedge + totloop) elements because partially invalid polys due to merge may require
 +       * generating new edges, and while in 99% cases we'll still end with less final edges than totedge,
 +       * cases can be forged that would end requiring more... */
 +      MEdge *med, *medge = MEM_malloc_arrayN((totedge + totloop), sizeof(*medge), __func__);
 +      int *olde          = MEM_malloc_arrayN((totedge + totloop), sizeof(*olde), __func__);
 +      int *newe          = MEM_malloc_arrayN((totedge + totloop), sizeof(*newe), __func__);
 +      STACK_DECLARE(medge);
 +      STACK_DECLARE(olde);
 +
 +      MLoop *ml, *mloop = MEM_malloc_arrayN(totloop, sizeof(*mloop), __func__);
 +      int *oldl         = MEM_malloc_arrayN(totloop, sizeof(*oldl), __func__);
 +#ifdef USE_LOOPS
 +      int *newl          = MEM_malloc_arrayN(totloop, sizeof(*newl), __func__);
 +#endif
 +      STACK_DECLARE(mloop);
 +      STACK_DECLARE(oldl);
 +
 +      MPoly *mp, *mpoly = MEM_malloc_arrayN(totpoly, sizeof(*medge), __func__);
 +      int *oldp         = MEM_malloc_arrayN(totpoly, sizeof(*oldp), __func__);
 +      STACK_DECLARE(mpoly);
 +      STACK_DECLARE(oldp);
 +
 +      EdgeHash *ehash = BLI_edgehash_new_ex(__func__, totedge);
 +
 +      int i, j, c;
 +
 +      PolyKey *poly_keys;
 +      GSet *poly_gset = NULL;
 +      MeshElemMap *poly_map = NULL;
 +      int *poly_map_mem = NULL;
 +
 +      STACK_INIT(oldv, totvert_final);
 +      STACK_INIT(olde, totedge);
 +      STACK_INIT(oldl, totloop);
 +      STACK_INIT(oldp, totpoly);
 +
 +      STACK_INIT(mvert, totvert_final);
 +      STACK_INIT(medge, totedge);
 +      STACK_INIT(mloop, totloop);
 +      STACK_INIT(mpoly, totpoly);
 +
 +      /* fill newv with destination vertex indices */
 +      mv = mesh->mvert;
 +      c = 0;
 +      for (i = 0; i < totvert; i++, mv++) {
 +              if (vtargetmap[i] == -1) {
 +                      STACK_PUSH(oldv, i);
 +                      STACK_PUSH(mvert, *mv);
 +                      newv[i] = c++;
 +              }
 +              else {
 +                      /* dummy value */
 +                      newv[i] = 0;
 +              }
 +      }
 +
 +      /* now link target vertices to destination indices */
 +      for (i = 0; i < totvert; i++) {
 +              if (vtargetmap[i] != -1) {
 +                      newv[i] = newv[vtargetmap[i]];
 +              }
 +      }
 +
 +      /* Don't remap vertices in cddm->mloop, because we need to know the original
 +       * indices in order to skip faces with all vertices merged.
 +       * The "update loop indices..." section further down remaps vertices in mloop.
 +       */
 +
 +      /* now go through and fix edges and faces */
 +      med = mesh->medge;
 +      c = 0;
 +      for (i = 0; i < totedge; i++, med++) {
 +              const unsigned int v1 = (vtargetmap[med->v1] != -1) ? vtargetmap[med->v1] : med->v1;
 +              const unsigned int v2 = (vtargetmap[med->v2] != -1) ? vtargetmap[med->v2] : med->v2;
 +              if (LIKELY(v1 != v2)) {
 +                      void **val_p;
 +
 +                      if (BLI_edgehash_ensure_p(ehash, v1, v2, &val_p)) {
 +                              newe[i] = POINTER_AS_INT(*val_p);
 +                      }
 +                      else {
 +                              STACK_PUSH(olde, i);
 +                              STACK_PUSH(medge, *med);
 +                              newe[i] = c;
 +                              *val_p = POINTER_FROM_INT(c);
 +                              c++;
 +                      }
 +              }
 +              else {
 +                      newe[i] = -1;
 +              }
 +      }
 +
 +      if (merge_mode == MESH_MERGE_VERTS_DUMP_IF_EQUAL) {
 +              /* In this mode, we need to determine,  whenever a poly' vertices are all mapped */
 +              /* if the targets already make up a poly, in which case the new poly is dropped */
 +              /* This poly equality check is rather complex.   We use a BLI_ghash to speed it up with a first level check */
 +              PolyKey *mpgh;
 +              poly_keys = MEM_malloc_arrayN(totpoly, sizeof(PolyKey), __func__);
 +              poly_gset = BLI_gset_new_ex(poly_gset_hash_fn, poly_gset_compare_fn, __func__, totpoly);
 +              /* Duplicates allowed because our compare function is not pure equality */
 +              BLI_gset_flag_set(poly_gset, GHASH_FLAG_ALLOW_DUPES);
 +
 +              mp = mesh->mpoly;
 +              mpgh = poly_keys;
 +              for (i = 0; i < totpoly; i++, mp++, mpgh++) {
 +                      mpgh->poly_index = i;
 +                      mpgh->totloops = mp->totloop;
 +                      ml = mesh->mloop + mp->loopstart;
 +                      mpgh->hash_sum = mpgh->hash_xor = 0;
 +                      for (j = 0; j < mp->totloop; j++, ml++) {
 +                              mpgh->hash_sum += ml->v;
 +                              mpgh->hash_xor ^= ml->v;
 +                      }
 +                      BLI_gset_insert(poly_gset, mpgh);
 +              }
 +
 +              /* Can we optimise by reusing an old pmap ?  How do we know an old pmap is stale ?  */
 +              /* When called by MOD_array.c, the cddm has just been created, so it has no valid pmap.   */
 +              BKE_mesh_vert_poly_map_create(
 +                      &poly_map, &poly_map_mem,
 +                      mesh->mpoly, mesh->mloop,
 +                      totvert, totpoly, totloop);
 +      }  /* done preparing for fast poly compare */
 +
 +
 +      mp = mesh->mpoly;
 +      mv = mesh->mvert;
 +      for (i = 0; i < totpoly; i++, mp++) {
 +              MPoly *mp_new;
 +
 +              ml = mesh->mloop + mp->loopstart;
 +
 +              /* check faces with all vertices merged */
 +              bool all_vertices_merged = true;
 +
 +              for (j = 0; j < mp->totloop; j++, ml++) {
 +                      if (vtargetmap[ml->v] == -1) {
 +                              all_vertices_merged = false;
 +                              /* This will be used to check for poly using several time the same vert. */
 +                              mv[ml->v].flag &= ~ME_VERT_TMP_TAG;
 +                      }
 +                      else {
 +                              /* This will be used to check for poly using several time the same vert. */
 +                              mv[vtargetmap[ml->v]].flag &= ~ME_VERT_TMP_TAG;
 +                      }
 +              }
 +
 +              if (UNLIKELY(all_vertices_merged)) {
 +                      if (merge_mode == MESH_MERGE_VERTS_DUMP_IF_MAPPED) {
 +                              /* In this mode, all vertices merged is enough to dump face */
 +                              continue;
 +                      }
 +                      else if (merge_mode == MESH_MERGE_VERTS_DUMP_IF_EQUAL) {
 +                              /* Additional condition for face dump:  target vertices must make up an identical face */
 +                              /* The test has 2 steps:  (1) first step is fast ghash lookup, but not failproof       */
 +                              /*                        (2) second step is thorough but more costly poly compare     */
 +                              int i_poly, v_target;
 +                              bool found = false;
 +                              PolyKey pkey;
 +
 +                              /* Use poly_gset for fast (although not 100% certain) identification of same poly */
 +                              /* First, make up a poly_summary structure */
 +                              ml = mesh->mloop + mp->loopstart;
 +                              pkey.hash_sum = pkey.hash_xor = 0;
 +                              pkey.totloops = 0;
 +                              for (j = 0; j < mp->totloop; j++, ml++) {
 +                                      v_target = vtargetmap[ml->v];   /* Cannot be -1, they are all mapped */
 +                                      pkey.hash_sum += v_target;
 +                                      pkey.hash_xor ^= v_target;
 +                                      pkey.totloops++;
 +                              }
 +                              if (BLI_gset_haskey(poly_gset, &pkey)) {
 +
 +                                      /* There might be a poly that matches this one.
 +                                       * We could just leave it there and say there is, and do a "continue".
 +                                       * ... but we are checking whether there is an exact poly match.
 +                                       * It's not so costly in terms of CPU since it's very rare, just a lot of complex code.
 +                                       */
 +
 +                                      /* Consider current loop again */
 +                                      ml = mesh->mloop + mp->loopstart;
 +                                      /* Consider the target of the loop's first vert */
 +                                      v_target = vtargetmap[ml->v];
 +                                      /* Now see if v_target belongs to a poly that shares all vertices with source poly,
 +                                       * in same order, or reverse order */
 +
 +                                      for (i_poly = 0; i_poly < poly_map[v_target].count; i_poly++) {
 +                                              MPoly *target_poly = mesh->mpoly + *(poly_map[v_target].indices + i_poly);
 +
 +                                              if (cddm_poly_compare(mesh->mloop, mp, target_poly, vtargetmap, +1) ||
 +                                                  cddm_poly_compare(mesh->mloop, mp, target_poly, vtargetmap, -1))
 +                                              {
 +                                                      found = true;
 +                                                      break;
 +                                              }
 +                                      }
 +                                      if (found) {
 +                                              /* Current poly's vertices are mapped to a poly that is strictly identical */
 +                                              /* Current poly is dumped */
 +                                              continue;
 +                                      }
 +                              }
 +                      }
 +              }
 +
 +
 +              /* Here either the poly's vertices were not all merged
 +               * or they were all merged, but targets do not make up an identical poly,
 +               * the poly is retained.
 +               */
 +              ml = mesh->mloop + mp->loopstart;
 +
 +              c = 0;
 +              MLoop *last_valid_ml = NULL;
 +              MLoop *first_valid_ml = NULL;
 +              bool need_edge_from_last_valid_ml = false;
 +              bool need_edge_to_first_valid_ml = false;
 +              int created_edges = 0;
 +              for (j = 0; j < mp->totloop; j++, ml++) {
 +                      const uint mlv = (vtargetmap[ml->v] != -1) ? vtargetmap[ml->v] : ml->v;
 +#ifndef NDEBUG
 +                      {
 +                              MLoop *next_ml = mesh->mloop + mp->loopstart + ((j + 1) % mp->totloop);
 +                              uint next_mlv = (vtargetmap[next_ml->v] != -1) ? vtargetmap[next_ml->v] : next_ml->v;
 +                              med = mesh->medge + ml->e;
 +                              uint v1 = (vtargetmap[med->v1] != -1) ? vtargetmap[med->v1] : med->v1;
 +                              uint v2 = (vtargetmap[med->v2] != -1) ? vtargetmap[med->v2] : med->v2;
 +                              BLI_assert((mlv == v1 && next_mlv == v2) || (mlv == v2 && next_mlv == v1));
 +                      }
 +#endif
 +                      /* A loop is only valid if its matching edge is, and it's not reusing a vertex already used by this poly. */
 +                      if (LIKELY((newe[ml->e] != -1) && ((mv[mlv].flag & ME_VERT_TMP_TAG) == 0))) {
 +                              mv[mlv].flag |= ME_VERT_TMP_TAG;
 +
 +                              if (UNLIKELY(last_valid_ml != NULL && need_edge_from_last_valid_ml)) {
 +                                      /* We need to create a new edge between last valid loop and this one! */
 +                                      void **val_p;
 +
 +                                      uint v1 = (vtargetmap[last_valid_ml->v] != -1) ? vtargetmap[last_valid_ml->v] : last_valid_ml->v;
 +                                      uint v2 = mlv;
 +                                      BLI_assert(v1 != v2);
 +                                      if (BLI_edgehash_ensure_p(ehash, v1, v2, &val_p)) {
 +                                              last_valid_ml->e = POINTER_AS_INT(*val_p);
 +                                      }
 +                                      else {
 +                                              const int new_eidx = STACK_SIZE(medge);
 +                                              STACK_PUSH(olde, olde[last_valid_ml->e]);
 +                                              STACK_PUSH(medge, mesh->medge[last_valid_ml->e]);
 +                                              medge[new_eidx].v1 = last_valid_ml->v;
 +                                              medge[new_eidx].v2 = ml->v;
 +                                              /* DO NOT change newe mapping, could break actual values due to some deleted original edges. */
 +                                              *val_p = POINTER_FROM_INT(new_eidx);
 +                                              created_edges++;
 +
 +                                              last_valid_ml->e = new_eidx;
 +                                      }
 +                                      need_edge_from_last_valid_ml = false;
 +                              }
 +
 +#ifdef USE_LOOPS
 +                              newl[j + mp->loopstart] = STACK_SIZE(mloop);
 +#endif
 +                              STACK_PUSH(oldl, j + mp->loopstart);
 +                              last_valid_ml = STACK_PUSH_RET_PTR(mloop);
 +                              *last_valid_ml = *ml;
 +                              if (first_valid_ml == NULL) {
 +                                      first_valid_ml = last_valid_ml;
 +                              }
 +                              c++;
 +
 +                              /* We absolutely HAVE to handle edge index remapping here, otherwise potential newly created edges
 +                               * in that part of code make remapping later totally unreliable. */
 +                              BLI_assert(newe[ml->e] != -1);
 +                              last_valid_ml->e = newe[ml->e];
 +                      }
 +                      else {
 +                              if (last_valid_ml != NULL) {
 +                                      need_edge_from_last_valid_ml = true;
 +                              }
 +                              else {
 +                                      need_edge_to_first_valid_ml = true;
 +                              }
 +                      }
 +              }
 +              if (UNLIKELY(last_valid_ml != NULL && !ELEM(first_valid_ml, NULL, last_valid_ml) &&
 +                           (need_edge_to_first_valid_ml || need_edge_from_last_valid_ml)))
 +              {
 +                      /* We need to create a new edge between last valid loop and first valid one! */
 +                      void **val_p;
 +
 +                      uint v1 = (vtargetmap[last_valid_ml->v] != -1) ? vtargetmap[last_valid_ml->v] : last_valid_ml->v;
 +                      uint v2 = (vtargetmap[first_valid_ml->v] != -1) ? vtargetmap[first_valid_ml->v] : first_valid_ml->v;
 +                      BLI_assert(v1 != v2);
 +                      if (BLI_edgehash_ensure_p(ehash, v1, v2, &val_p)) {
 +                              last_valid_ml->e = POINTER_AS_INT(*val_p);
 +                      }
 +                      else {
 +                              const int new_eidx = STACK_SIZE(medge);
 +                              STACK_PUSH(olde, olde[last_valid_ml->e]);
 +                              STACK_PUSH(medge, mesh->medge[last_valid_ml->e]);
 +                              medge[new_eidx].v1 = last_valid_ml->v;
 +                              medge[new_eidx].v2 = first_valid_ml->v;
 +                              /* DO NOT change newe mapping, could break actual values due to some deleted original edges. */
 +                              *val_p = POINTER_FROM_INT(new_eidx);
 +                              created_edges++;
 +
 +                              last_valid_ml->e = new_eidx;
 +                      }
 +                      need_edge_to_first_valid_ml = need_edge_from_last_valid_ml = false;
 +              }
 +
 +              if (UNLIKELY(c == 0)) {
 +                      BLI_assert(created_edges == 0);
 +                      continue;
 +              }
 +              else if (UNLIKELY(c < 3)) {
 +                      STACK_DISCARD(oldl, c);
 +                      STACK_DISCARD(mloop, c);
 +                      if (created_edges > 0) {
 +                              for (j = STACK_SIZE(medge) - created_edges; j < STACK_SIZE(medge); j++) {
 +                                      BLI_edgehash_remove(ehash, medge[j].v1, medge[j].v2, NULL);
 +                              }
 +                              STACK_DISCARD(olde, created_edges);
 +                              STACK_DISCARD(medge, created_edges);
 +                      }
 +                      continue;
 +              }
 +
 +              mp_new = STACK_PUSH_RET_PTR(mpoly);
 +              *mp_new = *mp;
 +              mp_new->totloop = c;
 +              BLI_assert(mp_new->totloop >= 3);
 +              mp_new->loopstart = STACK_SIZE(mloop) - c;
 +
 +              STACK_PUSH(oldp, i);
 +      }  /* end of the loop that tests polys   */
 +
 +
 +      if (poly_gset) {
 +              // printf("hash quality %.6f\n", BLI_gset_calc_quality(poly_gset));
 +
 +              BLI_gset_free(poly_gset, NULL);
 +              MEM_freeN(poly_keys);
 +      }
 +
 +      /*create new cddm*/
 +      result = BKE_mesh_new_nomain_from_template(
 +              mesh, STACK_SIZE(mvert), STACK_SIZE(medge), 0, STACK_SIZE(mloop), STACK_SIZE(mpoly));
 +
 +      /*update edge indices and copy customdata*/
 +      med = medge;
 +      for (i = 0; i < result->totedge; i++, med++) {
 +              BLI_assert(newv[med->v1] != -1);
 +              med->v1 = newv[med->v1];
 +              BLI_assert(newv[med->v2] != -1);
 +              med->v2 = newv[med->v2];
 +
 +              /* Can happen in case vtargetmap contains some double chains, we do not support that. */
 +              BLI_assert(med->v1 != med->v2);
 +
 +              CustomData_copy_data(&mesh->edata, &result->edata, olde[i], i, 1);
 +      }
 +
 +      /*update loop indices and copy customdata*/
 +      ml = mloop;
 +      for (i = 0; i < result->totloop; i++, ml++) {
 +              /* Edge remapping has already be done in main loop handling part above. */
 +              BLI_assert(newv[ml->v] != -1);
 +              ml->v = newv[ml->v];
 +
 +              CustomData_copy_data(&mesh->ldata, &result->ldata, oldl[i], i, 1);
 +      }
 +
 +      /*copy vertex customdata*/
 +      mv = mvert;
 +      for (i = 0; i < result->totvert; i++, mv++) {
 +              CustomData_copy_data(&mesh->vdata, &result->vdata, oldv[i], i, 1);
 +      }
 +
 +      /*copy poly customdata*/
 +      mp = mpoly;
 +      for (i = 0; i < result->totpoly; i++, mp++) {
 +              CustomData_copy_data(&mesh->pdata, &result->pdata, oldp[i], i, 1);
 +      }
 +
 +      /*copy over data.  CustomData_add_layer can do this, need to look it up.*/
 +      memcpy(result->mvert, mvert, sizeof(MVert) * STACK_SIZE(mvert));
 +      memcpy(result->medge, medge, sizeof(MEdge) * STACK_SIZE(medge));
 +      memcpy(result->mloop, mloop, sizeof(MLoop) * STACK_SIZE(mloop));
 +      memcpy(result->mpoly, mpoly, sizeof(MPoly) * STACK_SIZE(mpoly));
 +
 +      MEM_freeN(mvert);
 +      MEM_freeN(medge);
 +      MEM_freeN(mloop);
 +      MEM_freeN(mpoly);
 +
 +      MEM_freeN(newv);
 +      MEM_freeN(newe);
 +#ifdef USE_LOOPS
 +      MEM_freeN(newl);
 +#endif
 +
 +      MEM_freeN(oldv);
 +      MEM_freeN(olde);
 +      MEM_freeN(oldl);
 +      MEM_freeN(oldp);
 +
 +      BLI_edgehash_free(ehash, NULL);
 +
 +      if (poly_map != NULL)
 +              MEM_freeN(poly_map);
 +      if (poly_map_mem != NULL)
 +              MEM_freeN(poly_map_mem);
 +
 +      BKE_id_free(NULL, mesh);
 +
 +      return result;
 +}
@@@ -1457,78 -1540,4 +1457,78 @@@ void BKE_mesh_calc_edges(Mesh *mesh, bo
  
        BLI_edgehash_free(eh, NULL);
  }
-  * \param mesh  The mesh to add edges into
 +
 +void BKE_mesh_calc_edges_loose(Mesh *mesh)
 +{
 +      MEdge *med = mesh->medge;
 +      for (int i = 0; i < mesh->totedge; i++, med++) {
 +              med->flag |= ME_LOOSEEDGE;
 +      }
 +      MLoop *ml = mesh->mloop;
 +      for (int i = 0; i < mesh->totloop; i++, ml++) {
 +              mesh->medge[ml->e].flag &= ~ME_LOOSEEDGE;
 +      }
 +}
 +
 +/**
 + * Calculate/create edges from tessface data
 + *
++ * \param mesh: The mesh to add edges into
 + */
 +
 +void BKE_mesh_calc_edges_tessface(Mesh *mesh)
 +{
 +      CustomData edgeData;
 +      EdgeSetIterator *ehi;
 +      MFace *mf = mesh->mface;
 +      MEdge *med;
 +      EdgeSet *eh;
 +      int i, *index, numEdges, numFaces = mesh->totface;
 +
 +      eh = BLI_edgeset_new_ex(__func__, BLI_EDGEHASH_SIZE_GUESS_FROM_POLYS(numFaces));
 +
 +      for (i = 0; i < numFaces; i++, mf++) {
 +              BLI_edgeset_add(eh, mf->v1, mf->v2);
 +              BLI_edgeset_add(eh, mf->v2, mf->v3);
 +
 +              if (mf->v4) {
 +                      BLI_edgeset_add(eh, mf->v3, mf->v4);
 +                      BLI_edgeset_add(eh, mf->v4, mf->v1);
 +              }
 +              else {
 +                      BLI_edgeset_add(eh, mf->v3, mf->v1);
 +              }
 +      }
 +
 +      numEdges = BLI_edgeset_len(eh);
 +
 +      /* write new edges into a temporary CustomData */
 +      CustomData_reset(&edgeData);
 +      CustomData_add_layer(&edgeData, CD_MEDGE, CD_CALLOC, NULL, numEdges);
 +      CustomData_add_layer(&edgeData, CD_ORIGINDEX, CD_CALLOC, NULL, numEdges);
 +
 +      med = CustomData_get_layer(&edgeData, CD_MEDGE);
 +      index = CustomData_get_layer(&edgeData, CD_ORIGINDEX);
 +
 +      for (ehi = BLI_edgesetIterator_new(eh), i = 0;
 +           BLI_edgesetIterator_isDone(ehi) == false;
 +           BLI_edgesetIterator_step(ehi), i++, med++, index++)
 +      {
 +              BLI_edgesetIterator_getKey(ehi, &med->v1, &med->v2);
 +
 +              med->flag = ME_EDGEDRAW | ME_EDGERENDER;
 +              *index = ORIGINDEX_NONE;
 +      }
 +      BLI_edgesetIterator_free(ehi);
 +
 +      /* free old CustomData and assign new one */
 +      CustomData_free(&mesh->edata, mesh->totedge);
 +      mesh->edata = edgeData;
 +      mesh->totedge = numEdges;
 +
 +      mesh->medge = CustomData_get_layer(&mesh->edata, CD_MEDGE);
 +
 +      BLI_edgeset_free(eh);
 +}
 +
  /** \} */
@@@ -471,9 -466,9 +471,9 @@@ bool modifiers_isParticleEnabled(Objec
  /**
   * Check whether is enabled.
   *
-  * \param scene Current scene, may be NULL, in which case isDisabled callback of the modifier is never called.
+  * \param scene: Current scene, may be NULL, in which case isDisabled callback of the modifier is never called.
   */
 -bool modifier_isEnabled(struct Scene *scene, ModifierData *md, int required_mode)
 +bool modifier_isEnabled(const struct Scene *scene, ModifierData *md, int required_mode)
  {
        const ModifierTypeInfo *mti = modifierType_getInfo(md->type);
  
@@@ -162,10 -161,9 +162,10 @@@ void BKE_nla_tracks_free(ListBase *trac
  /**
   * Copy NLA strip
   *
-  * \param use_same_action When true, the existing action is used (instead of being duplicated)
-  * \param flag Control ID pointers management, see LIB_ID_CREATE_.../LIB_ID_COPY_... flags in BKE_library.h
+  * \param use_same_action: When true, the existing action is used (instead of being duplicated)
++ * \param flag: Control ID pointers management, see LIB_ID_CREATE_.../LIB_ID_COPY_... flags in BKE_library.h
   */
 -NlaStrip *BKE_nlastrip_copy(Main *bmain, NlaStrip *strip, const bool use_same_action)
 +NlaStrip *BKE_nlastrip_copy(Main *bmain, NlaStrip *strip, const bool use_same_action, const int flag)
  {
        NlaStrip *strip_d;
        NlaStrip *cs, *cs_d;
        return strip_d;
  }
  
 -/* Copy NLA Track */
 -NlaTrack *BKE_nlatrack_copy(Main *bmain, NlaTrack *nlt, const bool use_same_actions)
 +/**
 + * Copy a single NLA Track.
-  * \param flag Control ID pointers management, see LIB_ID_CREATE_.../LIB_ID_COPY_... flags in BKE_library.h
++ * \param flag: Control ID pointers management, see LIB_ID_CREATE_.../LIB_ID_COPY_... flags in BKE_library.h
 + */
 +NlaTrack *BKE_nlatrack_copy(Main *bmain, NlaTrack *nlt, const bool use_same_actions, const int flag)
  {
        NlaStrip *strip, *strip_d;
        NlaTrack *nlt_d;
        return nlt_d;
  }
  
 -/* Copy all NLA data */
 -void BKE_nla_tracks_copy(Main *bmain, ListBase *dst, ListBase *src)
 +/**
 + * Copy all NLA data.
-  * \param flag Control ID pointers management, see LIB_ID_CREATE_.../LIB_ID_COPY_... flags in BKE_library.h
++ * \param flag: Control ID pointers management, see LIB_ID_CREATE_.../LIB_ID_COPY_... flags in BKE_library.h
 + */
 +void BKE_nla_tracks_copy(Main *bmain, ListBase *dst, ListBase *src, const int flag)
  {
        NlaTrack *nlt, *nlt_d;
  
@@@ -908,99 -707,145 +908,99 @@@ static Object *object_add_common(Main *
        return ob;
  }
  
 -
 -#ifdef WITH_GAMEENGINE
 -
 -void BKE_object_lod_add(Object *ob)
 -{
 -      LodLevel *lod = MEM_callocN(sizeof(LodLevel), "LoD Level");
 -      LodLevel *last = ob->lodlevels.last;
 -
 -      /* If the lod list is empty, initialize it with the base lod level */
 -      if (!last) {
 -              LodLevel *base = MEM_callocN(sizeof(LodLevel), "Base LoD Level");
 -              BLI_addtail(&ob->lodlevels, base);
 -              base->flags = OB_LOD_USE_MESH | OB_LOD_USE_MAT;
 -              base->source = ob;
 -              base->obhysteresis = 10;
 -              last = ob->currentlod = base;
 -      }
 -
 -      lod->distance = last->distance + 25.0f;
 -      lod->obhysteresis = 10;
 -      lod->flags = OB_LOD_USE_MESH | OB_LOD_USE_MAT;
 -
 -      BLI_addtail(&ob->lodlevels, lod);
 -}
 -
 -static int lod_cmp(const void *a, const void *b)
 -{
 -      const LodLevel *loda = a;
 -      const LodLevel *lodb = b;
 -
 -      if (loda->distance < lodb->distance) return -1;
 -      return loda->distance > lodb->distance;
 -}
 -
 -void BKE_object_lod_sort(Object *ob)
 -{
 -      BLI_listbase_sort(&ob->lodlevels, lod_cmp);
 -}
 -
 -bool BKE_object_lod_remove(Object *ob, int level)
 +/**
 + * General add: to scene, with layer from area and default name
 + *
 + * Object is added to the active Collection.
 + * If there is no linked collection to the active ViewLayer we create a new one.
 + */
 +/* creates minimum required data, but without vertices etc. */
 +Object *BKE_object_add(
 +        Main *bmain, Scene *UNUSED(scene), ViewLayer *view_layer,
 +        int type, const char *name)
  {
 -      LodLevel *rem;
 -
 -      if (level < 1 || level > BLI_listbase_count(&ob->lodlevels) - 1)
 -              return false;
 +      Object *ob;
 +      Base *base;
 +      LayerCollection *layer_collection;
  
 -      rem = BLI_findlink(&ob->lodlevels, level);
 +      ob = object_add_common(bmain, view_layer, type, name);
  
 -      if (rem == ob->currentlod) {
 -              ob->currentlod = rem->prev;
 -      }
 +      layer_collection = BKE_layer_collection_get_active(view_layer);
 +      BKE_collection_object_add(bmain, layer_collection->collection, ob);
  
 -      BLI_remlink(&ob->lodlevels, rem);
 -      MEM_freeN(rem);
 +      base = BKE_view_layer_base_find(view_layer, ob);
 +      BKE_view_layer_base_select_and_set_active(view_layer, base);
  
 -      /* If there are no user defined lods, remove the base lod as well */
 -      if (BLI_listbase_is_single(&ob->lodlevels)) {
 -              LodLevel *base = ob->lodlevels.first;
 -              BLI_remlink(&ob->lodlevels, base);
 -              MEM_freeN(base);
 -              ob->currentlod = NULL;
 -      }
 -
 -      return true;
 +      return ob;
  }
  
 -static LodLevel *lod_level_select(Object *ob, const float camera_position[3])
 +/**
 + * Add a new object, using another one as a reference
 + *
-  * \param ob_src object to use to determine the collections of the new object.
++ * \param ob_src: object to use to determine the collections of the new object.
 + */
 +Object *BKE_object_add_from(
 +        Main *bmain, Scene *scene, ViewLayer *view_layer,
 +        int type, const char *name, Object *ob_src)
  {
 -      LodLevel *current = ob->currentlod;
 -      float dist_sq;
 -
 -      if (!current) return NULL;
 -
 -      dist_sq = len_squared_v3v3(ob->obmat[3], camera_position);
 -
 -      if (dist_sq < SQUARE(current->distance)) {
 -              /* check for higher LoD */
 -              while (current->prev && dist_sq < SQUARE(current->distance)) {
 -                      current = current->prev;
 -              }
 -      }
 -      else {
 -              /* check for lower LoD */
 -              while (current->next && dist_sq > SQUARE(current->next->distance)) {
 -                      current = current->next;
 -              }
 -      }
 +      Object *ob;
 +      Base *base;
  
 -      return current;
 -}
 +      ob = object_add_common(bmain, view_layer, type, name);
 +      BKE_collection_object_add_from(bmain, scene, ob_src, ob);
  
 -bool BKE_object_lod_is_usable(Object *ob, Scene *scene)
 -{
 -      bool active = (scene) ? ob == OBACT : false;
 -      return (ob->mode == OB_MODE_OBJECT || !active);
 -}
 -
 -void BKE_object_lod_update(Object *ob, const float camera_position[3])
 -{
 -      LodLevel *cur_level = ob->currentlod;
 -      LodLevel *new_level = lod_level_select(ob, camera_position);
 +      base = BKE_view_layer_base_find(view_layer, ob);
 +      BKE_view_layer_base_select_and_set_active(view_layer, base);
  
 -      if (new_level != cur_level) {
 -              ob->currentlod = new_level;
 -      }
 +      return ob;
  }
  
 -static Object *lod_ob_get(Object *ob, Scene *scene, int flag)
 +/**
 + * Add a new object, but assign the given datablock as the ob->data
 + * for the newly created object.
 + *
-  * \param data The datablock to assign as ob->data for the new object.
++ * \param data: The datablock to assign as ob->data for the new object.
 + *             This is assumed to be of the correct type.
-  * \param do_id_user If true, id_us_plus() will be called on data when
++ * \param do_id_user: If true, id_us_plus() will be called on data when
 + *                 assigning it to the object.
 + */
 +Object *BKE_object_add_for_data(
 +        Main *bmain, ViewLayer *view_layer,
 +        int type, const char *name, ID *data, bool do_id_user)
  {
 -      LodLevel *current = ob->currentlod;
 +      Object *ob;
 +      Base *base;
 +      LayerCollection *layer_collection;
  
 -      if (!current || !BKE_object_lod_is_usable(ob, scene))
 -              return ob;
 +      /* same as object_add_common, except we don't create new ob->data */
 +      ob = BKE_object_add_only_object(bmain, type, name);
 +      ob->data = data;
 +      if (do_id_user) id_us_plus(data);
  
 -      while (current->prev && (!(current->flags & flag) || !current->source || current->source->type != OB_MESH)) {
 -              current = current->prev;
 -      }
 +      BKE_view_layer_base_deselect_all(view_layer);
 +      DEG_id_tag_update_ex(bmain, &ob->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY | ID_RECALC_ANIMATION);
  
 -      return current->source;
 -}
 +      layer_collection = BKE_layer_collection_get_active(view_layer);
 +      BKE_collection_object_add(bmain, layer_collection->collection, ob);
  
 -struct Object *BKE_object_lod_meshob_get(Object *ob, Scene *scene)
 -{
 -      return lod_ob_get(ob, scene, OB_LOD_USE_MESH);
 -}
 +      base = BKE_view_layer_base_find(view_layer, ob);
 +      BKE_view_layer_base_select_and_set_active(view_layer, base);
  
 -struct Object *BKE_object_lod_matob_get(Object *ob, Scene *scene)
 -{
 -      return lod_ob_get(ob, scene, OB_LOD_USE_MAT);
 +      return ob;
  }
  
 -#endif  /* WITH_GAMEENGINE */
  
 -
 -SoftBody *copy_softbody(const SoftBody *sb, const int flag)
 +void BKE_object_copy_softbody(struct Object *ob_dst, const struct Object *ob_src, const int flag)
  {
 +      SoftBody *sb = ob_src->soft;
        SoftBody *sbn;
 +      bool tagged_no_main = ob_dst->id.tag & LIB_TAG_NO_MAIN;
  
 -      if (sb == NULL) return(NULL);
 +      ob_dst->softflag = ob_src->softflag;
 +      if (sb == NULL) {
 +              ob_dst->soft = NULL;
 +              return;
 +      }
  
        sbn = MEM_dupallocN(sb);
  
@@@ -1317,16 -1110,11 +1317,16 @@@ void BKE_object_transform_copy(Object *
   *
   * WARNING! This function will not handle ID user count!
   *
-  * \param flag  Copying options (see BKE_library.h's LIB_ID_COPY_... flags for more).
+  * \param flag: Copying options (see BKE_library.h's LIB_ID_COPY_... flags for more).
   */
 -void BKE_object_copy_data(Main *UNUSED(bmain), Object *ob_dst, const Object *ob_src, const int flag)
 +void BKE_object_copy_data(Main *bmain, Object *ob_dst, const Object *ob_src, const int flag)
  {
        ModifierData *md;
 +      GpencilModifierData *gmd;
 +      ShaderFxData *fx;
 +
 +      /* Do not copy runtime data. */
 +      BKE_object_runtime_reset(ob_dst);
  
        /* We never handle usercount here for own data. */
        const int flag_subdata = flag | LIB_ID_CREATE_NO_USER_REFCOUNT;
@@@ -2202,10 -2014,9 +2202,10 @@@ void BKE_object_get_parent_matrix(Depsg
  }
  
  /**
-  * \param r_originmat  Optional matrix that stores the space the object is in (without its own matrix applied)
+  * \param r_originmat: Optional matrix that stores the space the object is in (without its own matrix applied)
   */
 -static void solve_parenting(Scene *scene, Object *ob, Object *par, float obmat[4][4], float slowmat[4][4],
 +static void solve_parenting(Depsgraph *depsgraph,
 +                            Scene *scene, Object *ob, Object *par, float obmat[4][4], float slowmat[4][4],
                              float r_originmat[3][3], const bool set_origin)
  {
        float totmat[4][4];
@@@ -2357,44 -2159,35 +2357,44 @@@ void BKE_object_workob_calc_parent(Deps
        unit_m4(workob->obmat);
        unit_m4(workob->parentinv);
        unit_m4(workob->constinv);
 -      workob->parent = ob->parent;
  
 -      workob->trackflag = ob->trackflag;
 -      workob->upflag = ob->upflag;
 +      /* Since this is used while calculating parenting, at this moment ob_eval->parent is still NULL. */
 +      workob->parent = DEG_get_evaluated_object(depsgraph, ob->parent);
  
 -      workob->partype = ob->partype;
 -      workob->par1 = ob->par1;
 -      workob->par2 = ob->par2;
 -      workob->par3 = ob->par3;
 +      workob->trackflag = ob_eval->trackflag;
 +      workob->upflag = ob_eval->upflag;
  
 -      workob->constraints.first = ob->constraints.first;
 -      workob->constraints.last = ob->constraints.last;
 +      workob->partype = ob_eval->partype;
 +      workob->par1 = ob_eval->par1;
 +      workob->par2 = ob_eval->par2;
 +      workob->par3 = ob_eval->par3;
  
 -      BLI_strncpy(workob->parsubstr, ob->parsubstr, sizeof(workob->parsubstr));
 +      workob->constraints = ob_eval->constraints;
  
 -      BKE_object_where_is_calc(scene, workob);
 +      BLI_strncpy(workob->parsubstr, ob_eval->parsubstr, sizeof(workob->parsubstr));
 +
 +      BKE_object_where_is_calc(depsgraph, scene, workob);
  }
  
 -/* see BKE_pchan_apply_mat4() for the equivalent 'pchan' function */
 -void BKE_object_apply_mat4(Object *ob, float mat[4][4], const bool use_compat, const bool use_parent)
 +/**
 + * Applies the global transformation \a mat to the \a ob using a relative parent space if supplied.
 + *
-  * \param mat the global transformation mat that the object should be set object to.
-  * \param parent the parent space in which this object will be set relative to (should probably always be parent_eval).
-  * \param use_compat true to ensure that rotations are set using the min difference between the old and new orientation.
++ * \param mat: the global transformation mat that the object should be set object to.
++ * \param parent: the parent space in which this object will be set relative to (should probably always be parent_eval).
++ * \param use_compat: true to ensure that rotations are set using the min difference between the old and new orientation.
 + */
 +void BKE_object_apply_mat4_ex(Object *ob, float mat[4][4], Object *parent, float parentinv[4][4], const bool use_compat)
  {
 +      /* see BKE_pchan_apply_mat4() for the equivalent 'pchan' function */
 +
        float rot[3][3];
  
 -      if (use_parent && ob->parent) {
 +      if (parent != NULL) {
                float rmat[4][4], diff_mat[4][4], imat[4][4], parent_mat[4][4];
  
 -              BKE_object_get_parent_matrix(NULL, ob, ob->parent, parent_mat);
 +              BKE_object_get_parent_matrix(NULL, NULL, ob, parent, parent_mat);
  
 -              mul_m4_m4m4(diff_mat, parent_mat, ob->parentinv);
 +              mul_m4_m4m4(diff_mat, parent_mat, parentinv);
                invert_m4_m4(imat, diff_mat);
                mul_m4_m4m4(rmat, imat, mat); /* get the parent relative matrix */
  
@@@ -1058,25 -853,14 +1058,25 @@@ static bool sculpt_modifiers_active(Sce
  }
  
  /**
-  * \param need_mask So that the evaluated mesh that is returned has mask data.
 - * \param need_mask: So the DerivedMesh thats returned has mask data
++ * \param need_mask: So that the evaluated mesh that is returned has mask data.
   */
 -void BKE_sculpt_update_mesh_elements(Scene *scene, Sculpt *sd, Object *ob,
 -                                     bool need_pmap, bool need_mask)
 +void BKE_sculpt_update_mesh_elements(
 +        Depsgraph *depsgraph, Scene *scene, Sculpt *sd, Object *ob,
 +        bool need_pmap, bool need_mask)
  {
 -      DerivedMesh *dm;
 +      /* TODO(sergey): Make sure ob points to an original object. This is what it
 +       * is supposed to be pointing to. The issue is, currently draw code takes
 +       * care of PBVH creation, even though this is something up to dependency
 +       * graph.
 +       * Probably, we need to being back logic which was checking for sculpt mode
 +       * and (re)create PBVH if needed in that case, similar to how DerivedMesh
 +       * was handling this.
 +       */
 +      ob = DEG_get_original_object(ob);
 +      Object *ob_eval = DEG_get_evaluated_object(depsgraph, ob);
 +
        SculptSession *ss = ob->sculpt;
 -      Mesh *me = ob->data;
 +      Mesh *me = BKE_object_get_original_mesh(ob);
        MultiresModifierData *mmd = BKE_sculpt_multires_active(scene, ob);
  
        ss->modifiers_active = sculpt_modifiers_active(scene, sd, ob);
@@@ -1389,10 -1338,12 +1389,10 @@@ static void rigidbody_update_sim_ob(Dep
  /**
   * Updates and validates world, bodies and shapes.
   *
-  * \param rebuild Rebuild entire simulation
+  * \param rebuild: Rebuild entire simulation
   */
 -static void rigidbody_update_simulation(Scene *scene, RigidBodyWorld *rbw, bool rebuild)
 +static void rigidbody_update_simulation(Depsgraph *depsgraph, Scene *scene, RigidBodyWorld *rbw, bool rebuild)
  {
 -      GroupObject *go;
 -
        /* update world */
        if (rebuild)
                BKE_rigidbody_validate_sim_world(scene, rbw, true);
@@@ -34,18 -34,6 +34,18 @@@ void BLI_system_backtrace(FILE *fp)
  /* Get CPU brand, result is to be MEM_freeN()-ed. */
  char *BLI_cpu_brand_string(void);
  
-  * @param buffer Character buffer to write the hostname into.
-  * @param bufsize Size of the character buffer, including trailing '\0'.
 +/**
 + * Obtain the hostname from the system.
 + *
 + * This simply determines the host's name, and doesn't do any DNS lookup of any
 + * IP address of the machine. As such, it's only usable for identification
 + * purposes, and not for reachability over a network.
 + *
++ * \param buffer: Character buffer to write the hostname into.
++ * \param bufsize: Size of the character buffer, including trailing '\0'.
 + */
 +void BLI_hostname_get(char *buffer, size_t bufsize);
 +
  /* getpid */
  #ifdef WIN32
  #  define BLI_SYSTEM_PID_H <process.h>
Simple merge
@@@ -179,97 -179,3 +179,97 @@@ bool BLI_tridiagonal_solve_cyclic(cons
  
        return success;
  }
-  * \param func_delta Callback computing the value of f(x).
-  * \param func_jacobian Callback computing the Jacobian matrix of the function at x.
-  * \param func_correction Callback for forcing the search into an arbitrary custom domain. May be NULL.
-  * \param userdata Data for the callbacks.
-  * \param epsilon Desired precision.
-  * \param max_iterations Limit on the iterations.
-  * \param trace Enables logging to console.
-  * \param x_init Initial solution vector.
-  * \param result Final result.
 +
 +/**
 + * \brief Solve a generic f(x) = 0 equation using Newton's method.
 + *
++ * \param func_delta: Callback computing the value of f(x).
++ * \param func_jacobian: Callback computing the Jacobian matrix of the function at x.
++ * \param func_correction: Callback for forcing the search into an arbitrary custom domain. May be NULL.
++ * \param userdata: Data for the callbacks.
++ * \param epsilon: Desired precision.
++ * \param max_iterations: Limit on the iterations.
++ * \param trace: Enables logging to console.
++ * \param x_init: Initial solution vector.
++ * \param result: Final result.
 + * \return true if success
 + */
 +bool BLI_newton3d_solve(
 +        Newton3D_DeltaFunc func_delta, Newton3D_JacobianFunc func_jacobian, Newton3D_CorrectionFunc func_correction, void *userdata,
 +        float epsilon, int max_iterations, bool trace, const float x_init[3], float result[3])
 +{
 +      float fdelta[3], fdeltav, next_fdeltav;
 +      float jacobian[3][3], step[3], x[3], x_next[3];
 +
 +      epsilon *= epsilon;
 +
 +      copy_v3_v3(x, x_init);
 +
 +      func_delta(userdata, x, fdelta);
 +      fdeltav = len_squared_v3(fdelta);
 +
 +      if (trace) {
 +              printf("START (%g, %g, %g) %g\n", x[0], x[1], x[2], fdeltav);
 +      }
 +
 +      for (int i = 0; i < max_iterations && fdeltav > epsilon; i++) {
 +              /* Newton's method step. */
 +              func_jacobian(userdata, x, jacobian);
 +
 +              if (!invert_m3(jacobian)) {
 +                      return false;
 +              }
 +
 +              mul_v3_m3v3(step, jacobian, fdelta);
 +              sub_v3_v3v3(x_next, x, step);
 +
 +              /* Custom out-of-bounds value correction. */
 +              if (func_correction) {
 +                      if (trace) {
 +                              printf("%3d * (%g, %g, %g)\n", i, x_next[0], x_next[1], x_next[2]);
 +                      }
 +
 +                      if (!func_correction(userdata, x, step, x_next)) {
 +                              return false;
 +                      }
 +              }
 +
 +              func_delta(userdata, x_next, fdelta);
 +              next_fdeltav = len_squared_v3(fdelta);
 +
 +              if (trace) {
 +                      printf("%3d ? (%g, %g, %g) %g\n", i, x_next[0], x_next[1], x_next[2], next_fdeltav);
 +              }
 +
 +              /* Line search correction. */
 +              while (next_fdeltav > fdeltav) {
 +                      float g0 = sqrtf(fdeltav), g1 = sqrtf(next_fdeltav);
 +                      float g01 = -g0 / len_v3(step);
 +                      float det = 2.0f * (g1 - g0 - g01);
 +                      float l = (det == 0.0f) ? 0.1f : -g01 / det;
 +                      CLAMP_MIN(l, 0.1f);
 +
 +                      mul_v3_fl(step, l);
 +                      sub_v3_v3v3(x_next, x, step);
 +
 +                      func_delta(userdata, x_next, fdelta);
 +                      next_fdeltav = len_squared_v3(fdelta);
 +
 +                      if (trace) {
 +                              printf("%3d . (%g, %g, %g) %g\n", i, x_next[0], x_next[1], x_next[2], next_fdeltav);
 +                      }
 +              }
 +
 +              copy_v3_v3(x, x_next);
 +              fdeltav = next_fdeltav;
 +      }
 +
 +      bool success = (fdeltav <= epsilon);
 +
 +      if (trace) {
 +              printf("%s  (%g, %g, %g) %g\n", success ? "OK  " : "FAIL", x[0], x[1], x[2], fdeltav);
 +      }
 +
 +      copy_v3_v3(result, x);
 +      return success;
 +}
@@@ -1000,38 -995,6 +1000,38 @@@ static size_t BLI_str_format_int_groupe
        return (size_t)(p_dst - dst);
  }
  
-  * \param dst  The resulting string
-  * \param num  Number to format
 +/**
 + * Format ints with decimal grouping.
 + * 1000 -> 1,000
 + *
-  * \param dst  The resulting string
-  * \param num  Number to format
++ * \param dst: The resulting string
++ * \param num: Number to format
 + * \return The length of \a dst
 + */
 +size_t BLI_str_format_int_grouped(char dst[16], int num)
 +{
 +      char src[16];
 +      int num_len = sprintf(src, "%d", num);
 +
 +      return BLI_str_format_int_grouped_ex(src, dst, num_len);
 +}
 +
 +/**
 + * Format uint64_t with decimal grouping.
 + * 1000 -> 1,000
 + *
++ * \param dst: The resulting string
++ * \param num: Number to format
 + * \return The length of \a dst
 + */
 +size_t BLI_str_format_uint64_grouped(char dst[16], uint64_t num)
 +{
 +      char src[16];
 +      int num_len = sprintf(src, "%"PRIu64"", num);
 +
 +      return BLI_str_format_int_grouped_ex(src, dst, num_len);
 +}
 +
  /**
   * Format a size in bytes using binary units.
   * 1000 -> 1 KB
Simple merge
@@@ -10576,15 -10258,15 +10576,15 @@@ ID *BLO_library_link_named_part(Main *m
  
  /**
   * Link a named datablock from an external blend file.
 - * Optionally instantiate the object/group in the scene when the flags are set.
 + * Optionally instantiate the object/collection in the scene when the flags are set.
   *
-  * \param mainl The main database to link from (not the active one).
-  * \param bh The blender file handle.
-  * \param idcode The kind of datablock to link.
-  * \param name The name of the datablock (without the 2 char ID prefix).
-  * \param flag Options for linking, used for instantiating.
-  * \param scene The scene in which to instantiate objects/collections (if NULL, no instantiation is done).
-  * \param v3d The active View3D (only to define active layers for instantiated objects & collections, can be NULL).
+  * \param mainl: The main database to link from (not the active one).
+  * \param bh: The blender file handle.
+  * \param idcode: The kind of datablock to link.
+  * \param name: The name of the datablock (without the 2 char ID prefix).
+  * \param flag: Options for linking, used for instantiating.
 - * \param scene: The scene in which to instantiate objects/groups (if NULL, no instantiation is done).
 - * \param v3d: The active View3D (only to define active layers for instantiated objects & groups, can be NULL).
++ * \param scene: The scene in which to instantiate objects/collections (if NULL, no instantiation is done).
++ * \param v3d: The active View3D (only to define active layers for instantiated objects & collections, can be NULL).
   * \return the linked ID when found.
   */
  ID *BLO_library_link_named_part_ex(
@@@ -10783,20 -10466,19 +10783,20 @@@ static void library_link_end(Main *main
  
  /**
   * Finalize linking from a given .blend file (library).
 - * Optionally instance the indirect object/group in the scene when the flags are set.
 + * Optionally instance the indirect object/collection in the scene when the flags are set.
   * \note Do not use \a bh after calling this function, it may frees it.
   *
-  * \param mainl The main database to link from (not the active one).
-  * \param bh The blender file handle (WARNING! may be freed by this function!).
-  * \param flag Options for linking, used for instantiating.
-  * \param bmain The main database in which to instantiate objects/collections
-  * \param scene The scene in which to instantiate objects/collections (if NULL, no instantiation is done).
-  * \param view_layer The scene layer in which to instantiate objects/collections (if NULL, no instantiation is done).
+  * \param mainl: The main database to link from (not the active one).
+  * \param bh: The blender file handle (WARNING! may be freed by this function!).
+  * \param flag: Options for linking, used for instantiating.
 - * \param scene: The scene in which to instantiate objects/groups (if NULL, no instantiation is done).
 - * \param v3d: The active View3D (only to define active layers for instantiated objects & groups, can be NULL).
++ * \param bmain: The main database in which to instantiate objects/collections
++ * \param scene: The scene in which to instantiate objects/collections (if NULL, no instantiation is done).
++ * \param view_layer: The scene layer in which to instantiate objects/collections (if NULL, no instantiation is done).
   */
 -void BLO_library_link_end(Main *mainl, BlendHandle **bh, short flag, Scene *scene, View3D *v3d)
 +void BLO_library_link_end(Main *mainl, BlendHandle **bh, int flag, Main *bmain, Scene *scene, ViewLayer *view_layer)
  {
        FileData *fd = (FileData *)(*bh);
 -      library_link_end(mainl, &fd, flag, scene, v3d);
 +      library_link_end(mainl, &fd, flag, bmain, scene, view_layer);
        *bh = (BlendHandle *)fd;
  }
  
@@@ -327,13 -327,13 +327,13 @@@ BMFace *BM_face_split
   *
   * Like BM_face_split, but with an edge split by \a n intermediate points with given coordinates.
   *
-  * \param bm The bmesh
-  * \param f the original face
-  * \param l_a, l_b vertices which define the split edge, must be different
-  * \param cos Array of coordinates for intermediate points
-  * \param n Length of \a cos (must be > 0)
-  * \param r_l pointer which will receive the BMLoop for the first split edge (from \a l_a) in the new face
-  * \param example Edge used for attributes of splitting edge, if non-NULL
+  * \param bm: The bmesh
+  * \param f: the original face
 - * \param l_a, l_b: Vertices which define the split edge, must be different.
++ * \param l_a, l_b: Vertices which define the split edge, must be different
+  * \param cos: Array of coordinates for intermediate points
+  * \param n: Length of \a cos (must be > 0)
+  * \param r_l: pointer which will receive the BMLoop for the first split edge (from \a l_a) in the new face
+  * \param example: Edge used for attributes of splitting edge, if non-NULL
   *
   * \return Pointer to the newly created face representing one side of the split
   * if the split is successful (and the original original face will be the
index 673b47f,0000000..225dc1a
mode 100644,000000..100644
--- /dev/null
@@@ -1,1132 -1,0 +1,1132 @@@
-  * \param gpd        GP datablock
-  * \param r_point    Point on plane
-  * \param r_normal   Normal vector
 +/*
 + * Copyright 2017, Blender Foundation.
 + *
 + * 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): Antonio Vazquez
 + *
 + */
 +
 +/** \file blender/draw/engines/gpencil/gpencil_shader_fx.c
 + *  \ingroup draw
 + */
 +#include "DNA_gpencil_types.h"
 +#include "DNA_shader_fx_types.h"
 +#include "DNA_screen_types.h"
 +#include "DNA_view3d_types.h"
 +#include "DNA_camera_types.h"
 +
 +#include "BKE_gpencil.h"
 +#include "BKE_shader_fx.h"
 +
 +#include "DRW_engine.h"
 +#include "DRW_render.h"
 +
 +#include "BKE_camera.h"
 +
 +#include "ED_view3d.h"
 +#include "ED_gpencil.h"
 +
 +#include "gpencil_engine.h"
 +
 +extern char datatoc_gpencil_fx_blur_frag_glsl[];
 +extern char datatoc_gpencil_fx_colorize_frag_glsl[];
 +extern char datatoc_gpencil_fx_flip_frag_glsl[];
 +extern char datatoc_gpencil_fx_light_frag_glsl[];
 +extern char datatoc_gpencil_fx_pixel_frag_glsl[];
 +extern char datatoc_gpencil_fx_rim_prepare_frag_glsl[];
 +extern char datatoc_gpencil_fx_rim_resolve_frag_glsl[];
 +extern char datatoc_gpencil_fx_shadow_prepare_frag_glsl[];
 +extern char datatoc_gpencil_fx_shadow_resolve_frag_glsl[];
 +extern char datatoc_gpencil_fx_glow_prepare_frag_glsl[];
 +extern char datatoc_gpencil_fx_glow_resolve_frag_glsl[];
 +extern char datatoc_gpencil_fx_swirl_frag_glsl[];
 +extern char datatoc_gpencil_fx_wave_frag_glsl[];
 +
 +/* verify if this fx is active */
 +static bool effect_is_active(bGPdata *gpd, ShaderFxData *fx, bool is_render)
 +{
 +      if (fx == NULL) {
 +              return false;
 +      }
 +
 +      if (gpd == NULL) {
 +              return false;
 +      }
 +
 +      bool is_edit = GPENCIL_ANY_EDIT_MODE(gpd);
 +      if (((fx->mode & eShaderFxMode_Editmode) == 0) && (is_edit)) {
 +              return false;
 +      }
 +
 +      if (((fx->mode & eShaderFxMode_Realtime) && (is_render == false)) ||
 +          ((fx->mode & eShaderFxMode_Render) && (is_render == true)))
 +      {
 +              return true;
 +      }
 +
 +      return false;
 +}
 +
 +/**
 + * Get normal of draw using one stroke of visible layer
++ * \param gpd: GP datablock
++ * \param r_point: Point on plane
++ * \param r_normal: Normal vector
 + */
 +static bool get_normal_vector(bGPdata *gpd, float r_point[3], float r_normal[3])
 +{
 +      for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) {
 +              if (gpl->flag & GP_LAYER_HIDE)
 +                      continue;
 +
 +              /* get frame  */
 +              bGPDframe *gpf = gpl->actframe;
 +              if (gpf == NULL)
 +                      continue;
 +              for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) {
 +                      if (gps->totpoints >= 3) {
 +                              bGPDspoint *pt = &gps->points[0];
 +                              BKE_gpencil_stroke_normal(gps, r_normal);
 +                              /* in some weird situations, the normal cannot be calculated, so try next stroke */
 +                              if ((r_normal[0] != 0.0f) || (r_normal[1] != 0.0f) || (r_normal[2] != 0.0f)) {
 +                                      copy_v3_v3(r_point, &pt->x);
 +                                      return true;
 +                              }
 +                      }
 +              }
 +      }
 +
 +      return false;
 +}
 +
 +/* helper to get near and far depth of field values */
 +static void GPENCIL_dof_nearfar(Object *camera, float coc, float nearfar[2])
 +{
 +      if (camera == NULL) {
 +              return;
 +      }
 +
 +      const DRWContextState *draw_ctx = DRW_context_state_get();
 +      Scene *scene = draw_ctx->scene;
 +      Camera *cam = (Camera *)camera->data;
 +
 +      float fstop = cam->gpu_dof.fstop;
 +      float focus_dist = BKE_camera_object_dof_distance(camera);
 +      float focal_len = cam->lens;
 +
 +      /* this is factor that converts to the scene scale. focal length and sensor are expressed in mm
 +       * unit.scale_length is how many meters per blender unit we have. We want to convert to blender units though
 +       * because the shader reads coordinates in world space, which is in blender units.
 +       * Note however that focus_distance is already in blender units and shall not be scaled here (see T48157). */
 +      float scale = (scene->unit.system) ? scene->unit.scale_length : 1.0f;
 +      float scale_camera = 0.001f / scale;
 +      /* we want radius here for the aperture number  */
 +      float aperture_scaled = 0.5f * scale_camera * focal_len / fstop;
 +      float focal_len_scaled = scale_camera * focal_len;
 +
 +      float hyperfocal = (focal_len_scaled * focal_len_scaled) / (aperture_scaled * coc);
 +      nearfar[0] = (hyperfocal * focus_dist) / (hyperfocal + focal_len);
 +      nearfar[1] = (hyperfocal * focus_dist) / (hyperfocal - focal_len);
 +}
 +
 +/* ****************  Shader Effects ***************************** */
 +
 +/* Gaussian Blur FX
 + * The effect is done using two shading groups because is faster to apply horizontal
 + * and vertical in different operations.
 + */
 +static void DRW_gpencil_fx_blur(
 +        ShaderFxData *fx, int ob_idx, GPENCIL_e_data *e_data,
 +        GPENCIL_Data *vedata, tGPencilObjectCache *cache)
 +{
 +      if (fx == NULL) {
 +              return;
 +      }
 +
 +      BlurShaderFxData *fxd = (BlurShaderFxData *)fx;
 +
 +      GPENCIL_StorageList *stl = ((GPENCIL_Data *)vedata)->stl;
 +      GPENCIL_PassList *psl = ((GPENCIL_Data *)vedata)->psl;
 +      const DRWContextState *draw_ctx = DRW_context_state_get();
 +      View3D *v3d = draw_ctx->v3d;
 +      RegionView3D *rv3d = draw_ctx->rv3d;
 +      DRWShadingGroup *fx_shgrp;
 +
 +      fxd->blur[0] = fxd->radius[0];
 +      fxd->blur[1] = fxd->radius[1];
 +
 +      /* init weight */
 +      if (fxd->flag & FX_BLUR_DOF_MODE) {
 +              /* viewport and opengl render */
 +              Object *camera = NULL;
 +              if (rv3d) {
 +                      if (rv3d->persp == RV3D_CAMOB) {
 +                              camera = v3d->camera;
 +                      }
 +              }
 +              else {
 +                      camera = stl->storage->camera;
 +              }
 +
 +              if (camera) {
 +                      float nearfar[2];
 +                      GPENCIL_dof_nearfar(camera, fxd->coc, nearfar);
 +                      float zdepth = stl->g_data->gp_object_cache[ob_idx].zdepth;
 +                      /* the object is on focus area */
 +                      if ((zdepth >= nearfar[0]) && (zdepth <= nearfar[1])) {
 +                              fxd->blur[0] = 0;
 +                              fxd->blur[1] = 0;
 +                      }
 +                      else {
 +                              float f;
 +                              if (zdepth < nearfar[0]) {
 +                                      f = nearfar[0] - zdepth;
 +                              }
 +                              else {
 +                                      f = zdepth - nearfar[1];
 +                              }
 +                              fxd->blur[0] = f;
 +                              fxd->blur[1] = f;
 +                              CLAMP2(&fxd->blur[0], 0, fxd->radius[0]);
 +                      }
 +              }
 +              else {
 +                      /* if not camera view, the blur is disabled */
 +                      fxd->blur[0] = 0;
 +                      fxd->blur[1] = 0;
 +              }
 +      }
 +
 +      GPUBatch *fxquad = DRW_cache_fullscreen_quad_get();
 +
 +      fx_shgrp = DRW_shgroup_create(
 +              e_data->gpencil_fx_blur_sh,
 +              psl->fx_shader_pass_blend);
 +      DRW_shgroup_call_add(fx_shgrp, fxquad, NULL);
 +      DRW_shgroup_uniform_texture_ref(fx_shgrp, "strokeColor", &e_data->temp_color_tx_a);
 +      DRW_shgroup_uniform_texture_ref(fx_shgrp, "strokeDepth", &e_data->temp_depth_tx_a);
 +      DRW_shgroup_uniform_vec2(fx_shgrp, "Viewport", DRW_viewport_size_get(), 1);
 +      DRW_shgroup_uniform_int(fx_shgrp, "blur", &fxd->blur[0], 2);
 +
 +      DRW_shgroup_uniform_vec3(fx_shgrp, "loc", &cache->loc[0], 1);
 +      DRW_shgroup_uniform_float(fx_shgrp, "pixsize", stl->storage->pixsize, 1);
 +      DRW_shgroup_uniform_float(fx_shgrp, "pixfactor", &cache->pixfactor, 1);
 +
 +      fxd->runtime.fx_sh = fx_shgrp;
 +}
 +
 +/* Colorize FX */
 +static void DRW_gpencil_fx_colorize(
 +        ShaderFxData *fx, GPENCIL_e_data *e_data, GPENCIL_Data *vedata)
 +{
 +      if (fx == NULL) {
 +              return;
 +      }
 +      ColorizeShaderFxData *fxd = (ColorizeShaderFxData *)fx;
 +      GPENCIL_PassList *psl = ((GPENCIL_Data *)vedata)->psl;
 +      DRWShadingGroup *fx_shgrp;
 +
 +      GPUBatch *fxquad = DRW_cache_fullscreen_quad_get();
 +      fx_shgrp = DRW_shgroup_create(e_data->gpencil_fx_colorize_sh, psl->fx_shader_pass);
 +      DRW_shgroup_call_add(fx_shgrp, fxquad, NULL);
 +      DRW_shgroup_uniform_texture_ref(fx_shgrp, "strokeColor", &e_data->temp_color_tx_a);
 +      DRW_shgroup_uniform_texture_ref(fx_shgrp, "strokeDepth", &e_data->temp_depth_tx_a);
 +      DRW_shgroup_uniform_vec4(fx_shgrp, "low_color", &fxd->low_color[0], 1);
 +      DRW_shgroup_uniform_vec4(fx_shgrp, "high_color", &fxd->high_color[0], 1);
 +      DRW_shgroup_uniform_int(fx_shgrp, "mode", &fxd->mode, 1);
 +      DRW_shgroup_uniform_float(fx_shgrp, "factor", &fxd->factor, 1);
 +
 +      fxd->runtime.fx_sh = fx_shgrp;
 +}
 +
 +/* Flip FX */
 +static void DRW_gpencil_fx_flip(
 +        ShaderFxData *fx, GPENCIL_e_data *e_data, GPENCIL_Data *vedata)
 +{
 +      if (fx == NULL) {
 +              return;
 +      }
 +      FlipShaderFxData *fxd = (FlipShaderFxData *)fx;
 +      GPENCIL_PassList *psl = ((GPENCIL_Data *)vedata)->psl;
 +      DRWShadingGroup *fx_shgrp;
 +
 +      fxd->flipmode = 100;
 +      /* the number works as bit flag */
 +      if (fxd->flag & FX_FLIP_HORIZONTAL) {
 +              fxd->flipmode += 10;
 +      }
 +      if (fxd->flag & FX_FLIP_VERTICAL) {
 +              fxd->flipmode += 1;
 +      }
 +
 +      GPUBatch *fxquad = DRW_cache_fullscreen_quad_get();
 +      fx_shgrp = DRW_shgroup_create(e_data->gpencil_fx_flip_sh, psl->fx_shader_pass);
 +      DRW_shgroup_call_add(fx_shgrp, fxquad, NULL);
 +      DRW_shgroup_uniform_texture_ref(fx_shgrp, "strokeColor", &e_data->temp_color_tx_a);
 +      DRW_shgroup_uniform_texture_ref(fx_shgrp, "strokeDepth", &e_data->temp_depth_tx_a);
 +      DRW_shgroup_uniform_int(fx_shgrp, "flipmode", &fxd->flipmode, 1);
 +
 +      DRW_shgroup_uniform_vec2(fx_shgrp, "wsize", DRW_viewport_size_get(), 1);
 +
 +      fxd->runtime.fx_sh = fx_shgrp;
 +}
 +
 +/* Light FX */
 +static void DRW_gpencil_fx_light(
 +        ShaderFxData *fx, GPENCIL_e_data *e_data, GPENCIL_Data *vedata,
 +        tGPencilObjectCache *cache)
 +{
 +      if (fx == NULL) {
 +              return;
 +      }
 +      LightShaderFxData *fxd = (LightShaderFxData *)fx;
 +
 +      if (fxd->object == NULL) {
 +              return;
 +      }
 +      GPENCIL_StorageList *stl = ((GPENCIL_Data *)vedata)->stl;
 +      GPENCIL_PassList *psl = ((GPENCIL_Data *)vedata)->psl;
 +      DRWShadingGroup *fx_shgrp;
 +
 +      GPUBatch *fxquad = DRW_cache_fullscreen_quad_get();
 +      fx_shgrp = DRW_shgroup_create(e_data->gpencil_fx_light_sh, psl->fx_shader_pass);
 +      DRW_shgroup_call_add(fx_shgrp, fxquad, NULL);
 +      DRW_shgroup_uniform_texture_ref(fx_shgrp, "strokeColor", &e_data->temp_color_tx_a);
 +      DRW_shgroup_uniform_texture_ref(fx_shgrp, "strokeDepth", &e_data->temp_depth_tx_a);
 +
 +      DRW_shgroup_uniform_vec2(fx_shgrp, "Viewport", DRW_viewport_size_get(), 1);
 +
 +      /* location of the light using obj location as origin */
 +      copy_v3_v3(fxd->loc, &fxd->object->loc[0]);
 +
 +      /* Calc distance to strokes plane
 +       * The w component of location is used to transfer the distance to drawing plane
 +       */
 +      float r_point[3], r_normal[3];
 +      float r_plane[4];
 +      bGPdata *gpd = cache->gpd;
 +      if (!get_normal_vector(gpd, r_point, r_normal)) {
 +              return;
 +      }
 +      mul_mat3_m4_v3(cache->obmat, r_normal); /* only rotation component */
 +      plane_from_point_normal_v3(r_plane, r_point, r_normal);
 +      float dt = dist_to_plane_v3(fxd->object->loc, r_plane);
 +      fxd->loc[3] = dt; /* use last element to save it */
 +
 +      DRW_shgroup_uniform_vec4(fx_shgrp, "loc", &fxd->loc[0], 1);
 +
 +      DRW_shgroup_uniform_float(fx_shgrp, "energy", &fxd->energy, 1);
 +      DRW_shgroup_uniform_float(fx_shgrp, "ambient", &fxd->ambient, 1);
 +
 +      DRW_shgroup_uniform_float(fx_shgrp, "pixsize", stl->storage->pixsize, 1);
 +      DRW_shgroup_uniform_float(fx_shgrp, "pixfactor", &cache->pixfactor, 1);
 +
 +      fxd->runtime.fx_sh = fx_shgrp;
 +}
 +
 +/* Pixelate FX */
 +static void DRW_gpencil_fx_pixel(
 +        ShaderFxData *fx, GPENCIL_e_data *e_data, GPENCIL_Data *vedata,
 +        tGPencilObjectCache *cache)
 +{
 +      if (fx == NULL) {
 +              return;
 +      }
 +      PixelShaderFxData *fxd = (PixelShaderFxData *)fx;
 +
 +      GPENCIL_StorageList *stl = ((GPENCIL_Data *)vedata)->stl;
 +      GPENCIL_PassList *psl = ((GPENCIL_Data *)vedata)->psl;
 +      DRWShadingGroup *fx_shgrp;
 +      bGPdata *gpd = cache->gpd;
 +
 +      fxd->size[2] = (int)fxd->flag & FX_PIXEL_USE_LINES;
 +
 +      GPUBatch *fxquad = DRW_cache_fullscreen_quad_get();
 +      fx_shgrp = DRW_shgroup_create(e_data->gpencil_fx_pixel_sh, psl->fx_shader_pass);
 +      DRW_shgroup_call_add(fx_shgrp, fxquad, NULL);
 +      DRW_shgroup_uniform_texture_ref(fx_shgrp, "strokeColor", &e_data->temp_color_tx_a);
 +      DRW_shgroup_uniform_texture_ref(fx_shgrp, "strokeDepth", &e_data->temp_depth_tx_a);
 +      DRW_shgroup_uniform_int(fx_shgrp, "size", &fxd->size[0], 3);
 +      DRW_shgroup_uniform_vec4(fx_shgrp, "color", &fxd->rgba[0], 1);
 +
 +      DRW_shgroup_uniform_vec3(fx_shgrp, "loc", &cache->loc[0], 1);
 +      DRW_shgroup_uniform_float(fx_shgrp, "pixsize", stl->storage->pixsize, 1);
 +      DRW_shgroup_uniform_float(fx_shgrp, "pixfactor", &gpd->pixfactor, 1);
 +
 +      fxd->runtime.fx_sh = fx_shgrp;
 +}
 +
 +/* Rim FX */
 +static void DRW_gpencil_fx_rim(
 +        ShaderFxData *fx, GPENCIL_e_data *e_data, GPENCIL_Data *vedata,
 +        tGPencilObjectCache *cache)
 +{
 +      if (fx == NULL) {
 +              return;
 +      }
 +      RimShaderFxData *fxd = (RimShaderFxData *)fx;
 +
 +      GPENCIL_StorageList *stl = ((GPENCIL_Data *)vedata)->stl;
 +      GPENCIL_PassList *psl = ((GPENCIL_Data *)vedata)->psl;
 +      DRWShadingGroup *fx_shgrp;
 +
 +      GPUBatch *fxquad = DRW_cache_fullscreen_quad_get();
 +      /* prepare pass */
 +      fx_shgrp = DRW_shgroup_create(
 +              e_data->gpencil_fx_rim_prepare_sh,
 +              psl->fx_shader_pass_blend);
 +      DRW_shgroup_call_add(fx_shgrp, fxquad, NULL);
 +      DRW_shgroup_uniform_texture_ref(fx_shgrp, "strokeColor", &e_data->temp_color_tx_a);
 +      DRW_shgroup_uniform_texture_ref(fx_shgrp, "strokeDepth", &e_data->temp_depth_tx_a);
 +      DRW_shgroup_uniform_vec2(fx_shgrp, "Viewport", DRW_viewport_size_get(), 1);
 +
 +      DRW_shgroup_uniform_int(fx_shgrp, "offset", &fxd->offset[0], 2);
 +      DRW_shgroup_uniform_vec3(fx_shgrp, "rim_color", &fxd->rim_rgb[0], 1);
 +      DRW_shgroup_uniform_vec3(fx_shgrp, "mask_color", &fxd->mask_rgb[0], 1);
 +
 +      DRW_shgroup_uniform_vec3(fx_shgrp, "loc", &cache->loc[0], 1);
 +      DRW_shgroup_uniform_float(fx_shgrp, "pixsize", stl->storage->pixsize, 1);
 +      DRW_shgroup_uniform_float(fx_shgrp, "pixfactor", &cache->pixfactor, 1);
 +
 +      fxd->runtime.fx_sh = fx_shgrp;
 +
 +      /* blur pass */
 +      fx_shgrp = DRW_shgroup_create(
 +              e_data->gpencil_fx_blur_sh,
 +              psl->fx_shader_pass_blend);
 +      DRW_shgroup_call_add(fx_shgrp, fxquad, NULL);
 +      DRW_shgroup_uniform_texture_ref(fx_shgrp, "strokeColor", &e_data->temp_color_tx_fx);
 +      DRW_shgroup_uniform_texture_ref(fx_shgrp, "strokeDepth", &e_data->temp_depth_tx_fx);
 +      DRW_shgroup_uniform_vec2(fx_shgrp, "Viewport", DRW_viewport_size_get(), 1);
 +      DRW_shgroup_uniform_int(fx_shgrp, "blur", &fxd->blur[0], 2);
 +
 +      DRW_shgroup_uniform_vec3(fx_shgrp, "loc", &cache->loc[0], 1);
 +      DRW_shgroup_uniform_float(fx_shgrp, "pixsize", stl->storage->pixsize, 1);
 +      DRW_shgroup_uniform_float(fx_shgrp, "pixfactor", &cache->pixfactor, 1);
 +
 +      fxd->runtime.fx_sh_b = fx_shgrp;
 +
 +      /* resolve pass */
 +      fx_shgrp = DRW_shgroup_create(
 +              e_data->gpencil_fx_rim_resolve_sh,
 +              psl->fx_shader_pass_blend);
 +      DRW_shgroup_call_add(fx_shgrp, fxquad, NULL);
 +      DRW_shgroup_uniform_texture_ref(fx_shgrp, "strokeColor", &e_data->temp_color_tx_a);
 +      DRW_shgroup_uniform_texture_ref(fx_shgrp, "strokeDepth", &e_data->temp_depth_tx_a);
 +      DRW_shgroup_uniform_texture_ref(fx_shgrp, "strokeRim", &e_data->temp_color_tx_fx);
 +      DRW_shgroup_uniform_vec3(fx_shgrp, "mask_color", &fxd->mask_rgb[0], 1);
 +      DRW_shgroup_uniform_int(fx_shgrp, "mode", &fxd->mode, 1);
 +
 +      fxd->runtime.fx_sh_c = fx_shgrp;
 +}
 +
 +/* Shadow FX */
 +static void DRW_gpencil_fx_shadow(
 +        ShaderFxData *fx, GPENCIL_e_data *e_data, GPENCIL_Data *vedata,
 +        tGPencilObjectCache *cache)
 +{
 +      if (fx == NULL) {
 +              return;
 +      }
 +      ShadowShaderFxData *fxd = (ShadowShaderFxData *)fx;
 +      if ((!fxd->object) && (fxd->flag & FX_SHADOW_USE_OBJECT)) {
 +              fxd->runtime.fx_sh = NULL;
 +              fxd->runtime.fx_sh_b = NULL;
 +              fxd->runtime.fx_sh_c = NULL;
 +              return;
 +      }
 +
 +      GPENCIL_StorageList *stl = ((GPENCIL_Data *)vedata)->stl;
 +      GPENCIL_PassList *psl = ((GPENCIL_Data *)vedata)->psl;
 +      DRWShadingGroup *fx_shgrp;
 +
 +      GPUBatch *fxquad = DRW_cache_fullscreen_quad_get();
 +      /* prepare pass */
 +      fx_shgrp = DRW_shgroup_create(
 +              e_data->gpencil_fx_shadow_prepare_sh,
 +              psl->fx_shader_pass_blend);
 +      DRW_shgroup_call_add(fx_shgrp, fxquad, NULL);
 +      DRW_shgroup_uniform_texture_ref(fx_shgrp, "strokeColor", &e_data->temp_color_tx_a);
 +      DRW_shgroup_uniform_texture_ref(fx_shgrp, "strokeDepth", &e_data->temp_depth_tx_a);
 +      DRW_shgroup_uniform_vec2(fx_shgrp, "Viewport", DRW_viewport_size_get(), 1);
 +
 +      DRW_shgroup_uniform_int(fx_shgrp, "offset", &fxd->offset[0], 2);
 +      DRW_shgroup_uniform_float(fx_shgrp, "scale", &fxd->scale[0], 2);
 +      DRW_shgroup_uniform_float(fx_shgrp, "rotation", &fxd->rotation, 1);
 +      DRW_shgroup_uniform_vec4(fx_shgrp, "shadow_color", &fxd->shadow_rgba[0], 1);
 +
 +      if ((fxd->object) && (fxd->flag & FX_SHADOW_USE_OBJECT)) {
 +              DRW_shgroup_uniform_vec3(fx_shgrp, "loc", &fxd->object->loc[0], 1);
 +      }
 +      else {
 +              DRW_shgroup_uniform_vec3(fx_shgrp, "loc", &cache->loc[0], 1);
 +      }
 +
 +      const int nowave = -1;
 +      if (fxd->flag & FX_SHADOW_USE_WAVE) {
 +              DRW_shgroup_uniform_int(fx_shgrp, "orientation", &fxd->orientation, 1);
 +      }
 +      else {
 +              DRW_shgroup_uniform_int(fx_shgrp, "orientation", &nowave, 1);
 +      }
 +      DRW_shgroup_uniform_float(fx_shgrp, "amplitude", &fxd->amplitude, 1);
 +      DRW_shgroup_uniform_float(fx_shgrp, "period", &fxd->period, 1);
 +      DRW_shgroup_uniform_float(fx_shgrp, "phase", &fxd->phase, 1);
 +
 +      DRW_shgroup_uniform_float(fx_shgrp, "pixsize", stl->storage->pixsize, 1);
 +      DRW_shgroup_uniform_float(fx_shgrp, "pixfactor", &cache->pixfactor, 1);
 +
 +      fxd->runtime.fx_sh = fx_shgrp;
 +
 +      /* blur pass */
 +      fx_shgrp = DRW_shgroup_create(
 +              e_data->gpencil_fx_blur_sh,
 +              psl->fx_shader_pass_blend);
 +      DRW_shgroup_call_add(fx_shgrp, fxquad, NULL);
 +      DRW_shgroup_uniform_texture_ref(fx_shgrp, "strokeColor", &e_data->temp_color_tx_fx);
 +      DRW_shgroup_uniform_texture_ref(fx_shgrp, "strokeDepth", &e_data->temp_depth_tx_fx);
 +      DRW_shgroup_uniform_vec2(fx_shgrp, "Viewport", DRW_viewport_size_get(), 1);
 +      DRW_shgroup_uniform_int(fx_shgrp, "blur", &fxd->blur[0], 2);
 +
 +      DRW_shgroup_uniform_vec3(fx_shgrp, "loc", &cache->loc[0], 1);
 +      DRW_shgroup_uniform_float(fx_shgrp, "pixsize", stl->storage->pixsize, 1);
 +      DRW_shgroup_uniform_float(fx_shgrp, "pixfactor", &cache->pixfactor, 1);
 +
 +      fxd->runtime.fx_sh_b = fx_shgrp;
 +
 +      /* resolve pass */
 +      fx_shgrp = DRW_shgroup_create(
 +              e_data->gpencil_fx_shadow_resolve_sh,
 +              psl->fx_shader_pass_blend);
 +      DRW_shgroup_call_add(fx_shgrp, fxquad, NULL);
 +      DRW_shgroup_uniform_texture_ref(fx_shgrp, "strokeColor", &e_data->temp_color_tx_a);
 +      DRW_shgroup_uniform_texture_ref(fx_shgrp, "strokeDepth", &e_data->temp_depth_tx_a);
 +