Merge branch 'master' into blender2.8
authorCampbell Barton <ideasman42@gmail.com>
Fri, 14 Dec 2018 00:09:42 +0000 (11:09 +1100)
committerCampbell Barton <ideasman42@gmail.com>
Fri, 14 Dec 2018 00:09:42 +0000 (11:09 +1100)
38 files changed:
1  2 
source/blender/blenkernel/intern/editmesh_cache.c
source/blender/blenkernel/intern/scene.c
source/blender/blenloader/intern/versioning_260.c
source/blender/blenloader/intern/versioning_280.c
source/blender/bmesh/intern/bmesh_core.c
source/blender/bmesh/intern/bmesh_mods.c
source/blender/bmesh/intern/bmesh_opdefines.c
source/blender/bmesh/intern/bmesh_operators.h
source/blender/bmesh/intern/bmesh_polygon.c
source/blender/bmesh/intern/bmesh_polygon.h
source/blender/bmesh/intern/bmesh_query.c
source/blender/bmesh/intern/bmesh_query.h
source/blender/bmesh/tools/bmesh_decimate_collapse.c
source/blender/bmesh/tools/bmesh_intersect.c
source/blender/bmesh/tools/bmesh_path.c
source/blender/draw/intern/draw_cache_impl_mesh.c
source/blender/draw/modes/edit_mesh_mode_text.c
source/blender/editors/armature/armature_edit.c
source/blender/editors/gpencil/gpencil_edit.c
source/blender/editors/mesh/editmesh_bevel.c
source/blender/editors/mesh/editmesh_inset.c
source/blender/editors/mesh/editmesh_select.c
source/blender/editors/mesh/editmesh_select_similar.c
source/blender/editors/mesh/editmesh_tools.c
source/blender/editors/object/object_transform.c
source/blender/editors/space_view3d/view3d_edit.c
source/blender/editors/space_view3d/view3d_snap.c
source/blender/editors/transform/transform.c
source/blender/editors/transform/transform_conversions.c
source/blender/editors/transform/transform_generics.c
source/blender/editors/transform/transform_gizmo_3d.c
source/blender/editors/transform/transform_snap.c
source/blender/editors/uvedit/uvedit_unwrap_ops.c
source/blender/makesdna/DNA_view3d_types.h
source/blender/makesrna/intern/rna_scene.c
source/blender/makesrna/intern/rna_space.c
source/blender/modifiers/intern/MOD_skin.c
source/blender/python/bmesh/bmesh_py_types.c

index f0af0ed,0000000..09bf7e9
mode 100644,000000..100644
--- /dev/null
@@@ -1,120 -1,0 +1,120 @@@
-                       BM_face_calc_center_mean_vcos(bm, efa, polyCos[i], vertexCos);
 +/*
 + * ***** 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.
 + *
 + * ***** END GPL LICENSE BLOCK *****
 + */
 +
 +/** \file blender/blenkernel/intern/editmesh_cache.c
 + *  \ingroup bke
 + *
 + * Manage edit mesh cache: #EditMeshData
 + */
 +
 +#include "MEM_guardedalloc.h"
 +
 +#include "DNA_mesh_types.h"
 +
 +#include "BLI_math.h"
 +
 +#include "BKE_editmesh.h"
 +#include "BKE_editmesh_cache.h"  /* own include */
 +
 +void BKE_editmesh_cache_ensure_poly_normals(BMEditMesh *em, EditMeshData *emd)
 +{
 +      if (!(emd->vertexCos && (emd->polyNos == NULL))) {
 +              return;
 +      }
 +
 +      BMesh *bm = em->bm;
 +      const float (*vertexCos)[3];
 +      float (*polyNos)[3];
 +
 +      BMFace *efa;
 +      BMIter fiter;
 +      int i;
 +
 +      BM_mesh_elem_index_ensure(bm, BM_VERT);
 +
 +      polyNos = MEM_mallocN(sizeof(*polyNos) * bm->totface, __func__);
 +
 +      vertexCos = emd->vertexCos;
 +
 +      BM_ITER_MESH_INDEX (efa, &fiter, bm, BM_FACES_OF_MESH, i) {
 +              BM_elem_index_set(efa, i); /* set_inline */
 +              BM_face_calc_normal_vcos(bm, efa, polyNos[i], vertexCos);
 +      }
 +      bm->elem_index_dirty &= ~BM_FACE;
 +
 +      emd->polyNos = (const float (*)[3])polyNos;
 +}
 +
 +void BKE_editmesh_cache_ensure_vert_normals(BMEditMesh *em, EditMeshData *emd)
 +{
 +      if (!(emd->vertexCos && (emd->vertexNos == NULL))) {
 +              return;
 +      }
 +
 +      BMesh *bm = em->bm;
 +      const float (*vertexCos)[3], (*polyNos)[3];
 +      float (*vertexNos)[3];
 +
 +      /* calculate vertex normals from poly normals */
 +      BKE_editmesh_cache_ensure_poly_normals(em, emd);
 +
 +      BM_mesh_elem_index_ensure(bm, BM_FACE);
 +
 +      polyNos = emd->polyNos;
 +      vertexCos = emd->vertexCos;
 +      vertexNos = MEM_callocN(sizeof(*vertexNos) * bm->totvert, __func__);
 +
 +      BM_verts_calc_normal_vcos(bm, polyNos, vertexCos, vertexNos);
 +
 +      emd->vertexNos = (const float (*)[3])vertexNos;
 +}
 +
 +void BKE_editmesh_cache_ensure_poly_centers(BMEditMesh *em, EditMeshData *emd)
 +{
 +      if (emd->polyCos != NULL) {
 +              return;
 +      }
 +      BMesh *bm = em->bm;
 +      float (*polyCos)[3];
 +
 +      BMFace *efa;
 +      BMIter fiter;
 +      int i;
 +
 +      polyCos = MEM_mallocN(sizeof(*polyCos) * bm->totface, __func__);
 +
 +      if (emd->vertexCos) {
 +              const float (*vertexCos)[3];
 +              vertexCos = emd->vertexCos;
 +
 +              BM_mesh_elem_index_ensure(bm, BM_VERT);
 +
 +              BM_ITER_MESH_INDEX (efa, &fiter, bm, BM_FACES_OF_MESH, i) {
-                       BM_face_calc_center_mean(efa, polyCos[i]);
++                      BM_face_calc_center_median_vcos(bm, efa, polyCos[i], vertexCos);
 +              }
 +      }
 +      else {
 +              BM_ITER_MESH_INDEX (efa, &fiter, bm, BM_FACES_OF_MESH, i) {
++                      BM_face_calc_center_median(efa, polyCos[i]);
 +              }
 +      }
 +
 +      emd->polyCos = (const float (*)[3])polyCos;
 +}
@@@ -659,13 -651,25 +659,13 @@@ void BKE_scene_init(Scene *sce
  
        sce->toolsettings->selectmode = SCE_SELECT_VERTEX;
        sce->toolsettings->uv_selectmode = UV_SELECT_VERTEX;
 -      sce->toolsettings->normalsize = 0.1;
        sce->toolsettings->autokey_mode = U.autokey_mode;
  
 -      sce->toolsettings->snap_node_mode = SCE_SNAP_MODE_GRID;
  
-       sce->toolsettings->transform_pivot_point = V3D_AROUND_CENTER_MEAN;
 -      sce->toolsettings->skgen_resolution = 100;
 -      sce->toolsettings->skgen_threshold_internal     = 0.01f;
 -      sce->toolsettings->skgen_threshold_external     = 0.01f;
 -      sce->toolsettings->skgen_angle_limit            = 45.0f;
 -      sce->toolsettings->skgen_length_ratio           = 1.3f;
 -      sce->toolsettings->skgen_length_limit           = 1.5f;
 -      sce->toolsettings->skgen_correlation_limit      = 0.98f;
 -      sce->toolsettings->skgen_symmetry_limit         = 0.1f;
 -      sce->toolsettings->skgen_postpro = SKGEN_SMOOTH;
 -      sce->toolsettings->skgen_postpro_passes = 1;
 -      sce->toolsettings->skgen_options = SKGEN_FILTER_INTERNAL | SKGEN_FILTER_EXTERNAL | SKGEN_FILTER_SMART | SKGEN_HARMONIC | SKGEN_SUB_CORRELATION | SKGEN_STICK_TO_EMBEDDING;
 -      sce->toolsettings->skgen_subdivisions[0] = SKGEN_SUB_CORRELATION;
 -      sce->toolsettings->skgen_subdivisions[1] = SKGEN_SUB_LENGTH;
 -      sce->toolsettings->skgen_subdivisions[2] = SKGEN_SUB_ANGLE;
++      sce->toolsettings->transform_pivot_point = V3D_AROUND_CENTER_MEDIAN;
 +      sce->toolsettings->snap_mode = SCE_SNAP_MODE_INCREMENT;
 +      sce->toolsettings->snap_node_mode = SCE_SNAP_MODE_GRID;
 +      sce->toolsettings->snap_uv_mode = SCE_SNAP_MODE_INCREMENT;
  
        sce->toolsettings->curve_paint_settings.curve_type = CU_BEZIER;
        sce->toolsettings->curve_paint_settings.flag |= CURVE_PAINT_FLAG_CORNERS_DETECT;
index e2ac0f1,0000000..56ca663
mode 100644,000000..100644
--- /dev/null
@@@ -1,2525 -1,0 +1,2525 @@@
-                               scene->toolsettings->transform_pivot_point = V3D_AROUND_CENTER_MEAN;
 +/*
 + * ***** 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/blenloader/intern/versioning_280.c
 + *  \ingroup blenloader
 + */
 +
 +/* allow readfile to use deprecated functionality */
 +#define DNA_DEPRECATED_ALLOW
 +
 +#include <string.h>
 +#include <float.h>
 +
 +#include "BLI_listbase.h"
 +#include "BLI_math.h"
 +#include "BLI_mempool.h"
 +#include "BLI_string.h"
 +#include "BLI_string_utf8.h"
 +#include "BLI_utildefines.h"
 +
 +#include "DNA_object_types.h"
 +#include "DNA_camera_types.h"
 +#include "DNA_cloth_types.h"
 +#include "DNA_collection_types.h"
 +#include "DNA_constraint_types.h"
 +#include "DNA_gpu_types.h"
 +#include "DNA_lamp_types.h"
 +#include "DNA_layer_types.h"
 +#include "DNA_lightprobe_types.h"
 +#include "DNA_material_types.h"
 +#include "DNA_mesh_types.h"
 +#include "DNA_modifier_types.h"
 +#include "DNA_particle_types.h"
 +#include "DNA_rigidbody_types.h"
 +#include "DNA_scene_types.h"
 +#include "DNA_screen_types.h"
 +#include "DNA_view3d_types.h"
 +#include "DNA_genfile.h"
 +#include "DNA_gpencil_types.h"
 +#include "DNA_workspace_types.h"
 +#include "DNA_key_types.h"
 +#include "DNA_curve_types.h"
 +#include "DNA_armature_types.h"
 +
 +#include "BKE_action.h"
 +#include "BKE_cloth.h"
 +#include "BKE_collection.h"
 +#include "BKE_constraint.h"
 +#include "BKE_colortools.h"
 +#include "BKE_customdata.h"
 +#include "BKE_freestyle.h"
 +#include "BKE_gpencil.h"
 +#include "BKE_idprop.h"
 +#include "BKE_image.h"
 +#include "BKE_key.h"
 +#include "BKE_library.h"
 +#include "BKE_layer.h"
 +#include "BKE_main.h"
 +#include "BKE_material.h"
 +#include "BKE_mesh.h"
 +#include "BKE_node.h"
 +#include "BKE_object.h"
 +#include "BKE_paint.h"
 +#include "BKE_pointcache.h"
 +#include "BKE_report.h"
 +#include "BKE_rigidbody.h"
 +#include "BKE_scene.h"
 +#include "BKE_screen.h"
 +#include "BKE_sequencer.h"
 +#include "BKE_studiolight.h"
 +#include "BKE_unit.h"
 +#include "BKE_workspace.h"
 +
 +/* Only for IMB_BlendMode */
 +#include "IMB_imbuf.h"
 +
 +#include "DEG_depsgraph.h"
 +
 +#include "BLT_translation.h"
 +
 +#include "BLO_readfile.h"
 +#include "readfile.h"
 +
 +#include "MEM_guardedalloc.h"
 +
 +static bScreen *screen_parent_find(const bScreen *screen)
 +{
 +      /* can avoid lookup if screen state isn't maximized/full (parent and child store the same state) */
 +      if (ELEM(screen->state, SCREENMAXIMIZED, SCREENFULL)) {
 +              for (const ScrArea *sa = screen->areabase.first; sa; sa = sa->next) {
 +                      if (sa->full && sa->full != screen) {
 +                              BLI_assert(sa->full->state == screen->state);
 +                              return sa->full;
 +                      }
 +              }
 +      }
 +
 +      return NULL;
 +}
 +
 +static void do_version_workspaces_create_from_screens(Main *bmain)
 +{
 +      for (bScreen *screen = bmain->screen.first; screen; screen = screen->id.next) {
 +              const bScreen *screen_parent = screen_parent_find(screen);
 +              WorkSpace *workspace;
 +              if (screen->temp) {
 +                      continue;
 +              }
 +
 +              if (screen_parent) {
 +                      /* fullscreen with "Back to Previous" option, don't create
 +                       * a new workspace, add layout workspace containing parent */
 +                      workspace = BLI_findstring(
 +                              &bmain->workspaces, screen_parent->id.name + 2, offsetof(ID, name) + 2);
 +              }
 +              else {
 +                      workspace = BKE_workspace_add(bmain, screen->id.name + 2);
 +              }
 +              if (workspace == NULL) {
 +                      continue;  /* Not much we can do.. */
 +              }
 +              BKE_workspace_layout_add(bmain, workspace, screen, screen->id.name + 2);
 +      }
 +}
 +
 +static void do_version_area_change_space_to_space_action(ScrArea *area, const Scene *scene)
 +{
 +      SpaceType *stype = BKE_spacetype_from_id(SPACE_ACTION);
 +      SpaceAction *saction = (SpaceAction *)stype->new(area, scene);
 +      ARegion *region_channels;
 +
 +      /* Properly free current regions */
 +      for (ARegion *region = area->regionbase.first; region; region = region->next) {
 +              BKE_area_region_free(area->type, region);
 +      }
 +      BLI_freelistN(&area->regionbase);
 +
 +      area->type = stype;
 +      area->spacetype = stype->spaceid;
 +
 +      BLI_addhead(&area->spacedata, saction);
 +      area->regionbase = saction->regionbase;
 +      BLI_listbase_clear(&saction->regionbase);
 +
 +      /* Different defaults for timeline */
 +      region_channels = BKE_area_find_region_type(area, RGN_TYPE_CHANNELS);
 +      region_channels->flag |= RGN_FLAG_HIDDEN;
 +
 +      saction->mode = SACTCONT_TIMELINE;
 +      saction->ads.flag |= ADS_FLAG_SUMMARY_COLLAPSED;
 +      saction->ads.filterflag |= ADS_FILTER_SUMMARY;
 +}
 +
 +/**
 + * \brief After lib-link versioning for new workspace design.
 + *
 + * - Adds a workspace for (almost) each screen of the old file
 + *   and adds the needed workspace-layout to wrap the screen.
 + * - Active screen isn't stored directly in window anymore, but in the active workspace.
 + * - Active scene isn't stored in screen anymore, but in window.
 + * - Create workspace instance hook for each window.
 + *
 + * \note Some of the created workspaces might be deleted again in case of reading the default startup.blend.
 + */
 +static void do_version_workspaces_after_lib_link(Main *bmain)
 +{
 +      BLI_assert(BLI_listbase_is_empty(&bmain->workspaces));
 +
 +      do_version_workspaces_create_from_screens(bmain);
 +
 +      for (wmWindowManager *wm = bmain->wm.first; wm; wm = wm->id.next) {
 +              for (wmWindow *win = wm->windows.first; win; win = win->next) {
 +                      bScreen *screen_parent = screen_parent_find(win->screen);
 +                      bScreen *screen = screen_parent ? screen_parent : win->screen;
 +
 +                      if (screen->temp) {
 +                              /* We do not generate a new workspace for those screens... still need to set some data in win. */
 +                              win->workspace_hook = BKE_workspace_instance_hook_create(bmain);
 +                              win->scene = screen->scene;
 +                              /* Deprecated from now on! */
 +                              win->screen = NULL;
 +                              continue;
 +                      }
 +
 +                      WorkSpace *workspace = BLI_findstring(&bmain->workspaces, screen->id.name + 2, offsetof(ID, name) + 2);
 +                      BLI_assert(workspace != NULL);
 +                      ListBase *layouts = BKE_workspace_layouts_get(workspace);
 +
 +                      win->workspace_hook = BKE_workspace_instance_hook_create(bmain);
 +
 +                      BKE_workspace_active_set(win->workspace_hook, workspace);
 +                      BKE_workspace_active_layout_set(win->workspace_hook, layouts->first);
 +
 +                      /* Move scene and view layer to window. */
 +                      Scene *scene = screen->scene;
 +                      ViewLayer *layer = BLI_findlink(&scene->view_layers, scene->r.actlay);
 +                      if (!layer) {
 +                              layer = BKE_view_layer_default_view(scene);
 +                      }
 +
 +                      win->scene = scene;
 +                      STRNCPY(win->view_layer_name, layer->name);
 +
 +                      /* Deprecated from now on! */
 +                      win->screen = NULL;
 +              }
 +      }
 +
 +      for (bScreen *screen = bmain->screen.first; screen; screen = screen->id.next) {
 +              /* Deprecated from now on! */
 +              BLI_freelistN(&screen->scene->transform_spaces);
 +              screen->scene = NULL;
 +      }
 +}
 +
 +#ifdef USE_COLLECTION_COMPAT_28
 +enum {
 +      COLLECTION_DEPRECATED_VISIBLE    = (1 << 0),
 +      COLLECTION_DEPRECATED_VIEWPORT   = (1 << 0),
 +      COLLECTION_DEPRECATED_SELECTABLE = (1 << 1),
 +      COLLECTION_DEPRECATED_DISABLED   = (1 << 2),
 +      COLLECTION_DEPRECATED_RENDER     = (1 << 3),
 +};
 +
 +static void do_version_view_layer_visibility(ViewLayer *view_layer)
 +{
 +      /* Convert from deprecated VISIBLE flag to DISABLED */
 +      LayerCollection *lc;
 +      for (lc = view_layer->layer_collections.first;
 +           lc;
 +           lc = lc->next)
 +      {
 +              if (lc->flag & COLLECTION_DEPRECATED_DISABLED) {
 +                      lc->flag &= ~COLLECTION_DEPRECATED_DISABLED;
 +              }
 +
 +              if ((lc->flag & COLLECTION_DEPRECATED_VISIBLE) == 0) {
 +                      lc->flag |= COLLECTION_DEPRECATED_DISABLED;
 +              }
 +
 +              lc->flag |= COLLECTION_DEPRECATED_VIEWPORT | COLLECTION_DEPRECATED_RENDER;
 +      }
 +}
 +
 +static void do_version_layer_collection_pre(
 +        ViewLayer *view_layer,
 +        ListBase *lb,
 +        GSet *enabled_set,
 +        GSet *selectable_set)
 +{
 +      /* Convert from deprecated DISABLED to new layer collection and collection flags */
 +      for (LayerCollection *lc = lb->first; lc; lc = lc->next) {
 +              if (lc->scene_collection) {
 +                      if (!(lc->flag & COLLECTION_DEPRECATED_DISABLED)) {
 +                              BLI_gset_insert(enabled_set, lc->scene_collection);
 +                      }
 +                      if (lc->flag & COLLECTION_DEPRECATED_SELECTABLE) {
 +                              BLI_gset_insert(selectable_set, lc->scene_collection);
 +                      }
 +              }
 +
 +              do_version_layer_collection_pre(view_layer, &lc->layer_collections, enabled_set, selectable_set);
 +      }
 +}
 +
 +static void do_version_layer_collection_post(
 +        ViewLayer *view_layer,
 +        ListBase *lb,
 +        GSet *enabled_set,
 +        GSet *selectable_set,
 +        GHash *collection_map)
 +{
 +      /* Apply layer collection exclude flags. */
 +      for (LayerCollection *lc = lb->first; lc; lc = lc->next) {
 +              if (!(lc->collection->flag & COLLECTION_IS_MASTER)) {
 +                      SceneCollection *sc = BLI_ghash_lookup(collection_map, lc->collection);
 +                      const bool enabled = (sc && BLI_gset_haskey(enabled_set, sc));
 +                      const bool selectable = (sc && BLI_gset_haskey(selectable_set, sc));
 +
 +                      if (!enabled) {
 +                              lc->flag |= LAYER_COLLECTION_EXCLUDE;
 +                      }
 +                      if (enabled && !selectable) {
 +                              lc->collection->flag |= COLLECTION_RESTRICT_SELECT;
 +                      }
 +              }
 +
 +              do_version_layer_collection_post(
 +                      view_layer, &lc->layer_collections, enabled_set, selectable_set, collection_map);
 +      }
 +}
 +
 +static void do_version_scene_collection_convert(
 +        Main *bmain,
 +        ID *id,
 +        SceneCollection *sc,
 +        Collection *collection,
 +        GHash *collection_map)
 +{
 +      if (collection_map) {
 +              BLI_ghash_insert(collection_map, collection, sc);
 +      }
 +
 +      for (SceneCollection *nsc = sc->scene_collections.first; nsc;) {
 +              SceneCollection *nsc_next = nsc->next;
 +              Collection *ncollection = BKE_collection_add(bmain, collection, nsc->name);
 +              ncollection->id.lib = id->lib;
 +              do_version_scene_collection_convert(bmain, id, nsc, ncollection, collection_map);
 +              nsc = nsc_next;
 +      }
 +
 +      for (LinkData *link = sc->objects.first; link; link = link->next) {
 +              Object *ob = link->data;
 +              if (ob) {
 +                      BKE_collection_object_add(bmain, collection, ob);
 +                      id_us_min(&ob->id);
 +              }
 +      }
 +
 +      BLI_freelistN(&sc->objects);
 +      MEM_freeN(sc);
 +}
 +
 +static void do_version_group_collection_to_collection(Main *bmain, Collection *group)
 +{
 +      /* Convert old 2.8 group collections to new unified collections. */
 +      if (group->collection) {
 +              do_version_scene_collection_convert(bmain, &group->id, group->collection, group, NULL);
 +      }
 +
 +      group->collection = NULL;
 +      group->view_layer = NULL;
 +      id_fake_user_set(&group->id);
 +}
 +
 +static void do_version_scene_collection_to_collection(Main *bmain, Scene *scene)
 +{
 +      /* Convert old 2.8 scene collections to new unified collections. */
 +
 +      /* Temporarily clear view layers so we don't do any layer collection syncing
 +       * and destroy old flags that we want to restore. */
 +      ListBase view_layers = scene->view_layers;
 +      BLI_listbase_clear(&scene->view_layers);
 +
 +      if (!scene->master_collection) {
 +              scene->master_collection = BKE_collection_master_add();
 +      }
 +
 +      /* Convert scene collections. */
 +      GHash *collection_map = BLI_ghash_new(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp, __func__);
 +      if (scene->collection) {
 +              do_version_scene_collection_convert(bmain, &scene->id, scene->collection, scene->master_collection, collection_map);
 +              scene->collection = NULL;
 +      }
 +
 +      scene->view_layers = view_layers;
 +
 +      /* Convert layer collections. */
 +      ViewLayer *view_layer;
 +      for (view_layer = scene->view_layers.first; view_layer; view_layer = view_layer->next) {
 +              GSet *enabled_set = BLI_gset_new(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp, __func__);
 +              GSet *selectable_set = BLI_gset_new(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp, __func__);
 +
 +              do_version_layer_collection_pre(
 +                      view_layer, &view_layer->layer_collections, enabled_set, selectable_set);
 +
 +              BKE_layer_collection_sync(scene, view_layer);
 +
 +              do_version_layer_collection_post(
 +                      view_layer, &view_layer->layer_collections, enabled_set, selectable_set, collection_map);
 +
 +              BLI_gset_free(enabled_set, NULL);
 +              BLI_gset_free(selectable_set, NULL);
 +
 +              BKE_layer_collection_sync(scene, view_layer);
 +      }
 +
 +      BLI_ghash_free(collection_map, NULL, NULL);
 +}
 +#endif
 +
 +
 +static void do_version_layers_to_collections(Main *bmain, Scene *scene)
 +{
 +      /* Since we don't have access to FileData we check the (always valid) first
 +       * render layer instead. */
 +      if (!scene->master_collection) {
 +              scene->master_collection = BKE_collection_master_add();
 +      }
 +
 +      if (scene->view_layers.first) {
 +              return;
 +      }
 +
 +      /* Create collections from layers. */
 +      Collection *collection_master = BKE_collection_master(scene);
 +      Collection *collections[20] = {NULL};
 +
 +      for (int layer = 0; layer < 20; layer++) {
 +              for (Base *base = scene->base.first; base; base = base->next) {
 +                      if (base->lay & (1 << layer)) {
 +                              /* Create collections when needed only. */
 +                              if (collections[layer] == NULL) {
 +                                      char name[MAX_NAME];
 +
 +                                      BLI_snprintf(name,
 +                                                   sizeof(collection_master->id.name),
 +                                                   DATA_("Collection %d"),
 +                                                   layer + 1);
 +
 +                                      Collection *collection = BKE_collection_add(bmain, collection_master, name);
 +                                      collection->id.lib = scene->id.lib;
 +                                      collections[layer] = collection;
 +
 +                                      if (!(scene->lay & (1 << layer))) {
 +                                              collection->flag |= COLLECTION_RESTRICT_VIEW | COLLECTION_RESTRICT_RENDER;
 +                                      }
 +                              }
 +
 +                              /* Note usually this would do slow collection syncing for view layers,
 +                               * but since no view layers exists yet at this point it's fast. */
 +                              BKE_collection_object_add(
 +                                      bmain,
 +                                      collections[layer], base->object);
 +                      }
 +
 +                      if (base->flag & SELECT) {
 +                              base->object->flag |= SELECT;
 +                      }
 +                      else {
 +                              base->object->flag &= ~SELECT;
 +                      }
 +              }
 +      }
 +
 +      /* Handle legacy render layers. */
 +      bool have_override = false;
 +      const bool need_default_renderlayer = scene->r.layers.first == NULL;
 +
 +      for (SceneRenderLayer *srl = scene->r.layers.first; srl; srl = srl->next) {
 +              ViewLayer *view_layer = BKE_view_layer_add(scene, srl->name);
 +
 +              if (srl->samples != 0) {
 +                      have_override = true;
 +
 +                      /* It is up to the external engine to handle
 +                       * its own doversion in this case. */
 +                      BKE_override_view_layer_int_add(
 +                              view_layer,
 +                              ID_SCE,
 +                              "samples",
 +                              srl->samples);
 +              }
 +
 +              if (srl->mat_override) {
 +                      have_override = true;
 +
 +                      BKE_override_view_layer_datablock_add(
 +                              view_layer,
 +                              ID_MA,
 +                              "self",
 +                              (ID *)srl->mat_override);
 +              }
 +
 +              if (srl->layflag & SCE_LAY_DISABLE) {
 +                      view_layer->flag &= ~VIEW_LAYER_RENDER;
 +              }
 +
 +              if ((srl->layflag & SCE_LAY_FRS) == 0) {
 +                      view_layer->flag &= ~VIEW_LAYER_FREESTYLE;
 +              }
 +
 +              /* XXX If we are to keep layflag it should be merged with flag (dfelinto). */
 +              view_layer->layflag = srl->layflag;
 +              /* XXX Not sure if we should keep the passes (dfelinto). */
 +              view_layer->passflag = srl->passflag;
 +              view_layer->pass_xor = srl->pass_xor;
 +              view_layer->pass_alpha_threshold = srl->pass_alpha_threshold;
 +
 +              BKE_freestyle_config_free(&view_layer->freestyle_config, true);
 +              view_layer->freestyle_config = srl->freestyleConfig;
 +              view_layer->id_properties = srl->prop;
 +
 +              /* Set exclusion and overrides. */
 +              for (int layer = 0; layer < 20; layer++) {
 +                      Collection *collection = collections[layer];
 +                      if (collection) {
 +                              LayerCollection *lc = BKE_layer_collection_first_from_scene_collection(view_layer, collection);
 +
 +                              if (srl->lay_exclude & (1 << layer)) {
 +                                      /* Disable excluded layer. */
 +                                      have_override = true;
 +                                      lc->flag |= LAYER_COLLECTION_EXCLUDE;
 +                                      for (LayerCollection *nlc = lc->layer_collections.first; nlc; nlc = nlc->next) {
 +                                              nlc->flag |= LAYER_COLLECTION_EXCLUDE;
 +                                      }
 +                              }
 +                              else {
 +                                      if (srl->lay_zmask & (1 << layer)) {
 +                                              have_override = true;
 +                                              lc->flag |= LAYER_COLLECTION_HOLDOUT;
 +                                      }
 +
 +                                      if ((srl->lay & (1 << layer)) == 0) {
 +                                              have_override = true;
 +                                              lc->flag |= LAYER_COLLECTION_INDIRECT_ONLY;
 +                                      }
 +                              }
 +                      }
 +              }
 +
 +              /* for convenience set the same active object in all the layers */
 +              if (scene->basact) {
 +                      view_layer->basact = BKE_view_layer_base_find(view_layer, scene->basact->object);
 +              }
 +
 +              for (Base *base = view_layer->object_bases.first; base; base = base->next) {
 +                      if ((base->flag & BASE_SELECTABLE) && (base->object->flag & SELECT)) {
 +                              base->flag |= BASE_SELECTED;
 +                      }
 +              }
 +      }
 +
 +      BLI_freelistN(&scene->r.layers);
 +
 +      /* If render layers included overrides, or there are no render layers,
 +       * we also create a vanilla viewport layer. */
 +      if (have_override || need_default_renderlayer) {
 +              ViewLayer *view_layer = BKE_view_layer_add(scene, "Viewport");
 +
 +              /* Make it first in the list. */
 +              BLI_remlink(&scene->view_layers, view_layer);
 +              BLI_addhead(&scene->view_layers, view_layer);
 +
 +              /* If we ported all the original render layers, we don't need to make the viewport layer renderable. */
 +              if (!BLI_listbase_is_single(&scene->view_layers)) {
 +                      view_layer->flag &= ~VIEW_LAYER_RENDER;
 +              }
 +
 +              /* convert active base */
 +              if (scene->basact) {
 +                      view_layer->basact = BKE_view_layer_base_find(view_layer, scene->basact->object);
 +              }
 +
 +              /* convert selected bases */
 +              for (Base *base = view_layer->object_bases.first; base; base = base->next) {
 +                      if ((base->flag & BASE_SELECTABLE) && (base->object->flag & SELECT)) {
 +                              base->flag |= BASE_SELECTED;
 +                      }
 +
 +                      /* keep lay around for forward compatibility (open those files in 2.79) */
 +                      base->lay = base->object->lay;
 +              }
 +      }
 +
 +      /* remove bases once and for all */
 +      for (Base *base = scene->base.first; base; base = base->next) {
 +              id_us_min(&base->object->id);
 +      }
 +
 +      BLI_freelistN(&scene->base);
 +      scene->basact = NULL;
 +}
 +
 +static void do_version_collection_propagate_lib_to_children(Collection *collection)
 +{
 +      if (collection->id.lib != NULL) {
 +              for (CollectionChild *collection_child = collection->children.first;
 +                   collection_child != NULL;
 +                   collection_child = collection_child->next)
 +              {
 +                      if (collection_child->collection->id.lib == NULL) {
 +                              collection_child->collection->id.lib = collection->id.lib;
 +                      }
 +                      do_version_collection_propagate_lib_to_children(collection_child->collection);
 +              }
 +      }
 +}
 +
 +void do_versions_after_linking_280(Main *bmain)
 +{
 +      bool use_collection_compat_28 = true;
 +
 +      if (!MAIN_VERSION_ATLEAST(bmain, 280, 0)) {
 +              use_collection_compat_28 = false;
 +
 +              /* Convert group layer visibility flags to hidden nested collection. */
 +              for (Collection *collection = bmain->collection.first; collection; collection = collection->id.next) {
 +                      /* Add fake user for all existing groups. */
 +                      id_fake_user_set(&collection->id);
 +
 +                      if (collection->flag & (COLLECTION_RESTRICT_VIEW | COLLECTION_RESTRICT_RENDER)) {
 +                              continue;
 +                      }
 +
 +                      Collection *hidden_collection_array[20] = {NULL};
 +                      for (CollectionObject *cob = collection->gobject.first, *cob_next = NULL; cob; cob = cob_next) {
 +                              cob_next = cob->next;
 +                              Object *ob = cob->ob;
 +
 +                              if (!(ob->lay & collection->layer)) {
 +                                      /* Find or create hidden collection matching object's first layer. */
 +                                      Collection **collection_hidden = NULL;
 +                                      int coll_idx = 0;
 +                                      for (; coll_idx < 20; coll_idx++) {
 +                                              if (ob->lay & (1 << coll_idx)) {
 +                                                      collection_hidden = &hidden_collection_array[coll_idx];
 +                                                      break;
 +                                              }
 +                                      }
 +                                      BLI_assert(collection_hidden != NULL);
 +
 +                                      if (*collection_hidden == NULL) {
 +                                              char name[MAX_ID_NAME];
 +                                              BLI_snprintf(name, sizeof(name), DATA_("Hidden %d"), coll_idx + 1);
 +                                              *collection_hidden = BKE_collection_add(bmain, collection, name);
 +                                              (*collection_hidden)->flag |= COLLECTION_RESTRICT_VIEW | COLLECTION_RESTRICT_RENDER;
 +                                      }
 +
 +                                      BKE_collection_object_add(bmain, *collection_hidden, ob);
 +                                      BKE_collection_object_remove(bmain, collection, ob, true);
 +                              }
 +                      }
 +              }
 +
 +              /* We need to assign lib pointer to generated hidden collections *after* all have been created, otherwise we'll
 +               * end up with several datablocks sharing same name/library, which is FORBIDDEN!
 +               * Note: we need this to be recursive, since a child collection may be sorted before its parent in bmain... */
 +              for (Collection *collection = bmain->collection.first; collection != NULL; collection = collection->id.next) {
 +                      do_version_collection_propagate_lib_to_children(collection);
 +              }
 +
 +              /* Convert layers to collections. */
 +              for (Scene *scene = bmain->scene.first; scene; scene = scene->id.next) {
 +                      do_version_layers_to_collections(bmain, scene);
 +              }
 +      }
 +
 +      if (!MAIN_VERSION_ATLEAST(bmain, 280, 0)) {
 +              for (bScreen *screen = bmain->screen.first; screen; screen = screen->id.next) {
 +                      /* same render-layer as do_version_workspaces_after_lib_link will activate,
 +                       * so same layer as BKE_view_layer_default_view would return */
 +                      ViewLayer *layer = screen->scene->view_layers.first;
 +
 +                      for (ScrArea *sa = screen->areabase.first; sa; sa = sa->next) {
 +                              for (SpaceLink *space = sa->spacedata.first; space; space = space->next) {
 +                                      if (space->spacetype == SPACE_OUTLINER) {
 +                                              SpaceOops *soutliner = (SpaceOops *)space;
 +
 +                                              soutliner->outlinevis = SO_VIEW_LAYER;
 +
 +                                              if (BLI_listbase_count_at_most(&layer->layer_collections, 2) == 1) {
 +                                                      if (soutliner->treestore == NULL) {
 +                                                              soutliner->treestore = BLI_mempool_create(
 +                                                                      sizeof(TreeStoreElem), 1, 512, BLI_MEMPOOL_ALLOW_ITER);
 +                                                      }
 +
 +                                                      /* Create a tree store element for the collection. This is normally
 +                                                       * done in check_persistent (outliner_tree.c), but we need to access
 +                                                       * it here :/ (expand element if it's the only one) */
 +                                                      TreeStoreElem *tselem = BLI_mempool_calloc(soutliner->treestore);
 +                                                      tselem->type = TSE_LAYER_COLLECTION;
 +                                                      tselem->id = layer->layer_collections.first;
 +                                                      tselem->nr = tselem->used = 0;
 +                                                      tselem->flag &= ~TSE_CLOSED;
 +                                              }
 +                                      }
 +                              }
 +                      }
 +              }
 +      }
 +
 +      /* New workspace design */
 +      if (!MAIN_VERSION_ATLEAST(bmain, 280, 1)) {
 +              do_version_workspaces_after_lib_link(bmain);
 +      }
 +
 +      if (!MAIN_VERSION_ATLEAST(bmain, 280, 2)) {
 +              /* Cleanup any remaining SceneRenderLayer data for files that were created
 +               * with Blender 2.8 before the SceneRenderLayer > RenderLayer refactor. */
 +              for (Scene *scene = bmain->scene.first; scene; scene = scene->id.next) {
 +                      for (SceneRenderLayer *srl = scene->r.layers.first; srl; srl = srl->next) {
 +                              if (srl->prop) {
 +                                      IDP_FreeProperty(srl->prop);
 +                                      MEM_freeN(srl->prop);
 +                              }
 +                              BKE_freestyle_config_free(&srl->freestyleConfig, true);
 +                      }
 +                      BLI_freelistN(&scene->r.layers);
 +              }
 +      }
 +
 +      if (!MAIN_VERSION_ATLEAST(bmain, 280, 3)) {
 +              /* Due to several changes to particle RNA and draw code particles from older files may no longer
 +               * be visible. Here we correct this by setting a default draw size for those files. */
 +              for (Object *object = bmain->object.first; object; object = object->id.next) {
 +                      for (ParticleSystem *psys = object->particlesystem.first; psys; psys = psys->next) {
 +                              if (psys->part->draw_size == 0.0f) {
 +                                      psys->part->draw_size = 0.1f;
 +                              }
 +                      }
 +              }
 +      }
 +
 +      if (!MAIN_VERSION_ATLEAST(bmain, 280, 4)) {
 +              for (Object *object = bmain->object.first; object; object = object->id.next) {
 +                      if (object->particlesystem.first) {
 +                              object->duplicator_visibility_flag = OB_DUPLI_FLAG_VIEWPORT;
 +                              for (ParticleSystem *psys = object->particlesystem.first; psys; psys = psys->next) {
 +                                      if (psys->part->draw & PART_DRAW_EMITTER) {
 +                                              object->duplicator_visibility_flag |= OB_DUPLI_FLAG_RENDER;
 +                                              break;
 +                                      }
 +                              }
 +                      }
 +                      else if (object->transflag & OB_DUPLI) {
 +                              object->duplicator_visibility_flag = OB_DUPLI_FLAG_VIEWPORT;
 +                      }
 +                      else {
 +                              object->duplicator_visibility_flag = OB_DUPLI_FLAG_VIEWPORT | OB_DUPLI_FLAG_RENDER;
 +                      }
 +              }
 +
 +              /* Cleanup deprecated flag from particlesettings data-blocks. */
 +              for (ParticleSettings *part = bmain->particle.first; part; part = part->id.next) {
 +                      part->draw &= ~PART_DRAW_EMITTER;
 +              }
 +      }
 +
 +      /* SpaceTime & SpaceLogic removal/replacing */
 +      if (!MAIN_VERSION_ATLEAST(bmain, 280, 9)) {
 +              const wmWindowManager *wm = bmain->wm.first;
 +              const Scene *scene = bmain->scene.first;
 +
 +              if (wm != NULL) {
 +                      /* Action editors need a scene for creation. First, update active
 +                       * screens using the active scene of the window they're displayed in.
 +                       * Next, update remaining screens using first scene in main listbase. */
 +
 +                      for (wmWindow *win = wm->windows.first; win; win = win->next) {
 +                              const bScreen *screen = BKE_workspace_active_screen_get(win->workspace_hook);
 +                              for (ScrArea *area = screen->areabase.first; area; area = area->next) {
 +                                      if (ELEM(area->butspacetype, SPACE_TIME, SPACE_LOGIC)) {
 +                                              do_version_area_change_space_to_space_action(area, win->scene);
 +
 +                                              /* Don't forget to unset! */
 +                                              area->butspacetype = SPACE_EMPTY;
 +                                      }
 +                              }
 +                      }
 +              }
 +              if (scene != NULL) {
 +                      for (bScreen *screen = bmain->screen.first; screen; screen = screen->id.next) {
 +                              for (ScrArea *area = screen->areabase.first; area; area = area->next) {
 +                                      if (ELEM(area->butspacetype, SPACE_TIME, SPACE_LOGIC)) {
 +                                              /* Areas that were already handled won't be handled again */
 +                                              do_version_area_change_space_to_space_action(area, scene);
 +
 +                                              /* Don't forget to unset! */
 +                                              area->butspacetype = SPACE_EMPTY;
 +                                      }
 +                              }
 +                      }
 +              }
 +      }
 +
 +#ifdef USE_COLLECTION_COMPAT_28
 +      if (use_collection_compat_28 && !MAIN_VERSION_ATLEAST(bmain, 280, 14)) {
 +              for (Collection *group = bmain->collection.first; group; group = group->id.next) {
 +                      do_version_group_collection_to_collection(bmain, group);
 +              }
 +
 +              for (Scene *scene = bmain->scene.first; scene; scene = scene->id.next) {
 +                      do_version_scene_collection_to_collection(bmain, scene);
 +              }
 +      }
 +#endif
 +
 +      /* Update Curve object Shape Key data layout to include the Radius property */
 +      if (!MAIN_VERSION_ATLEAST(bmain, 280, 23)) {
 +              for (Curve *cu = bmain->curve.first; cu; cu = cu->id.next) {
 +                      if (!cu->key || cu->key->elemsize != sizeof(float[4]))
 +                              continue;
 +
 +                      cu->key->elemstr[0] = 3; /*KEYELEM_ELEM_SIZE_CURVE*/
 +                      cu->key->elemsize = sizeof(float[3]);
 +
 +                      int new_count = BKE_keyblock_curve_element_count(&cu->nurb);
 +
 +                      for (KeyBlock *block = cu->key->block.first; block; block = block->next) {
 +                              int old_count = block->totelem;
 +                              void *old_data = block->data;
 +
 +                              if (!old_data || old_count <= 0)
 +                                      continue;
 +
 +                              block->totelem = new_count;
 +                              block->data = MEM_callocN(sizeof(float[3]) * new_count, __func__);
 +
 +                              float *oldptr = old_data;
 +                              float (*newptr)[3] = block->data;
 +
 +                              for (Nurb *nu = cu->nurb.first; nu; nu = nu->next) {
 +                                      if (nu->bezt) {
 +                                              BezTriple *bezt = nu->bezt;
 +
 +                                              for (int a = 0; a < nu->pntsu; a++, bezt++) {
 +                                                      if ((old_count -= 3) < 0) {
 +                                                              memcpy(newptr, bezt->vec, sizeof(float[3][3]));
 +                                                              newptr[3][0] = bezt->alfa;
 +                                                      }
 +                                                      else {
 +                                                              memcpy(newptr, oldptr, sizeof(float[3][4]));
 +                                                      }
 +
 +                                                      newptr[3][1] = bezt->radius;
 +
 +                                                      oldptr += 3 * 4;
 +                                                      newptr += 4; /*KEYELEM_ELEM_LEN_BEZTRIPLE*/
 +                                              }
 +                                      }
 +                                      else if (nu->bp) {
 +                                              BPoint *bp = nu->bp;
 +
 +                                              for (int a = 0; a < nu->pntsu * nu->pntsv; a++, bp++) {
 +                                                      if (--old_count < 0) {
 +                                                              copy_v3_v3(newptr[0], bp->vec);
 +                                                              newptr[1][0] = bp->alfa;
 +                                                      }
 +                                                      else {
 +                                                              memcpy(newptr, oldptr, sizeof(float[4]));
 +                                                      }
 +
 +                                                      newptr[1][1] = bp->radius;
 +
 +                                                      oldptr += 4;
 +                                                      newptr += 2; /*KEYELEM_ELEM_LEN_BPOINT*/
 +                                              }
 +                                      }
 +                              }
 +
 +                              MEM_freeN(old_data);
 +                      }
 +              }
 +      }
 +
 +      /* Move B-Bone custom handle settings from bPoseChannel to Bone. */
 +      if (!MAIN_VERSION_ATLEAST(bmain, 280, 25)) {
 +              for (Object *ob = bmain->object.first; ob; ob = ob->id.next) {
 +                      bArmature *arm = ob->data;
 +
 +                      /* If it is an armature from the same file. */
 +                      if (ob->pose && arm && arm->id.lib == ob->id.lib) {
 +                              bool rebuild = false;
 +
 +                              for (bPoseChannel *pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) {
 +                                      /* If the 2.7 flag is enabled, processing is needed. */
 +                                      if (pchan->bone && (pchan->bboneflag & PCHAN_BBONE_CUSTOM_HANDLES)) {
 +                                              /* If the settings in the Bone are not set, copy. */
 +                                              if (pchan->bone->bbone_prev_type == BBONE_HANDLE_AUTO &&
 +                                                  pchan->bone->bbone_next_type == BBONE_HANDLE_AUTO &&
 +                                                  pchan->bone->bbone_prev == NULL && pchan->bone->bbone_next == NULL)
 +                                              {
 +                                                      pchan->bone->bbone_prev_type = (pchan->bboneflag & PCHAN_BBONE_CUSTOM_START_REL) ? BBONE_HANDLE_RELATIVE : BBONE_HANDLE_ABSOLUTE;
 +                                                      pchan->bone->bbone_next_type = (pchan->bboneflag & PCHAN_BBONE_CUSTOM_END_REL) ? BBONE_HANDLE_RELATIVE : BBONE_HANDLE_ABSOLUTE;
 +
 +                                                      if (pchan->bbone_prev) {
 +                                                              pchan->bone->bbone_prev = pchan->bbone_prev->bone;
 +                                                      }
 +                                                      if (pchan->bbone_next) {
 +                                                              pchan->bone->bbone_next = pchan->bbone_next->bone;
 +                                                      }
 +                                              }
 +
 +                                              rebuild = true;
 +                                              pchan->bboneflag = 0;
 +                                      }
 +                              }
 +
 +                              /* Tag pose rebuild for all objects that use this armature. */
 +                              if (rebuild) {
 +                                      for (Object *ob2 = bmain->object.first; ob2; ob2 = ob2->id.next) {
 +                                              if (ob2->pose && ob2->data == arm) {
 +                                                      ob2->pose->flag |= POSE_RECALC;
 +                                              }
 +                                      }
 +                              }
 +                      }
 +              }
 +      }
 +
 +      if (!MAIN_VERSION_ATLEAST(bmain, 280, 30)) {
 +              for (Brush *brush = bmain->brush.first; brush; brush = brush->id.next) {
 +                      if (brush->gpencil_settings != NULL) {
 +                              brush->gpencil_tool = brush->gpencil_settings->brush_type;
 +                      }
 +              }
 +              BKE_paint_toolslots_init_from_main(bmain);
 +      }
 +
 +      if (!MAIN_VERSION_ATLEAST(bmain, 280, 38)) {
 +              /* Ensure we get valid rigidbody object/constraint data in relevant collections' objects. */
 +              for (Scene *scene = bmain->scene.first; scene; scene = scene->id.next) {
 +                      RigidBodyWorld *rbw = scene->rigidbody_world;
 +
 +                      if (rbw == NULL) {
 +                              continue;
 +                      }
 +
 +                      BKE_rigidbody_objects_collection_validate(scene, rbw);
 +                      BKE_rigidbody_constraints_collection_validate(scene, rbw);
 +              }
 +      }
 +}
 +
 +/* NOTE: this version patch is intended for versions < 2.52.2, but was initially introduced in 2.27 already.
 + *       But in 2.79 another case generating non-unique names was discovered (see T55668, involving Meta strips)... */
 +static void do_versions_seq_unique_name_all_strips(Scene *sce, ListBase *seqbasep)
 +{
 +      for (Sequence *seq = seqbasep->first; seq != NULL; seq = seq->next) {
 +              BKE_sequence_base_unique_name_recursive(&sce->ed->seqbase, seq);
 +              if (seq->seqbase.first != NULL) {
 +                      do_versions_seq_unique_name_all_strips(sce, &seq->seqbase);
 +              }
 +      }
 +}
 +
 +void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain)
 +{
 +      bool use_collection_compat_28 = true;
 +
 +      if (!MAIN_VERSION_ATLEAST(bmain, 280, 0)) {
 +              use_collection_compat_28 = false;
 +
 +              for (Scene *scene = bmain->scene.first; scene; scene = scene->id.next) {
 +                      scene->r.gauss = 1.5f;
 +              }
 +      }
 +
 +      if (!MAIN_VERSION_ATLEAST(bmain, 280, 1)) {
 +              if (!DNA_struct_elem_find(fd->filesdna, "Lamp", "float", "bleedexp")) {
 +                      for (Lamp *la = bmain->lamp.first; la; la = la->id.next) {
 +                              la->bleedexp = 2.5f;
 +                      }
 +              }
 +
 +              if (!DNA_struct_elem_find(fd->filesdna, "GPUDOFSettings", "float", "ratio")) {
 +                      for (Camera *ca = bmain->camera.first; ca; ca = ca->id.next) {
 +                              ca->gpu_dof.ratio = 1.0f;
 +                      }
 +              }
 +
 +              /* MTexPoly now removed. */
 +              if (DNA_struct_find(fd->filesdna, "MTexPoly")) {
 +                      const int cd_mtexpoly = 15;  /* CD_MTEXPOLY, deprecated */
 +                      for (Mesh *me = bmain->mesh.first; me; me = me->id.next) {
 +                              /* If we have UV's, so this file will have MTexPoly layers too! */
 +                              if (me->mloopuv != NULL) {
 +                                      CustomData_update_typemap(&me->pdata);
 +                                      CustomData_free_layers(&me->pdata, cd_mtexpoly, me->totpoly);
 +                                      BKE_mesh_update_customdata_pointers(me, false);
 +                              }
 +                      }
 +              }
 +      }
 +
 +      if (!MAIN_VERSION_ATLEAST(bmain, 280, 2)) {
 +              if (!DNA_struct_elem_find(fd->filesdna, "Lamp", "float", "cascade_max_dist")) {
 +                      for (Lamp *la = bmain->lamp.first; la; la = la->id.next) {
 +                              la->cascade_max_dist = 1000.0f;
 +                              la->cascade_count = 4;
 +                              la->cascade_exponent = 0.8f;
 +                              la->cascade_fade = 0.1f;
 +                      }
 +              }
 +
 +              if (!DNA_struct_elem_find(fd->filesdna, "Lamp", "float", "contact_dist")) {
 +                      for (Lamp *la = bmain->lamp.first; la; la = la->id.next) {
 +                              la->contact_dist = 0.2f;
 +                              la->contact_bias = 0.03f;
 +                              la->contact_spread = 0.2f;
 +                              la->contact_thickness = 0.2f;
 +                      }
 +              }
 +
 +              if (!DNA_struct_elem_find(fd->filesdna, "LightProbe", "float", "vis_bias")) {
 +                      for (LightProbe *probe = bmain->lightprobe.first; probe; probe = probe->id.next) {
 +                              probe->vis_bias = 1.0f;
 +                              probe->vis_blur = 0.2f;
 +                      }
 +              }
 +
 +              typedef enum eNTreeDoVersionErrors {
 +                      NTREE_DOVERSION_NO_ERROR = 0,
 +                      NTREE_DOVERSION_NEED_OUTPUT = (1 << 0),
 +                      NTREE_DOVERSION_TRANSPARENCY_EMISSION = (1 << 1),
 +              } eNTreeDoVersionErrors;
 +
 +              /* Eevee shader nodes renamed because of the output node system.
 +               * Note that a new output node is not being added here, because it would be overkill
 +               * to handle this case in lib_verify_nodetree.
 +               *
 +               * Also, metallic node is now unified into the principled node. */
 +              eNTreeDoVersionErrors error = NTREE_DOVERSION_NO_ERROR;
 +
 +              FOREACH_NODETREE_BEGIN(bmain, ntree, id) {
 +                      if (ntree->type == NTREE_SHADER) {
 +                              for (bNode *node = ntree->nodes.first; node; node = node->next) {
 +                                      if (node->type == 194 /* SH_NODE_EEVEE_METALLIC */ &&
 +                                          STREQ(node->idname, "ShaderNodeOutputMetallic"))
 +                                      {
 +                                              BLI_strncpy(node->idname, "ShaderNodeEeveeMetallic", sizeof(node->idname));
 +                                              error |= NTREE_DOVERSION_NEED_OUTPUT;
 +                                      }
 +
 +                                      else if (node->type == SH_NODE_EEVEE_SPECULAR && STREQ(node->idname, "ShaderNodeOutputSpecular")) {
 +                                              BLI_strncpy(node->idname, "ShaderNodeEeveeSpecular", sizeof(node->idname));
 +                                              error |= NTREE_DOVERSION_NEED_OUTPUT;
 +                                      }
 +
 +                                      else if (node->type == 196 /* SH_NODE_OUTPUT_EEVEE_MATERIAL */ &&
 +                                               STREQ(node->idname, "ShaderNodeOutputEeveeMaterial"))
 +                                      {
 +                                              node->type = SH_NODE_OUTPUT_MATERIAL;
 +                                              BLI_strncpy(node->idname, "ShaderNodeOutputMaterial", sizeof(node->idname));
 +                                      }
 +
 +                                      else if (node->type == 194 /* SH_NODE_EEVEE_METALLIC */ &&
 +                                               STREQ(node->idname, "ShaderNodeEeveeMetallic"))
 +                                      {
 +                                              node->type = SH_NODE_BSDF_PRINCIPLED;
 +                                              BLI_strncpy(node->idname, "ShaderNodeBsdfPrincipled", sizeof(node->idname));
 +                                              node->custom1 = SHD_GLOSSY_MULTI_GGX;
 +                                              error |= NTREE_DOVERSION_TRANSPARENCY_EMISSION;
 +                                      }
 +                              }
 +                      }
 +              } FOREACH_NODETREE_END;
 +
 +              if (error & NTREE_DOVERSION_NEED_OUTPUT) {
 +                      BKE_report(fd->reports, RPT_ERROR, "Eevee material conversion problem. Error in console");
 +                      printf("You need to connect Principled and Eevee Specular shader nodes to new material output nodes.\n");
 +              }
 +
 +              if (error & NTREE_DOVERSION_TRANSPARENCY_EMISSION) {
 +                      BKE_report(fd->reports, RPT_ERROR, "Eevee material conversion problem. Error in console");
 +                      printf("You need to combine transparency and emission shaders to the converted Principled shader nodes.\n");
 +              }
 +
 +#ifdef USE_COLLECTION_COMPAT_28
 +              if (use_collection_compat_28 &&
 +                  (DNA_struct_elem_find(fd->filesdna, "ViewLayer", "FreestyleConfig", "freestyle_config") == false) &&
 +                  DNA_struct_elem_find(fd->filesdna, "Scene", "ListBase", "view_layers"))
 +              {
 +                      for (Scene *scene = bmain->scene.first; scene; scene = scene->id.next) {
 +                              ViewLayer *view_layer;
 +                              for (view_layer = scene->view_layers.first; view_layer; view_layer = view_layer->next) {
 +                                      view_layer->flag |= VIEW_LAYER_FREESTYLE;
 +                                      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);
 +                              }
 +                      }
 +              }
 +#endif
 +
 +              {
 +                      /* Grease pencil sculpt and paint cursors */
 +                      if (!DNA_struct_elem_find(fd->filesdna, "GP_Sculpt_Settings", "int", "weighttype")) {
 +                              for (Scene *scene = bmain->scene.first; scene; scene = scene->id.next) {
 +                                      /* sculpt brushes */
 +                                      GP_Sculpt_Settings *gset = &scene->toolsettings->gp_sculpt;
 +                                      if (gset) {
 +                                              gset->weighttype = GP_SCULPT_TYPE_WEIGHT;
 +                                      }
 +                              }
 +                      }
 +
 +                      {
 +                              float curcolor_add[3], curcolor_sub[3];
 +                              ARRAY_SET_ITEMS(curcolor_add, 1.0f, 0.6f, 0.6f);
 +                              ARRAY_SET_ITEMS(curcolor_sub, 0.6f, 0.6f, 1.0f);
 +                              GP_Sculpt_Data *gp_brush;
 +
 +                              for (Scene *scene = bmain->scene.first; scene; scene = scene->id.next) {
 +                                      ToolSettings *ts = scene->toolsettings;
 +                                      /* sculpt brushes */
 +                                      GP_Sculpt_Settings *gset = &ts->gp_sculpt;
 +                                      for (int i = 0; i < GP_SCULPT_TYPE_MAX; ++i) {
 +                                              gp_brush = &gset->brush[i];
 +                                              gp_brush->flag |= GP_SCULPT_FLAG_ENABLE_CURSOR;
 +                                              copy_v3_v3(gp_brush->curcolor_add, curcolor_add);
 +                                              copy_v3_v3(gp_brush->curcolor_sub, curcolor_sub);
 +                                      }
 +                              }
 +                      }
 +
 +                      /* Init grease pencil edit line color */
 +                      if (!DNA_struct_elem_find(fd->filesdna, "bGPdata", "float", "line_color[4]")) {
 +                              for (bGPdata *gpd = bmain->gpencil.first; gpd; gpd = gpd->id.next) {
 +                                      ARRAY_SET_ITEMS(gpd->line_color, 0.6f, 0.6f, 0.6f, 0.5f);
 +                              }
 +                      }
 +
 +                      /* Init grease pencil pixel size factor */
 +                      if (!DNA_struct_elem_find(fd->filesdna, "bGPdata", "int", "pixfactor")) {
 +                              for (bGPdata *gpd = bmain->gpencil.first; gpd; gpd = gpd->id.next) {
 +                                      gpd->pixfactor = GP_DEFAULT_PIX_FACTOR;
 +                              }
 +                      }
 +
 +                      /* Grease pencil multiframe falloff curve */
 +                      if (!DNA_struct_elem_find(fd->filesdna, "GP_Sculpt_Settings", "CurveMapping", "cur_falloff")) {
 +                              for (Scene *scene = bmain->scene.first; scene; scene = scene->id.next) {
 +                                      /* sculpt brushes */
 +                                      GP_Sculpt_Settings *gset = &scene->toolsettings->gp_sculpt;
 +                                      if ((gset) && (gset->cur_falloff == NULL)) {
 +                                              gset->cur_falloff = curvemapping_add(1, 0.0f, 0.0f, 1.0f, 1.0f);
 +                                              curvemapping_initialize(gset->cur_falloff);
 +                                              curvemap_reset(gset->cur_falloff->cm,
 +                                                             &gset->cur_falloff->clipr,
 +                                                             CURVE_PRESET_GAUSS,
 +                                                             CURVEMAP_SLOPE_POSITIVE);
 +                                      }
 +                              }
 +                      }
 +              }
 +      }
 +
 +#ifdef USE_COLLECTION_COMPAT_28
 +      if (use_collection_compat_28 && !MAIN_VERSION_ATLEAST(bmain, 280, 3)) {
 +              for (Scene *scene = bmain->scene.first; scene; scene = scene->id.next) {
 +                      ViewLayer *view_layer;
 +                      for (view_layer = scene->view_layers.first; view_layer; view_layer = view_layer->next) {
 +                              do_version_view_layer_visibility(view_layer);
 +                      }
 +              }
 +
 +              for (Collection *group = bmain->collection.first; group; group = group->id.next) {
 +                      if (group->view_layer != NULL) {
 +                              do_version_view_layer_visibility(group->view_layer);
 +                      }
 +              }
 +      }
 +#endif
 +
 +      if (!MAIN_VERSION_ATLEAST(bmain, 280, 3)) {
 +              /* init grease pencil grids and paper */
 +              if (!DNA_struct_elem_find(fd->filesdna, "gp_paper_opacity", "float", "gpencil_paper_color[3]")) {
 +                      for (bScreen *screen = bmain->screen.first; screen; screen = screen->id.next) {
 +                              for (ScrArea *area = screen->areabase.first; area; area = area->next) {
 +                                      for (SpaceLink *sl = area->spacedata.first; sl; sl = sl->next) {
 +                                              if (sl->spacetype == SPACE_VIEW3D) {
 +                                                      View3D *v3d = (View3D *)sl;
 +                                                      v3d->overlay.gpencil_paper_opacity = 0.5f;
 +                                                      v3d->overlay.gpencil_grid_opacity = 0.9f;
 +                                              }
 +                                      }
 +                              }
 +                      }
 +              }
 +      }
 +
 +      if (!MAIN_VERSION_ATLEAST(bmain, 280, 6)) {
 +              if (DNA_struct_elem_find(fd->filesdna, "SpaceOops", "int", "filter") == false) {
 +                      bScreen *sc;
 +                      ScrArea *sa;
 +                      SpaceLink *sl;
 +
 +                      /* Update files using invalid (outdated) outlinevis Outliner values. */
 +                      for (sc = bmain->screen.first; sc; sc = sc->id.next) {
 +                              for (sa = sc->areabase.first; sa; sa = sa->next) {
 +                                      for (sl = sa->spacedata.first; sl; sl = sl->next) {
 +                                              if (sl->spacetype == SPACE_OUTLINER) {
 +                                                      SpaceOops *so = (SpaceOops *)sl;
 +
 +                                                      if (!ELEM(so->outlinevis,
 +                                                                SO_SCENES,
 +                                                                SO_LIBRARIES,
 +                                                                SO_SEQUENCE,
 +                                                                SO_DATA_API,
 +                                                                SO_ID_ORPHANS))
 +                                                      {
 +                                                              so->outlinevis = SO_VIEW_LAYER;
 +                                                      }
 +                                              }
 +                                      }
 +                              }
 +                      }
 +              }
 +
 +              if (!DNA_struct_elem_find(fd->filesdna, "LightProbe", "float", "intensity")) {
 +                      for (LightProbe *probe = bmain->lightprobe.first; probe; probe = probe->id.next) {
 +                              probe->intensity = 1.0f;
 +                      }
 +              }
 +
 +              for (Object *ob = bmain->object.first; ob; ob = ob->id.next) {
 +                      bConstraint *con, *con_next;
 +                      con = ob->constraints.first;
 +                      while (con) {
 +                              con_next = con->next;
 +                              if (con->type == 17) { /* CONSTRAINT_TYPE_RIGIDBODYJOINT */
 +                                      BLI_remlink(&ob->constraints, con);
 +                                      BKE_constraint_free_data(con);
 +                                      MEM_freeN(con);
 +                              }
 +                              con = con_next;
 +                      }
 +              }
 +
 +              if (!DNA_struct_elem_find(fd->filesdna, "Scene", "int", "orientation_index_custom")) {
 +                      for (Scene *scene = bmain->scene.first; scene; scene = scene->id.next) {
 +                              scene->orientation_index_custom = -1;
 +                      }
 +              }
 +
 +              for (bScreen *sc = bmain->screen.first; sc; sc = sc->id.next) {
 +                      for (ScrArea *sa = sc->areabase.first; sa; sa = sa->next) {
 +                              for (SpaceLink *sl = sa->spacedata.first; sl; sl = sl->next) {
 +                                      if (sl->spacetype == SPACE_VIEW3D) {
 +                                              View3D *v3d = (View3D *)sl;
 +                                              v3d->shading.light = V3D_LIGHTING_STUDIO;
 +                                              v3d->shading.flag |= V3D_SHADING_OBJECT_OUTLINE;
 +
 +                                              /* Assume (demo) files written with 2.8 want to show
 +                                               * Eevee renders in the viewport. */
 +                                              if (MAIN_VERSION_ATLEAST(bmain, 280, 0)) {
 +                                                      v3d->drawtype = OB_MATERIAL;
 +                                              }
 +                                      }
 +                              }
 +                      }
 +              }
 +      }
 +
 +      if (!MAIN_VERSION_ATLEAST(bmain, 280, 7)) {
 +              /* Render engine storage moved elsewhere and back during 2.8
 +               * development, we assume any files saved in 2.8 had Eevee set
 +               * as scene render engine. */
 +              if (MAIN_VERSION_ATLEAST(bmain, 280, 0)) {
 +                      for (Scene *scene = bmain->scene.first; scene; scene = scene->id.next) {
 +                              BLI_strncpy(scene->r.engine, RE_engine_id_BLENDER_EEVEE, sizeof(scene->r.engine));
 +                      }
 +              }
 +      }
 +
 +      if (!MAIN_VERSION_ATLEAST(bmain, 280, 8)) {
 +              /* Blender Internal removal */
 +              for (Scene *scene = bmain->scene.first; scene; scene = scene->id.next) {
 +                      if (STREQ(scene->r.engine, "BLENDER_RENDER") ||
 +                          STREQ(scene->r.engine, "BLENDER_GAME"))
 +                      {
 +                              BLI_strncpy(scene->r.engine, RE_engine_id_BLENDER_EEVEE, sizeof(scene->r.engine));
 +                      }
 +
 +                      scene->r.bake_mode = 0;
 +              }
 +
 +              for (Tex *tex = bmain->tex.first; tex; tex = tex->id.next) {
 +                      /* Removed envmap, pointdensity, voxeldata, ocean textures. */
 +                      if (ELEM(tex->type, 10, 14, 15, 16)) {
 +                              tex->type = 0;
 +                      }
 +              }
 +
 +      }
 +
 +      if (!MAIN_VERSION_ATLEAST(bmain, 280, 11)) {
 +
 +              /* Remove info editor, but only if at the top of the window. */
 +              for (bScreen *screen = bmain->screen.first; screen; screen = screen->id.next) {
 +                      /* Calculate window width/height from screen vertices */
 +                      int win_width = 0, win_height = 0;
 +                      for (ScrVert *vert = screen->vertbase.first; vert; vert = vert->next) {
 +                              win_width  = MAX2(win_width, vert->vec.x);
 +                              win_height = MAX2(win_height, vert->vec.y);
 +                      }
 +
 +                      for (ScrArea *area = screen->areabase.first, *area_next; area; area = area_next) {
 +                              area_next = area->next;
 +
 +                              if (area->spacetype == SPACE_INFO) {
 +                                      if ((area->v2->vec.y == win_height) && (area->v1->vec.x == 0) && (area->v4->vec.x == win_width)) {
 +                                              BKE_screen_area_free(area);
 +
 +                                              BLI_remlink(&screen->areabase, area);
 +
 +                                              BKE_screen_remove_double_scredges(screen);
 +                                              BKE_screen_remove_unused_scredges(screen);
 +                                              BKE_screen_remove_unused_scrverts(screen);
 +
 +                                              MEM_freeN(area);
 +                                      }
 +                              }
 +                              /* AREA_TEMP_INFO is deprecated from now on, it should only be set for info areas
 +                               * which are deleted above, so don't need to unset it. Its slot/bit can be reused */
 +                      }
 +              }
 +      }
 +
 +      if (!MAIN_VERSION_ATLEAST(bmain, 280, 11)) {
 +              for (Lamp *lamp = bmain->lamp.first; lamp; lamp = lamp->id.next) {
 +                      if (lamp->mode & (1 << 13)) { /* LA_SHAD_RAY */
 +                              lamp->mode |= LA_SHADOW;
 +                              lamp->mode &= ~(1 << 13);
 +                      }
 +              }
 +      }
 +
 +      if (!MAIN_VERSION_ATLEAST(bmain, 280, 12)) {
 +              /* Remove tool property regions. */
 +              for (bScreen *screen = bmain->screen.first; screen; screen = screen->id.next) {
 +                      for (ScrArea *sa = screen->areabase.first; sa; sa = sa->next) {
 +                              for (SpaceLink *sl = sa->spacedata.first; sl; sl = sl->next) {
 +                                      if (ELEM(sl->spacetype, SPACE_VIEW3D, SPACE_CLIP)) {
 +                                              ListBase *regionbase = (sl == sa->spacedata.first) ? &sa->regionbase : &sl->regionbase;
 +
 +                                              for (ARegion *region = regionbase->first, *region_next; region; region = region_next) {
 +                                                      region_next = region->next;
 +
 +                                                      if (region->regiontype == RGN_TYPE_TOOL_PROPS) {
 +                                                              BKE_area_region_free(NULL, region);
 +                                                              BLI_freelinkN(regionbase, region);
 +                                                      }
 +                                              }
 +                                      }
 +                              }
 +                      }
 +              }
 +      }
 +
 +      if (!MAIN_VERSION_ATLEAST(bmain, 280, 13)) {
 +              /* Initialize specular factor. */
 +              if (!DNA_struct_elem_find(fd->filesdna, "Lamp", "float", "spec_fac")) {
 +                      for (Lamp *la = bmain->lamp.first; la; la = la->id.next) {
 +                              la->spec_fac = 1.0f;
 +                      }
 +              }
 +
 +              /* Initialize new view3D options. */
 +              for (bScreen *screen = bmain->screen.first; screen; screen = screen->id.next) {
 +                      for (ScrArea *sa = screen->areabase.first; sa; sa = sa->next) {
 +                              for (SpaceLink *sl = sa->spacedata.first; sl; sl = sl->next) {
 +                                      if (sl->spacetype == SPACE_VIEW3D) {
 +                                              View3D *v3d = (View3D *)sl;
 +                                              v3d->shading.light = V3D_LIGHTING_STUDIO;
 +                                              v3d->shading.color_type = V3D_SHADING_MATERIAL_COLOR;
 +                                              copy_v3_fl(v3d->shading.single_color, 0.8f);
 +                                              v3d->shading.shadow_intensity = 0.5;
 +
 +                                              v3d->overlay.backwire_opacity = 0.5f;
 +                                              v3d->overlay.normals_length = 0.1f;
 +                                              v3d->overlay.flag = 0;
 +                                      }
 +                              }
 +                      }
 +              }
 +
 +              if (!DNA_struct_find(fd->filesdna, "View3DCursor")) {
 +                      for (Scene *scene = bmain->scene.first; scene; scene = scene->id.next) {
 +                              unit_qt(scene->cursor.rotation);
 +                      }
 +              }
 +      }
 +
 +      if (!MAIN_VERSION_ATLEAST(bmain, 280, 14)) {
 +              if (!DNA_struct_elem_find(fd->filesdna, "Scene", "SceneDisplay", "display")) {
 +                      /* Initialize new scene.SceneDisplay */
 +                      for (Scene *scene = bmain->scene.first; scene; scene = scene->id.next) {
 +                              copy_v3_v3(scene->display.light_direction, (float[3]){-M_SQRT1_3, -M_SQRT1_3, M_SQRT1_3});
 +                      }
 +              }
 +              if (!DNA_struct_elem_find(fd->filesdna, "SceneDisplay", "float", "shadow_shift")) {
 +                      for (Scene *scene = bmain->scene.first; scene; scene = scene->id.next) {
 +                              scene->display.shadow_shift = 0.1;
 +                      }
 +              }
 +
 +              if (!DNA_struct_elem_find(fd->filesdna, "Object", "ObjectDisplay", "display")) {
 +                      /* Initialize new object.ObjectDisplay */
 +                      for (Object *ob = bmain->object.first; ob; ob = ob->id.next) {
 +                              ob->display.flag = OB_SHOW_SHADOW;
 +                      }
 +              }
 +
 +              if (!DNA_struct_elem_find(fd->filesdna, "ToolSettings", "char", "transform_pivot_point")) {
 +                      for (Scene *scene = bmain->scene.first; scene; scene = scene->id.next) {
++                              scene->toolsettings->transform_pivot_point = V3D_AROUND_CENTER_MEDIAN;
 +                      }
 +              }
 +
 +              if (!DNA_struct_find(fd->filesdna, "SceneEEVEE")) {
 +                      for (Scene *scene = bmain->scene.first; scene; scene = scene->id.next) {
 +                              /* First set the default for all the properties. */
 +
 +                              scene->eevee.gi_diffuse_bounces = 3;
 +                              scene->eevee.gi_cubemap_resolution = 512;
 +                              scene->eevee.gi_visibility_resolution = 32;
 +
 +                              scene->eevee.taa_samples = 16;
 +                              scene->eevee.taa_render_samples = 64;
 +
 +                              scene->eevee.sss_samples = 7;
 +                              scene->eevee.sss_jitter_threshold = 0.3f;
 +
 +                              scene->eevee.ssr_quality = 0.25f;
 +                              scene->eevee.ssr_max_roughness = 0.5f;
 +                              scene->eevee.ssr_thickness = 0.2f;
 +                              scene->eevee.ssr_border_fade = 0.075f;
 +                              scene->eevee.ssr_firefly_fac = 10.0f;
 +
 +                              scene->eevee.volumetric_start = 0.1f;
 +                              scene->eevee.volumetric_end = 100.0f;
 +                              scene->eevee.volumetric_tile_size = 8;
 +                              scene->eevee.volumetric_samples = 64;
 +                              scene->eevee.volumetric_sample_distribution = 0.8f;
 +                              scene->eevee.volumetric_light_clamp = 0.0f;
 +                              scene->eevee.volumetric_shadow_samples = 16;
 +
 +                              scene->eevee.gtao_distance = 0.2f;
 +                              scene->eevee.gtao_factor = 1.0f;
 +                              scene->eevee.gtao_quality = 0.25f;
 +
 +                              scene->eevee.bokeh_max_size = 100.0f;
 +                              scene->eevee.bokeh_threshold = 1.0f;
 +
 +                              copy_v3_fl(scene->eevee.bloom_color, 1.0f);
 +                              scene->eevee.bloom_threshold = 0.8f;
 +                              scene->eevee.bloom_knee = 0.5f;
 +                              scene->eevee.bloom_intensity = 0.8f;
 +                              scene->eevee.bloom_radius = 6.5f;
 +                              scene->eevee.bloom_clamp = 1.0f;
 +
 +                              scene->eevee.motion_blur_samples = 8;
 +                              scene->eevee.motion_blur_shutter = 1.0f;
 +
 +                              scene->eevee.shadow_method = SHADOW_ESM;
 +                              scene->eevee.shadow_cube_size = 512;
 +                              scene->eevee.shadow_cascade_size = 1024;
 +
 +                              scene->eevee.flag =
 +                                      SCE_EEVEE_VOLUMETRIC_LIGHTS |
 +                                      SCE_EEVEE_GTAO_BENT_NORMALS |
 +                                      SCE_EEVEE_GTAO_BOUNCE |
 +                                      SCE_EEVEE_TAA_REPROJECTION |
 +                                      SCE_EEVEE_SSR_HALF_RESOLUTION;
 +
 +
 +                              /* If the file is pre-2.80 move on. */
 +                              if (scene->layer_properties == NULL) {
 +                                      continue;
 +                              }
 +
 +                              /* Now we handle eventual properties that may be set in the file. */
 +#define EEVEE_GET_BOOL(_props, _name, _flag) \
 +                              { \
 +                                      IDProperty *_idprop = IDP_GetPropertyFromGroup(_props, #_name); \
 +                                      if (_idprop != NULL) { \
 +                                              const int _value = IDP_Int(_idprop); \
 +                                              if (_value) { \
 +                                                      scene->eevee.flag |= _flag; \
 +                                              } \
 +                                              else { \
 +                                                      scene->eevee.flag &= ~_flag; \
 +                                              } \
 +                                      } \
 +                              }
 +
 +#define EEVEE_GET_INT(_props, _name) \
 +                              { \
 +                                      IDProperty *_idprop = IDP_GetPropertyFromGroup(_props, #_name); \
 +                                      if (_idprop != NULL) { \
 +                                              scene->eevee._name = IDP_Int(_idprop); \
 +                                      } \
 +                              }
 +
 +#define EEVEE_GET_FLOAT(_props, _name) \
 +                              { \
 +                                      IDProperty *_idprop = IDP_GetPropertyFromGroup(_props, #_name); \
 +                                      if (_idprop != NULL) { \
 +                                              scene->eevee._name = IDP_Float(_idprop); \
 +                                      } \
 +                              }
 +
 +#define EEVEE_GET_FLOAT_ARRAY(_props, _name, _length) \
 +                              { \
 +                                      IDProperty *_idprop = IDP_GetPropertyFromGroup(_props, #_name); \
 +                                      if (_idprop != NULL) { \
 +                                              const float *_values = IDP_Array(_idprop); \
 +                                              for (int _i = 0; _i < _length; _i++) { \
 +                                                      scene->eevee._name [_i] = _values[_i]; \
 +                                              } \
 +                                      } \
 +                              }
 +
 +                              IDProperty *props = IDP_GetPropertyFromGroup(scene->layer_properties, RE_engine_id_BLENDER_EEVEE);
 +                              EEVEE_GET_BOOL(props, volumetric_enable, SCE_EEVEE_VOLUMETRIC_ENABLED);
 +                              EEVEE_GET_BOOL(props, volumetric_lights, SCE_EEVEE_VOLUMETRIC_LIGHTS);
 +                              EEVEE_GET_BOOL(props, volumetric_shadows, SCE_EEVEE_VOLUMETRIC_SHADOWS);
 +                              EEVEE_GET_BOOL(props, gtao_enable, SCE_EEVEE_GTAO_ENABLED);
 +                              EEVEE_GET_BOOL(props, gtao_use_bent_normals, SCE_EEVEE_GTAO_BENT_NORMALS);
 +                              EEVEE_GET_BOOL(props, gtao_bounce, SCE_EEVEE_GTAO_BOUNCE);
 +                              EEVEE_GET_BOOL(props, dof_enable, SCE_EEVEE_DOF_ENABLED);
 +                              EEVEE_GET_BOOL(props, bloom_enable, SCE_EEVEE_BLOOM_ENABLED);
 +                              EEVEE_GET_BOOL(props, motion_blur_enable, SCE_EEVEE_MOTION_BLUR_ENABLED);
 +                              EEVEE_GET_BOOL(props, shadow_high_bitdepth, SCE_EEVEE_SHADOW_HIGH_BITDEPTH);
 +                              EEVEE_GET_BOOL(props, taa_reprojection, SCE_EEVEE_TAA_REPROJECTION);
 +                              EEVEE_GET_BOOL(props, sss_enable, SCE_EEVEE_SSS_ENABLED);
 +                              EEVEE_GET_BOOL(props, sss_separate_albedo, SCE_EEVEE_SSS_SEPARATE_ALBEDO);
 +                              EEVEE_GET_BOOL(props, ssr_enable, SCE_EEVEE_SSR_ENABLED);
 +                              EEVEE_GET_BOOL(props, ssr_refraction, SCE_EEVEE_SSR_REFRACTION);
 +                              EEVEE_GET_BOOL(props, ssr_halfres, SCE_EEVEE_SSR_HALF_RESOLUTION);
 +
 +                              EEVEE_GET_INT(props, gi_diffuse_bounces);
 +                              EEVEE_GET_INT(props, gi_diffuse_bounces);
 +                              EEVEE_GET_INT(props, gi_cubemap_resolution);
 +                              EEVEE_GET_INT(props, gi_visibility_resolution);
 +
 +                              EEVEE_GET_INT(props, taa_samples);
 +                              EEVEE_GET_INT(props, taa_render_samples);
 +
 +                              EEVEE_GET_INT(props, sss_samples);
 +                              EEVEE_GET_FLOAT(props, sss_jitter_threshold);
 +
 +                              EEVEE_GET_FLOAT(props, ssr_quality);
 +                              EEVEE_GET_FLOAT(props, ssr_max_roughness);
 +                              EEVEE_GET_FLOAT(props, ssr_thickness);
 +                              EEVEE_GET_FLOAT(props, ssr_border_fade);
 +                              EEVEE_GET_FLOAT(props, ssr_firefly_fac);
 +
 +                              EEVEE_GET_FLOAT(props, volumetric_start);
 +                              EEVEE_GET_FLOAT(props, volumetric_end);
 +                              EEVEE_GET_INT(props, volumetric_tile_size);
 +                              EEVEE_GET_INT(props, volumetric_samples);
 +                              EEVEE_GET_FLOAT(props, volumetric_sample_distribution);
 +                              EEVEE_GET_FLOAT(props, volumetric_light_clamp);
 +                              EEVEE_GET_INT(props, volumetric_shadow_samples);
 +
 +                              EEVEE_GET_FLOAT(props, gtao_distance);
 +                              EEVEE_GET_FLOAT(props, gtao_factor);
 +                              EEVEE_GET_FLOAT(props, gtao_quality);
 +
 +                              EEVEE_GET_FLOAT(props, bokeh_max_size);
 +                              EEVEE_GET_FLOAT(props, bokeh_threshold);
 +
 +                              EEVEE_GET_FLOAT_ARRAY(props, bloom_color, 3);
 +                              EEVEE_GET_FLOAT(props, bloom_threshold);
 +                              EEVEE_GET_FLOAT(props, bloom_knee);
 +                              EEVEE_GET_FLOAT(props, bloom_intensity);
 +                              EEVEE_GET_FLOAT(props, bloom_radius);
 +                              EEVEE_GET_FLOAT(props, bloom_clamp);
 +
 +                              EEVEE_GET_INT(props, motion_blur_samples);
 +                              EEVEE_GET_FLOAT(props, motion_blur_shutter);
 +
 +                              EEVEE_GET_INT(props, shadow_method);
 +                              EEVEE_GET_INT(props, shadow_cube_size);
 +                              EEVEE_GET_INT(props, shadow_cascade_size);
 +
 +                              /* Cleanup. */
 +                              IDP_FreeProperty(scene->layer_properties);
 +                              MEM_freeN(scene->layer_properties);
 +                              scene->layer_properties = NULL;
 +
 +#undef EEVEE_GET_FLOAT_ARRAY
 +#undef EEVEE_GET_FLOAT
 +#undef EEVEE_GET_INT
 +#undef EEVEE_GET_BOOL
 +                      }
 +              }
 +
 +
 +              if (!MAIN_VERSION_ATLEAST(bmain, 280, 15)) {
 +                      for (Scene *scene = bmain->scene.first; scene; scene = scene->id.next) {
 +                              scene->display.matcap_ssao_distance = 0.2f;
 +                              scene->display.matcap_ssao_attenuation = 1.0f;
 +                              scene->display.matcap_ssao_samples = 16;
 +                      }
 +
 +                      for (bScreen *screen = bmain->screen.first; screen; screen = screen->id.next) {
 +                              for (ScrArea *sa = screen->areabase.first; sa; sa = sa->next) {
 +                                      for (SpaceLink *sl = sa->spacedata.first; sl; sl = sl->next) {
 +                                              if (sl->spacetype == SPACE_OUTLINER) {
 +                                                      SpaceOops *soops = (SpaceOops *)sl;
 +                                                      soops->filter_id_type = ID_GR;
 +                                                      soops->outlinevis = SO_VIEW_LAYER;
 +                                              }
 +                                      }
 +                              }
 +                      }
 +
 +                      for (Scene *scene = bmain->scene.first; scene; scene = scene->id.next) {
 +                              switch (scene->toolsettings->snap_mode) {
 +                                      case 0: scene->toolsettings->snap_mode = SCE_SNAP_MODE_INCREMENT; break;
 +                                      case 1: scene->toolsettings->snap_mode = SCE_SNAP_MODE_VERTEX   ; break;
 +                                      case 2: scene->toolsettings->snap_mode = SCE_SNAP_MODE_EDGE     ; break;
 +                                      case 3: scene->toolsettings->snap_mode = SCE_SNAP_MODE_FACE     ; break;
 +                                      case 4: scene->toolsettings->snap_mode = SCE_SNAP_MODE_VOLUME   ; break;
 +                              }
 +                              switch (scene->toolsettings->snap_node_mode) {
 +                                      case 5: scene->toolsettings->snap_node_mode = SCE_SNAP_MODE_NODE_X; break;
 +                                      case 6: scene->toolsettings->snap_node_mode = SCE_SNAP_MODE_NODE_Y; break;
 +                                      case 7: scene->toolsettings->snap_node_mode = SCE_SNAP_MODE_NODE_X | SCE_SNAP_MODE_NODE_Y; break;
 +                                      case 8: scene->toolsettings->snap_node_mode = SCE_SNAP_MODE_GRID  ; break;
 +                              }
 +                              switch (scene->toolsettings->snap_uv_mode) {
 +                                      case 0: scene->toolsettings->snap_uv_mode = SCE_SNAP_MODE_INCREMENT; break;
 +                                      case 1: scene->toolsettings->snap_uv_mode = SCE_SNAP_MODE_VERTEX   ; break;
 +                              }
 +                      }
 +
 +                      ParticleSettings *part;
 +                      for (part = bmain->particle.first; part; part = part->id.next) {
 +                              part->shape_flag = PART_SHAPE_CLOSE_TIP;
 +                              part->shape = 0.0f;
 +                              part->rad_root = 1.0f;
 +                              part->rad_tip = 0.0f;
 +                              part->rad_scale = 0.01f;
 +                      }
 +              }
 +
 +      }
 +
 +      if (!MAIN_VERSION_ATLEAST(bmain, 280, 18)) {
 +              if (!DNA_struct_elem_find(fd->filesdna, "Material", "float", "roughness")) {
 +                      for (Material *mat = bmain->mat.first; mat; mat = mat->id.next) {
 +                              if (mat->use_nodes) {
 +                                      if (MAIN_VERSION_ATLEAST(bmain, 280, 0)) {
 +                                              mat->roughness = mat->gloss_mir;
 +                                      }
 +                                      else {
 +                                              mat->roughness = 0.25f;
 +                                      }
 +                              }
 +                              else {
 +                                      mat->roughness = 1.0f - mat->gloss_mir;
 +                              }
 +                              mat->metallic = mat->ray_mirror;
 +                      }
 +
 +                      for (bScreen *screen = bmain->screen.first; screen; screen = screen->id.next) {
 +                              for (ScrArea *sa = screen->areabase.first; sa; sa = sa->next) {
 +                                      for (SpaceLink *sl = sa->spacedata.first; sl; sl = sl->next) {
 +                                              if (sl->spacetype == SPACE_VIEW3D) {
 +                                                      View3D *v3d = (View3D *)sl;
 +                                                      v3d->shading.flag |= V3D_SHADING_SPECULAR_HIGHLIGHT;
 +                                              }
 +                                      }
 +                              }
 +                      }
 +              }
 +
 +              if (!DNA_struct_elem_find(fd->filesdna, "View3DShading", "float", "xray_alpha")) {
 +                      for (bScreen *screen = bmain->screen.first; screen; screen = screen->id.next) {
 +                              for (ScrArea *sa = screen->areabase.first; sa; sa = sa->next) {
 +                                      for (SpaceLink *sl = sa->spacedata.first; sl; sl = sl->next) {
 +                                              if (sl->spacetype == SPACE_VIEW3D) {
 +                                                      View3D *v3d = (View3D *)sl;
 +                                                      v3d->shading.xray_alpha = 0.5f;
 +                                              }
 +                                      }
 +                              }
 +                      }
 +              }
 +              if (!DNA_struct_elem_find(fd->filesdna, "View3DShading", "char", "matcap[256]")) {
 +                      StudioLight *default_matcap = BKE_studiolight_find_first(STUDIOLIGHT_TYPE_MATCAP);
 +                      /* when loading the internal file is loaded before the matcaps */
 +                      if (default_matcap) {
 +                              for (bScreen *screen = bmain->screen.first; screen; screen = screen->id.next) {
 +                                      for (ScrArea *sa = screen->areabase.first; sa; sa = sa->next) {
 +                                              for (SpaceLink *sl = sa->spacedata.first; sl; sl = sl->next) {
 +                                                      if (sl->spacetype == SPACE_VIEW3D) {
 +                                                              View3D *v3d = (View3D *)sl;
 +                                                              BLI_strncpy(v3d->shading.matcap, default_matcap->name, FILE_MAXFILE);
 +                                                      }
 +                                              }
 +                                      }
 +                              }
 +                      }
 +              }
 +              if (!DNA_struct_elem_find(fd->filesdna, "View3DOverlay", "float", "wireframe_threshold")) {
 +                      for (bScreen *screen = bmain->screen.first; screen; screen = screen->id.next) {
 +                              for (ScrArea *sa = screen->areabase.first; sa; sa = sa->next) {
 +                                      for (SpaceLink *sl = sa->spacedata.first; sl; sl = sl->next) {
 +                                              if (sl->spacetype == SPACE_VIEW3D) {
 +                                                      View3D *v3d = (View3D *)sl;
 +                                                      v3d->overlay.wireframe_threshold = 0.5f;
 +                                              }
 +                                      }
 +                              }
 +                      }
 +              }
 +              if (!DNA_struct_elem_find(fd->filesdna, "View3DShading", "float", "cavity_valley_factor")) {
 +                      for (bScreen *screen = bmain->screen.first; screen; screen = screen->id.next) {
 +                              for (ScrArea *sa = screen->areabase.first; sa; sa = sa->next) {
 +                                      for (SpaceLink *sl = sa->spacedata.first; sl; sl = sl->next) {
 +                                              if (sl->spacetype == SPACE_VIEW3D) {
 +                                                      View3D *v3d = (View3D *)sl;
 +                                                      v3d->shading.cavity_valley_factor = 1.0f;
 +                                                      v3d->shading.cavity_ridge_factor = 1.0f;
 +                                              }
 +                                      }
 +                              }
 +                      }
 +              }
 +              if (!DNA_struct_elem_find(fd->filesdna, "View3DOverlay", "float", "xray_alpha_bone")) {
 +                      for (bScreen *screen = bmain->screen.first; screen; screen = screen->id.next) {
 +                              for (ScrArea *sa = screen->areabase.first; sa; sa = sa->next) {
 +                                      for (SpaceLink *sl = sa->spacedata.first; sl; sl = sl->next) {
 +                                              if (sl->spacetype == SPACE_VIEW3D) {
 +                                                      View3D *v3d = (View3D *)sl;
 +                                                      v3d->overlay.xray_alpha_bone = 0.5f;
 +                                              }
 +                                      }
 +                              }
 +                      }
 +              }
 +      }
 +
 +      if (!MAIN_VERSION_ATLEAST(bmain, 280, 19)) {
 +              if (!DNA_struct_elem_find(fd->filesdna, "Image", "ListBase", "renderslot")) {
 +                      for (Image *ima = bmain->image.first; ima; ima = ima->id.next) {
 +                              if (ima->type == IMA_TYPE_R_RESULT) {
 +                                      for (int i = 0; i < 8; i++) {
 +                                              RenderSlot *slot = MEM_callocN(sizeof(RenderSlot), "Image Render Slot Init");
 +                                              BLI_snprintf(slot->name, sizeof(slot->name), "Slot %d", i + 1);
 +                                              BLI_addtail(&ima->renderslots, slot);
 +                                      }
 +                              }
 +                      }
 +              }
 +              if (!DNA_struct_elem_find(fd->filesdna, "SpaceAction", "char", "mode_prev")) {
 +                      for (bScreen *screen = bmain->screen.first; screen; screen = screen->id.next) {
 +                              for (ScrArea *sa = screen->areabase.first; sa; sa = sa->next) {
 +                                      for (SpaceLink *sl = sa->spacedata.first; sl; sl = sl->next) {
 +                                              if (sl->spacetype == SPACE_ACTION) {
 +                                                      SpaceAction *saction = (SpaceAction *)sl;
 +                                                      /* "Dopesheet" should be default here, unless it looks like the Action Editor was active instead */
 +                                                      if ((saction->mode_prev == 0) && (saction->action == NULL)) {
 +                                                              saction->mode_prev = SACTCONT_DOPESHEET;
 +                                                      }
 +                                              }
 +                                      }
 +                              }
 +                      }
 +              }
 +
 +              for (bScreen *screen = bmain->screen.first; screen; screen = screen->id.next) {
 +                      for (ScrArea *sa = screen->areabase.first; sa; sa = sa->next) {
 +                              for (SpaceLink *sl = sa->spacedata.first; sl; sl = sl->next) {
 +                                      if (sl->spacetype == SPACE_VIEW3D) {
 +                                              View3D *v3d = (View3D *)sl;
 +                                              if (v3d->drawtype == OB_TEXTURE) {
 +                                                      v3d->drawtype = OB_SOLID;
 +                                                      v3d->shading.light = V3D_LIGHTING_STUDIO;
 +                                                      v3d->shading.color_type = V3D_SHADING_TEXTURE_COLOR;
 +                                              }
 +                                      }
 +                              }
 +                      }
 +              }
 +
 +      }
 +
 +      if (!MAIN_VERSION_ATLEAST(bmain, 280, 21)) {
 +              for (Scene *sce = bmain->scene.first; sce != NULL; sce = sce->id.next) {
 +                      if (sce->ed != NULL && sce->ed->seqbase.first != NULL) {
 +                              do_versions_seq_unique_name_all_strips(sce, &sce->ed->seqbase);
 +                      }
 +              }
 +
 +              if (!DNA_struct_elem_find(fd->filesdna, "View3DOverlay", "float", "texture_paint_mode_opacity")) {
 +                      for (bScreen *screen = bmain->screen.first; screen; screen = screen->id.next) {
 +                              for (ScrArea *sa = screen->areabase.first; sa; sa = sa->next) {
 +                                      for (SpaceLink *sl = sa->spacedata.first; sl; sl = sl->next) {
 +                                              if (sl->spacetype == SPACE_VIEW3D) {
 +                                                      View3D *v3d = (View3D *)sl;
 +                                                      float alpha = v3d->flag2 & V3D_SHOW_MODE_SHADE_OVERRIDE ? 0.0f : 0.8f;
 +                                                      float alpha_full = v3d->flag2 & V3D_SHOW_MODE_SHADE_OVERRIDE ? 0.0f : 1.0f;
 +                                                      v3d->overlay.texture_paint_mode_opacity = alpha;
 +                                                      v3d->overlay.vertex_paint_mode_opacity = alpha;
 +                                                      v3d->overlay.weight_paint_mode_opacity = alpha_full;
 +                                              }
 +                                      }
 +                              }
 +                      }
 +              }
 +
 +              if (!DNA_struct_elem_find(fd->filesdna, "View3DShadeing", "char", "background_type")) {
 +                      for (bScreen *screen = bmain->screen.first; screen; screen = screen->id.next) {
 +                              for (ScrArea *sa = screen->areabase.first; sa; sa = sa->next) {
 +                                      for (SpaceLink *sl = sa->spacedata.first; sl; sl = sl->next) {
 +                                              if (sl->spacetype == SPACE_VIEW3D) {
 +                                                      View3D *v3d = (View3D *)sl;
 +                                                      copy_v3_fl(v3d->shading.background_color, 0.05f);
 +                                              }
 +                                      }
 +                              }
 +                      }
 +              }
 +
 +              if (!DNA_struct_elem_find(fd->filesdna, "SceneEEVEE", "float", "gi_cubemap_draw_size")) {
 +                      for (Scene *scene = bmain->scene.first; scene; scene = scene->id.next) {
 +                              scene->eevee.gi_irradiance_draw_size = 0.1f;
 +                              scene->eevee.gi_cubemap_draw_size = 0.3f;
 +                      }
 +              }
 +
 +              for (Scene *scene = bmain->scene.first; scene; scene = scene->id.next) {
 +                      if (scene->toolsettings->gizmo_flag == 0) {
 +                              scene->toolsettings->gizmo_flag = SCE_GIZMO_SHOW_TRANSLATE | SCE_GIZMO_SHOW_ROTATE | SCE_GIZMO_SHOW_SCALE;
 +                      }
 +              }
 +
 +              if (!DNA_struct_elem_find(fd->filesdna, "RigidBodyWorld", "RigidBodyWorld_Shared", "*shared")) {
 +                      for (Scene *scene = bmain->scene.first; scene; scene = scene->id.next) {
 +                              RigidBodyWorld *rbw = scene->rigidbody_world;
 +
 +                              if (rbw == NULL) {
 +                                      continue;
 +                              }
 +
 +                              if (rbw->shared == NULL) {
 +                                      rbw->shared = MEM_callocN(sizeof(*rbw->shared), "RigidBodyWorld_Shared");
 +                              }
 +
 +                              /* Move shared pointers from deprecated location to current location */
 +                              rbw->shared->pointcache = rbw->pointcache;
 +                              rbw->shared->ptcaches = rbw->ptcaches;
 +
 +                              rbw->pointcache = NULL;
 +                              BLI_listbase_clear(&rbw->ptcaches);
 +
 +                              if (rbw->shared->pointcache == NULL) {
 +                                      rbw->shared->pointcache = BKE_ptcache_add(&(rbw->shared->ptcaches));
 +                              }
 +
 +                      }
 +              }
 +
 +              if (!DNA_struct_elem_find(fd->filesdna, "SoftBody", "SoftBody_Shared", "*shared")) {
 +                      for (Object *ob = bmain->object.first; ob; ob = ob->id.next) {
 +                              SoftBody *sb = ob->soft;
 +                              if (sb == NULL) {
 +                                      continue;
 +                              }
 +                              if (sb->shared == NULL) {
 +                                      sb->shared = MEM_callocN(sizeof(*sb->shared), "SoftBody_Shared");
 +                              }
 +
 +                              /* Move shared pointers from deprecated location to current location */
 +                              sb->shared->pointcache = sb->pointcache;
 +                              sb->shared->ptcaches = sb->ptcaches;
 +
 +                              sb->pointcache = NULL;
 +                              BLI_listbase_clear(&sb->ptcaches);
 +                      }
 +              }
 +
 +              if (!DNA_struct_elem_find(fd->filesdna, "View3DShading", "short", "type")) {
 +                      for (bScreen *screen = bmain->screen.first; screen; screen = screen->id.next) {
 +                              for (ScrArea *sa = screen->areabase.first; sa; sa = sa->next) {
 +                                      for (SpaceLink *sl = sa->spacedata.first; sl; sl = sl->next) {
 +                                              if (sl->spacetype == SPACE_VIEW3D) {
 +                                                      View3D *v3d = (View3D *)sl;
 +                                                      if (v3d->drawtype == OB_RENDER) {
 +                                                              v3d->drawtype = OB_SOLID;
 +                                                      }
 +                                                      v3d->shading.type = v3d->drawtype;
 +                                                      v3d->shading.prev_type = OB_SOLID;
 +                                              }
 +                                      }
 +                              }
 +                      }
 +              }
 +
 +              if (!DNA_struct_elem_find(fd->filesdna, "SceneDisplay", "View3DShading", "shading")) {
 +                      for (Scene *scene = bmain->scene.first; scene; scene = scene->id.next) {
 +                              BKE_screen_view3d_shading_init(&scene->display.shading);
 +                      }
 +              }
 +              /* initialize grease pencil view data */
 +              if (!DNA_struct_elem_find(fd->filesdna, "SpaceView3D", "float", "vertex_opacity")) {
 +                      for (bScreen *sc = bmain->screen.first; sc; sc = sc->id.next) {
 +                              for (ScrArea *sa = sc->areabase.first; sa; sa = sa->next) {
 +                                      for (SpaceLink *sl = sa->spacedata.first; sl; sl = sl->next) {
 +                                              if (sl->spacetype == SPACE_VIEW3D) {
 +                                                      View3D *v3d = (View3D *)sl;
 +                                                      v3d->vertex_opacity = 1.0f;
 +                                                      v3d->gp_flag |= V3D_GP_SHOW_EDIT_LINES;
 +                                              }
 +                                      }
 +                              }
 +                      }
 +              }
 +
 +      }
 +
 +      if (!MAIN_VERSION_ATLEAST(bmain, 280, 22)) {
 +              if (!DNA_struct_elem_find(fd->filesdna, "ToolSettings", "char", "annotate_v3d_align")) {
 +                      for (Scene *scene = bmain->scene.first; scene; scene = scene->id.next) {
 +                              scene->toolsettings->annotate_v3d_align = GP_PROJECT_VIEWSPACE | GP_PROJECT_CURSOR;
 +                              scene->toolsettings->annotate_thickness = 3;
 +                      }
 +              }
 +              if (!DNA_struct_elem_find(fd->filesdna, "bGPDlayer", "short", "line_change")) {
 +                      for (bGPdata *gpd = bmain->gpencil.first; gpd; gpd = gpd->id.next) {
 +                              for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) {
 +                                      gpl->line_change = gpl->thickness;
 +                                      if ((gpl->thickness < 1) || (gpl->thickness > 10)) {
 +                                              gpl->thickness = 3;
 +                                      }
 +                              }
 +                      }
 +              }
 +              if (!DNA_struct_elem_find(fd->filesdna, "View3DOverlay", "float", "gpencil_paper_opacity")) {
 +                      for (bScreen *screen = bmain->screen.first; screen; screen = screen->id.next) {
 +                              for (ScrArea *sa = screen->areabase.first; sa; sa = sa->next) {
 +                                      for (SpaceLink *sl = sa->spacedata.first; sl; sl = sl->next) {
 +                                              if (sl->spacetype == SPACE_VIEW3D) {
 +                                                      View3D *v3d = (View3D *)sl;
 +                                                      v3d->overlay.gpencil_paper_opacity = 0.5f;
 +                                              }
 +                                      }
 +                              }
 +                      }
 +              }
 +              if (!DNA_struct_elem_find(fd->filesdna, "View3DOverlay", "float", "gpencil_grid_opacity")) {
 +                      for (bScreen *screen = bmain->screen.first; screen; screen = screen->id.next) {
 +                              for (ScrArea *sa = screen->areabase.first; sa; sa = sa->next) {
 +                                      for (SpaceLink *sl = sa->spacedata.first; sl; sl = sl->next) {
 +                                              if (sl->spacetype == SPACE_VIEW3D) {
 +                                                      View3D *v3d = (View3D *)sl;
 +                                                      v3d->overlay.gpencil_grid_opacity = 0.5f;
 +                                              }
 +                                      }
 +                              }
 +                      }
 +              }
 +
 +              /* default loc axis */
 +              if (!DNA_struct_elem_find(fd->filesdna, "GP_Sculpt_Settings", "int", "lock_axis")) {
 +                      for (Scene *scene = bmain->scene.first; scene; scene = scene->id.next) {
 +                              /* lock axis */
 +                              GP_Sculpt_Settings *gset = &scene->toolsettings->gp_sculpt;
 +                              if (gset) {
 +                                      gset->lock_axis = GP_LOCKAXIS_Y;
 +                              }
 +                      }
 +              }
 +
 +              /* Versioning code for Subsurf modifier. */
 +              if (!DNA_struct_elem_find(fd->filesdna, "SubsurfModifier", "short", "uv_smooth")) {
 +                      for (Object *object = bmain->object.first; object != NULL; object = object->id.next) {
 +                              for (ModifierData *md = object->modifiers.first; md; md = md->next) {
 +                                      if (md->type == eModifierType_Subsurf) {
 +                                              SubsurfModifierData *smd = (SubsurfModifierData *)md;
 +                                              if (smd->flags & eSubsurfModifierFlag_SubsurfUv_DEPRECATED) {
 +                                                      smd->uv_smooth = SUBSURF_UV_SMOOTH_PRESERVE_CORNERS;
 +                                              }
 +                                              else {
 +                                                      smd->uv_smooth = SUBSURF_UV_SMOOTH_NONE;
 +                                              }
 +                                      }
 +                              }
 +                      }
 +              }
 +
 +              if (!DNA_struct_elem_find(fd->filesdna, "SubsurfModifier", "short", "quality")) {
 +                      for (Object *object = bmain->object.first; object != NULL; object = object->id.next) {
 +                              for (ModifierData *md = object->modifiers.first; md; md = md->next) {
 +                                      if (md->type == eModifierType_Subsurf) {
 +                                              SubsurfModifierData *smd = (SubsurfModifierData *)md;
 +                                              smd->quality = min_ii(smd->renderLevels, 3);
 +                                      }
 +                              }
 +                      }
 +              }
 +              /* Versioning code for Multires modifier. */
 +              if (!DNA_struct_elem_find(fd->filesdna, "MultiresModifier", "short", "quality")) {
 +                      for (Object *object = bmain->object.first; object != NULL; object = object->id.next) {
 +                              for (ModifierData *md = object->modifiers.first; md; md = md->next) {
 +                                      if (md->type == eModifierType_Multires) {
 +                                              MultiresModifierData *mmd = (MultiresModifierData *)md;
 +                                              mmd->quality = 3;
 +                                              if (mmd->flags & eMultiresModifierFlag_PlainUv_DEPRECATED) {
 +                                                      mmd->uv_smooth = SUBSURF_UV_SMOOTH_NONE;
 +                                              }
 +                                              else {
 +                                                      mmd->uv_smooth = SUBSURF_UV_SMOOTH_PRESERVE_CORNERS;
 +                                              }
 +                                      }
 +                              }
 +                      }
 +              }
 +
 +              if (!DNA_struct_elem_find(fd->filesdna, "ClothSimSettings", "short", "bending_model")) {
 +                      for (Object *ob = bmain->object.first; ob; ob = ob->id.next) {
 +                              for (ModifierData *md = ob->modifiers.first; md; md = md->next) {
 +                                      if (md->type == eModifierType_Cloth) {
 +                                              ClothModifierData *clmd = (ClothModifierData *)md;
 +
 +                                              clmd->sim_parms->bending_model = CLOTH_BENDING_LINEAR;
 +                                              clmd->sim_parms->tension = clmd->sim_parms->structural;
 +                                              clmd->sim_parms->compression = clmd->sim_parms->structural;
 +                                              clmd->sim_parms->shear = clmd->sim_parms->structural;
 +                                              clmd->sim_parms->max_tension = clmd->sim_parms->max_struct;
 +                                              clmd->sim_parms->max_compression = clmd->sim_parms->max_struct;
 +                                              clmd->sim_parms->max_shear = clmd->sim_parms->max_struct;
 +                                              clmd->sim_parms->vgroup_shear = clmd->sim_parms->vgroup_struct;
 +                                              clmd->sim_parms->tension_damp = clmd->sim_parms->Cdis;
 +                                              clmd->sim_parms->compression_damp = clmd->sim_parms->Cdis;
 +                                              clmd->sim_parms->shear_damp = clmd->sim_parms->Cdis;
 +                                      }
 +                              }
 +                      }
 +              }
 +
 +              if (!DNA_struct_elem_find(fd->filesdna, "BrushGpencilSettings", "float", "era_strength_f")) {
 +                      for (Brush *brush = bmain->brush.first; brush; brush = brush->id.next) {
 +                              if (brush->gpencil_settings != NULL) {
 +                                      BrushGpencilSettings *gp = brush->gpencil_settings;
 +                                      if (gp->brush_type == GPAINT_TOOL_ERASE) {
 +                                              gp->era_strength_f = 100.0f;
 +                                              gp->era_thickness_f = 10.0f;
 +                                      }
 +                              }
 +                      }
 +              }
 +
 +              for (Object *ob = bmain->object.first; ob; ob = ob->id.next) {
 +                      for (ModifierData *md = ob->modifiers.first; md; md = md->next) {
 +                              if (md->type == eModifierType_Cloth) {
 +                                      ClothModifierData *clmd = (ClothModifierData *)md;
 +
 +                                      if (!(clmd->sim_parms->flags & CLOTH_SIMSETTINGS_FLAG_GOAL)) {
 +                                              clmd->sim_parms->vgroup_mass = 0;
 +                                      }
 +
 +                                      if (!(clmd->sim_parms->flags & CLOTH_SIMSETTINGS_FLAG_SCALING)) {
 +                                              clmd->sim_parms->vgroup_struct = 0;
 +                                              clmd->sim_parms->vgroup_shear = 0;
 +                                              clmd->sim_parms->vgroup_bend = 0;
 +                                      }
 +
 +                                      if (!(clmd->sim_parms->flags & CLOTH_SIMSETTINGS_FLAG_SEW)) {
 +                                              clmd->sim_parms->shrink_min = 0.0f;
 +                                              clmd->sim_parms->vgroup_shrink = 0;
 +                                      }
 +
 +                                      if (!(clmd->coll_parms->flags & CLOTH_COLLSETTINGS_FLAG_ENABLED)) {
 +                                              clmd->coll_parms->flags &= ~CLOTH_COLLSETTINGS_FLAG_SELF;
 +                                      }
 +                              }
 +                      }
 +              }
 +      }
 +
 +      if (!MAIN_VERSION_ATLEAST(bmain, 280, 24)) {
 +              for (bScreen *screen = bmain->screen.first; screen; screen = screen->id.next) {
 +                      for (ScrArea *sa = screen->areabase.first; sa; sa = sa->next) {
 +                              for (SpaceLink *sl = sa->spacedata.first; sl; sl = sl->next) {
 +                                      if (sl->spacetype == SPACE_VIEW3D) {
 +                                              View3D *v3d = (View3D *)sl;
 +                                              v3d->overlay.edit_flag |= V3D_OVERLAY_EDIT_FACES |
 +                                                                        V3D_OVERLAY_EDIT_SEAMS |
 +                                                                        V3D_OVERLAY_EDIT_SHARP |
 +                                                                        V3D_OVERLAY_EDIT_FREESTYLE_EDGE |
 +                                                                        V3D_OVERLAY_EDIT_FREESTYLE_FACE |
 +                                                                        V3D_OVERLAY_EDIT_EDGES |
 +                                                                        V3D_OVERLAY_EDIT_CREASES |
 +                                                                        V3D_OVERLAY_EDIT_BWEIGHTS |
 +                                                                        V3D_OVERLAY_EDIT_CU_HANDLES |
 +                                                                        V3D_OVERLAY_EDIT_CU_NORMALS;
 +                                      }
 +                              }
 +                      }
 +              }
 +      }
 +
 +      {
 +              if (!DNA_struct_elem_find(fd->filesdna, "ShrinkwrapModifierData", "char", "shrinkMode")) {
 +                      for (Object *ob = bmain->object.first; ob; ob = ob->id.next) {
 +                              for (ModifierData *md = ob->modifiers.first; md; md = md->next) {
 +                                      if (md->type == eModifierType_Shrinkwrap) {
 +                                              ShrinkwrapModifierData *smd = (ShrinkwrapModifierData *)md;
 +                                              if (smd->shrinkOpts & MOD_SHRINKWRAP_KEEP_ABOVE_SURFACE) {
 +                                                      smd->shrinkMode = MOD_SHRINKWRAP_ABOVE_SURFACE;
 +                                                      smd->shrinkOpts &= ~MOD_SHRINKWRAP_KEEP_ABOVE_SURFACE;
 +                                              }
 +                                      }
 +                              }
 +                      }
 +              }
 +      }
 +
 +      if (!MAIN_VERSION_ATLEAST(bmain, 280, 24)) {
 +              if (!DNA_struct_elem_find(fd->filesdna, "PartDeflect", "float", "pdef_cfrict")) {
 +                      for (Object *ob = bmain->object.first; ob; ob = ob->id.next) {
 +                              if (ob->pd) {
 +                                      ob->pd->pdef_cfrict = 5.0f;
 +                              }
 +
 +                              for (ModifierData *md = ob->modifiers.first; md; md = md->next) {
 +                                      if (md->type == eModifierType_Cloth) {
 +                                              ClothModifierData *clmd = (ClothModifierData *)md;
 +
 +                                              clmd->coll_parms->selfepsilon = 0.015f;
 +                                      }
 +                              }
 +                      }
 +              }
 +
 +              if (!DNA_struct_elem_find(fd->filesdna, "View3DShading", "float", "xray_alpha_wire")) {
 +                      for (bScreen *screen = bmain->screen.first; screen; screen = screen->id.next) {
 +                              for (ScrArea *sa = screen->areabase.first; sa; sa = sa->next) {
 +                                      for (SpaceLink *sl = sa->spacedata.first; sl; sl = sl->next) {
 +                                              if (sl->spacetype == SPACE_VIEW3D) {
 +                                                      View3D *v3d = (View3D *)sl;
 +                                                      v3d->shading.xray_alpha_wire = 0.5f;
 +                                              }
 +                                      }
 +                              }
 +                      }
 +
 +                      for (bScreen *screen = bmain->screen.first; screen; screen = screen->id.next) {
 +                              for (ScrArea *sa = screen->areabase.first; sa; sa = sa->next) {
 +                                      for (SpaceLink *sl = sa->spacedata.first; sl; sl = sl->next) {
 +                                              if (sl->spacetype == SPACE_VIEW3D) {
 +                                                      View3D *v3d = (View3D *)sl;
 +                                                      v3d->shading.flag |= V3D_SHADING_XRAY_BONE;
 +                                              }
 +                                      }
 +                              }
 +                      }
 +              }
 +      }
 +
 +      if (!MAIN_VERSION_ATLEAST(bmain, 280, 25)) {
 +              for (Scene *scene = bmain->scene.first; scene; scene = scene->id.next) {
 +                      UnitSettings *unit = &scene->unit;
 +                      if (unit->system != USER_UNIT_NONE) {
 +                              unit->length_unit = bUnit_GetBaseUnitOfType(scene->unit.system, B_UNIT_LENGTH);
 +                              unit->mass_unit = bUnit_GetBaseUnitOfType(scene->unit.system, B_UNIT_MASS);
 +                      }
 +                      unit->time_unit = bUnit_GetBaseUnitOfType(USER_UNIT_NONE, B_UNIT_TIME);
 +              }
 +
 +              /* gpencil grid settings */
 +              for (bGPdata *gpd = bmain->gpencil.first; gpd; gpd = gpd->id.next) {
 +                      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
 +              }
 +      }
 +
 +      {
 +              for (bScreen *screen = bmain->screen.first; screen; screen = screen->id.next) {
 +                      for (ScrArea *sa = screen->areabase.first; sa; sa = sa->next) {
 +                              for (SpaceLink *sl = sa->spacedata.first; sl; sl = sl->next) {
 +                                      if (sl->spacetype == SPACE_VIEW3D) {
 +                                              View3D *v3d = (View3D *)sl;
 +                                              if (v3d->flag2 & V3D_OCCLUDE_WIRE) {
 +                                                      v3d->overlay.edit_flag |= V3D_OVERLAY_EDIT_OCCLUDE_WIRE;
 +                                                      v3d->flag2 &= ~V3D_OCCLUDE_WIRE;
 +                                              }
 +                                      }
 +                              }
 +                      }
 +              }
 +      }
 +
 +      if (!MAIN_VERSION_ATLEAST(bmain, 280, 29)) {
 +              for (bScreen *screen = bmain->screen.first; screen; screen = screen->id.next) {
 +                      for (ScrArea *sa = screen->areabase.first; sa; sa = sa->next) {
 +                              for (SpaceLink *sl = sa->spacedata.first; sl; sl = sl->next) {
 +                                      if (sl->spacetype == SPACE_BUTS) {
 +                                              ListBase *regionbase = (sl == sa->spacedata.first) ? &sa->regionbase : &sl->regionbase;
 +                                              ARegion *ar = MEM_callocN(sizeof(ARegion), "navigation bar for properties");
 +                                              ARegion *ar_header = NULL;
 +
 +                                              for (ar_header = regionbase->first; ar_header; ar_header = ar_header->next) {
 +                                                      if (ar_header->regiontype == RGN_TYPE_HEADER) {
 +                                                              break;
 +                                                      }
 +                                              }
 +                                              BLI_assert(ar_header);
 +
 +                                              BLI_insertlinkafter(regionbase, ar_header, ar);
 +
 +                                              ar->regiontype = RGN_TYPE_NAV_BAR;
 +                                              ar->alignment = RGN_ALIGN_LEFT;
 +                                      }
 +                              }
 +                      }
 +              }
 +
 +              /* grease pencil fade layer opacity */
 +              if (!DNA_struct_elem_find(fd->filesdna, "View3DOverlay", "float", "gpencil_fade_layer")) {
 +                      for (bScreen *screen = bmain->screen.first; screen; screen = screen->id.next) {
 +                              for (ScrArea *sa = screen->areabase.first; sa; sa = sa->next) {
 +                                      for (SpaceLink *sl = sa->spacedata.first; sl; sl = sl->next) {
 +                                              if (sl->spacetype == SPACE_VIEW3D) {
 +                                                      View3D *v3d = (View3D *)sl;
 +                                                      v3d->overlay.gpencil_fade_layer = 0.5f;
 +                                              }
 +                                      }
 +                              }
 +                      }
 +              }
 +
 +              for (Object *ob = bmain->object.first; ob; ob = ob->id.next) {
 +                      ob->empty_image_visibility_flag = (
 +                              OB_EMPTY_IMAGE_VISIBLE_PERSPECTIVE |
 +                              OB_EMPTY_IMAGE_VISIBLE_ORTHOGRAPHIC |
 +                              OB_EMPTY_IMAGE_VISIBLE_BACKSIDE);
 +              }
 +
 +
 +      }
 +
 +      if (!MAIN_VERSION_ATLEAST(bmain, 280, 30)) {
 +              /* grease pencil main material show switches */
 +              for (Material *mat = bmain->mat.first; mat; mat = mat->id.next) {
 +                      if (mat->gp_style) {
 +                              mat->gp_style->flag |= GP_STYLE_STROKE_SHOW;
 +                              mat->gp_style->flag |= GP_STYLE_FILL_SHOW;
 +                      }
 +              }
 +      }
 +
 +      if (!MAIN_VERSION_ATLEAST(bmain, 280, 33)) {
 +              /* Grease pencil reset sculpt brushes after struct rename  */
 +              if (!DNA_struct_elem_find(fd->filesdna, "GP_Sculpt_Settings", "int", "weighttype")) {
 +                      float curcolor_add[3], curcolor_sub[3];
 +                      ARRAY_SET_ITEMS(curcolor_add, 1.0f, 0.6f, 0.6f);
 +                      ARRAY_SET_ITEMS(curcolor_sub, 0.6f, 0.6f, 1.0f);
 +
 +                      for (Scene *scene = bmain->scene.first; scene; scene = scene->id.next) {
 +                              /* sculpt brushes */
 +                              GP_Sculpt_Settings *gset = &scene->toolsettings->gp_sculpt;
 +                              if (gset) {
 +                                      for (int i = 0; i < GP_SCULPT_TYPE_MAX; i++) {
 +                                              GP_Sculpt_Data *gp_brush = &gset->brush[i];
 +                                              gp_brush->size = 30;
 +                                              gp_brush->strength = 0.5f;
 +                                              gp_brush->flag = GP_SCULPT_FLAG_USE_FALLOFF | GP_SCULPT_FLAG_ENABLE_CURSOR;
 +                                              copy_v3_v3(gp_brush->curcolor_add, curcolor_add);
 +                                              copy_v3_v3(gp_brush->curcolor_sub, curcolor_sub);
 +                                      }
 +                              }
 +                      }
 +              }
 +
 +              /* Grease pencil target weight  */
 +              if (!DNA_struct_elem_find(fd->filesdna, "GP_Sculpt_Settings", "float", "target_weight")) {
 +                      for (Scene *scene = bmain->scene.first; scene; scene = scene->id.next) {
 +                              /* sculpt brushes */
 +                              GP_Sculpt_Settings *gset = &scene->toolsettings->gp_sculpt;
 +                              if (gset) {
 +                                      for (int i = 0; i < GP_SCULPT_TYPE_MAX; i++) {
 +                                              GP_Sculpt_Data *gp_brush = &gset->brush[i];
 +                                              gp_brush->target_weight = 1.0f;
 +                                      }
 +                              }
 +                      }
 +              }
 +
 +              if (!DNA_struct_elem_find(fd->filesdna, "SceneEEVEE", "float", "overscan")) {
 +                      for (Scene *scene = bmain->scene.first; scene; scene = scene->id.next) {
 +                              scene->eevee.overscan = 3.0f;
 +                      }
 +              }
 +
 +              for (Lamp *la = bmain->lamp.first; la; la = la->id.next) {
 +                      /* Removed Hemi lights. */
 +                      if (!ELEM(la->type, LA_LOCAL, LA_SUN, LA_SPOT, LA_AREA)) {
 +                              la->type = LA_SUN;
 +                      }
 +              }
 +
 +              if (!DNA_struct_elem_find(fd->filesdna, "SceneEEVEE", "float", "light_threshold")) {
 +                      for (Scene *scene = bmain->scene.first; scene; scene = scene->id.next) {
 +                              scene->eevee.light_threshold = 0.01f;
 +                      }
 +              }
 +
 +              if (!DNA_struct_elem_find(fd->filesdna, "SceneEEVEE", "float", "gi_irradiance_smoothing")) {
 +                      for (Scene *scene = bmain->scene.first; scene; scene = scene->id.next) {
 +                              scene->eevee.gi_irradiance_smoothing = 0.1f;
 +                      }
 +              }
 +
 +              if (!DNA_struct_elem_find(fd->filesdna, "SceneEEVEE", "float", "gi_filter_quality")) {
 +                      for (Scene *scene = bmain->scene.first; scene; scene = scene->id.next) {
 +                              scene->eevee.gi_filter_quality = 1.0f;
 +                      }
 +              }
 +
 +              if (!DNA_struct_elem_find(fd->filesdna, "Lamp", "float", "att_dist")) {
 +                      for (Lamp *la = bmain->lamp.first; la; la = la->id.next) {
 +                              la->att_dist = la->clipend;
 +                      }
 +              }
 +
 +              if (!DNA_struct_elem_find(fd->filesdna, "Brush", "char", "weightpaint_tool")) {
 +                      /* Magic defines from old files (2.7x) */
 +
 +#define PAINT_BLEND_MIX 0
 +#define PAINT_BLEND_ADD 1
 +#define PAINT_BLEND_SUB 2
 +#define PAINT_BLEND_MUL 3
 +#define PAINT_BLEND_BLUR 4
 +#define PAINT_BLEND_LIGHTEN 5
 +#define PAINT_BLEND_DARKEN 6
 +#define PAINT_BLEND_AVERAGE 7
 +#define PAINT_BLEND_SMEAR 8
 +#define PAINT_BLEND_COLORDODGE 9
 +#define PAINT_BLEND_DIFFERENCE 10
 +#define PAINT_BLEND_SCREEN 11
 +#define PAINT_BLEND_HARDLIGHT 12
 +#define PAINT_BLEND_OVERLAY 13
 +#define PAINT_BLEND_SOFTLIGHT 14
 +#define PAINT_BLEND_EXCLUSION 15
 +#define PAINT_BLEND_LUMINOSITY 16
 +#define PAINT_BLEND_SATURATION 17
 +#define PAINT_BLEND_HUE 18
 +#define PAINT_BLEND_ALPHA_SUB 19
 +#define PAINT_BLEND_ALPHA_ADD 20
 +
 +                      for (Brush *brush = bmain->brush.first; brush; brush = brush->id.next) {
 +                              if (brush->ob_mode & (OB_MODE_VERTEX_PAINT | OB_MODE_WEIGHT_PAINT)) {
 +                                      const char tool_init = brush->vertexpaint_tool;
 +                                      bool is_blend = false;
 +
 +                                      {
 +                                              char tool = tool_init;
 +                                              switch (tool_init) {
 +                                                      case PAINT_BLEND_MIX: tool = VPAINT_TOOL_DRAW; break;
 +                                                      case PAINT_BLEND_BLUR: tool = VPAINT_TOOL_BLUR; break;
 +                                                      case PAINT_BLEND_AVERAGE: tool = VPAINT_TOOL_AVERAGE; break;
 +                                                      case PAINT_BLEND_SMEAR: tool = VPAINT_TOOL_SMEAR; break;
 +                                                      default:
 +                                                              tool = VPAINT_TOOL_DRAW;
 +                                                              is_blend = true;
 +                                                              break;
 +                                              }
 +                                              brush->vertexpaint_tool = tool;
 +                                      }
 +
 +                                      if (is_blend == false) {
 +                                              brush->blend = IMB_BLEND_MIX;
 +                                      }
 +                                      else {
 +                                              short blend = IMB_BLEND_MIX;
 +                                              switch (tool_init) {
 +                                                      case PAINT_BLEND_ADD: blend = IMB_BLEND_ADD; break;
 +                                                      case PAINT_BLEND_SUB: blend = IMB_BLEND_SUB; break;
 +                                                      case PAINT_BLEND_MUL: blend = IMB_BLEND_MUL; break;
 +                                                      case PAINT_BLEND_LIGHTEN: blend = IMB_BLEND_LIGHTEN; break;
 +                                                      case PAINT_BLEND_DARKEN: blend = IMB_BLEND_DARKEN; break;
 +                                                      case PAINT_BLEND_COLORDODGE: blend = IMB_BLEND_COLORDODGE; break;
 +                                                      case PAINT_BLEND_DIFFERENCE: blend = IMB_BLEND_DIFFERENCE; break;
 +                                                      case PAINT_BLEND_SCREEN: blend = IMB_BLEND_SCREEN; break;
 +                                                      case PAINT_BLEND_HARDLIGHT: blend = IMB_BLEND_HARDLIGHT; break;
 +                                                      case PAINT_BLEND_OVERLAY: blend = IMB_BLEND_OVERLAY; break;
 +                                                      case PAINT_BLEND_SOFTLIGHT: blend = IMB_BLEND_SOFTLIGHT; break;
 +                                                      case PAINT_BLEND_EXCLUSION: blend = IMB_BLEND_EXCLUSION; break;
 +                                                      case PAINT_BLEND_LUMINOSITY: blend = IMB_BLEND_LUMINOSITY; break;
 +                                                      case PAINT_BLEND_SATURATION: blend = IMB_BLEND_SATURATION; break;
 +                                                      case PAINT_BLEND_HUE: blend = IMB_BLEND_HUE; break;
 +                                                      case PAINT_BLEND_ALPHA_SUB: blend = IMB_BLEND_ERASE_ALPHA; break;
 +                                                      case PAINT_BLEND_ALPHA_ADD: blend = IMB_BLEND_ADD_ALPHA; break;
 +                                              }
 +                                              brush->blend = blend;
 +                                      }
 +                              }
 +                              /* For now these match, in the future new items may not. */
 +                              brush->weightpaint_tool = brush->vertexpaint_tool;
 +                      }
 +
 +#undef PAINT_BLEND_MIX
 +#undef PAINT_BLEND_ADD
 +#undef PAINT_BLEND_SUB
 +#undef PAINT_BLEND_MUL
 +#undef PAINT_BLEND_BLUR
 +#undef PAINT_BLEND_LIGHTEN
 +#undef PAINT_BLEND_DARKEN
 +#undef PAINT_BLEND_AVERAGE
 +#undef PAINT_BLEND_SMEAR
 +#undef PAINT_BLEND_COLORDODGE
 +#undef PAINT_BLEND_DIFFERENCE
 +#undef PAINT_BLEND_SCREEN
 +#undef PAINT_BLEND_HARDLIGHT
 +#undef PAINT_BLEND_OVERLAY
 +#undef PAINT_BLEND_SOFTLIGHT
 +#undef PAINT_BLEND_EXCLUSION
 +#undef PAINT_BLEND_LUMINOSITY
 +#undef PAINT_BLEND_SATURATION
 +#undef PAINT_BLEND_HUE
 +#undef PAINT_BLEND_ALPHA_SUB
 +#undef PAINT_BLEND_ALPHA_ADD
 +              }
 +      }
 +
 +      if (!MAIN_VERSION_ATLEAST(bmain, 280, 34)) {
 +              for (bScreen *screen = bmain->screen.first; screen; screen = screen->id.next) {
 +                      for (ScrArea *area = screen->areabase.first; area; area = area->next) {
 +                              for (SpaceLink *slink = area->spacedata.first; slink; slink = slink->next) {
 +                                      if (slink->spacetype == SPACE_USERPREF) {
 +                                              ARegion *navigation_region = BKE_spacedata_find_region_type(slink, area, RGN_TYPE_NAV_BAR);
 +
 +                                              if (!navigation_region) {
 +                                                      ListBase *regionbase = (slink == area->spacedata.first) ?
 +                                                                             &area->regionbase : &slink->regionbase;
 +
 +                                                      navigation_region = MEM_callocN(sizeof(ARegion), "userpref navigation-region do_versions");
 +
 +                                                      BLI_addhead(regionbase, navigation_region); /* order matters, addhead not addtail! */
 +                                                      navigation_region->regiontype = RGN_TYPE_NAV_BAR;
 +                                                      navigation_region->alignment = RGN_ALIGN_LEFT;
 +                                              }
 +                                      }
 +                              }
 +                      }
 +              }
 +      }
 +
 +      if (!MAIN_VERSION_ATLEAST(bmain, 280, 36)) {
 +              if (!DNA_struct_elem_find(fd->filesdna, "View3DShading", "float", "curvature_ridge_factor")) {
 +                      for (bScreen *screen = bmain->screen.first; screen; screen = screen->id.next) {
 +                              for (ScrArea *sa = screen->areabase.first; sa; sa = sa->next) {
 +                                      for (SpaceLink *sl = sa->spacedata.first; sl; sl = sl->next) {
 +                                              if (sl->spacetype == SPACE_VIEW3D) {
 +                                                      View3D *v3d = (View3D *)sl;
 +                                                      v3d->shading.curvature_ridge_factor = 1.0f;
 +                                                      v3d->shading.curvature_valley_factor = 1.0f;
 +                                              }
 +                                      }
 +                              }
 +                      }
 +              }
 +
 +              /* Rename OpenGL to Workbench. */
 +              for (Scene *scene = bmain->scene.first; scene; scene = scene->id.next) {
 +                      if (STREQ(scene->r.engine, "BLENDER_OPENGL")) {
 +                              STRNCPY(scene->r.engine, RE_engine_id_BLENDER_WORKBENCH);
 +                      }
 +              }
 +
 +              /* init Annotations onion skin */
 +              if (!DNA_struct_elem_find(fd->filesdna, "bGPDlayer", "int", "gstep")) {
 +                      for (bGPdata *gpd = bmain->gpencil.first; gpd; gpd = gpd->id.next) {
 +                              for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) {
 +                                      ARRAY_SET_ITEMS(gpl->gcolor_prev, 0.302f, 0.851f, 0.302f);
 +                                      ARRAY_SET_ITEMS(gpl->gcolor_next, 0.250f, 0.1f, 1.0f);
 +                              }
 +                      }
 +              }
 +
 +              /* Move studio_light selection to lookdev_light. */
 +              if (!DNA_struct_elem_find(fd->filesdna, "View3DShading", "char", "lookdev_light[256]")) {
 +                      for (bScreen *screen = bmain->screen.first; screen; screen = screen->id.next) {
 +                              for (ScrArea *sa = screen->areabase.first; sa; sa = sa->next) {
 +                                      for (SpaceLink *sl = sa->spacedata.first; sl; sl = sl->next) {
 +                                              if (sl->spacetype == SPACE_VIEW3D) {
 +                                                      View3D *v3d = (View3D *)sl;
 +                                                      memcpy(v3d->shading.lookdev_light, v3d->shading.studio_light, sizeof(char) * 256);
 +                                              }
 +                                      }
 +                              }
 +                      }
 +              }
 +
 +              /* Change Solid mode shadow orientation. */
 +              if (!DNA_struct_elem_find(fd->filesdna, "SceneDisplay", "float", "shadow_focus")) {
 +                      for (Scene *scene = bmain->scene.first; scene; scene = scene->id.next) {
 +                              float *dir = scene->display.light_direction;
 +                              SWAP(float, dir[2], dir[1]);
 +                              dir[2] = -dir[2];
 +                              dir[0] = -dir[0];
 +                      }
 +              }
 +      }
 +
 +      if (!MAIN_VERSION_ATLEAST(bmain, 280, 37)) {
 +              for (Camera *ca = bmain->camera.first; ca; ca = ca->id.next) {
 +                      ca->drawsize *= 2.0f;
 +              }
 +              for (Object *ob = bmain->object.first; ob; ob = ob->id.next) {
 +                      if (ob->type != OB_EMPTY) {
 +                              if (UNLIKELY(ob->transflag & OB_DUPLICOLLECTION)) {
 +                                      BKE_object_type_set_empty_for_versioning(ob);
 +                              }
 +                      }
 +              }
 +      }
 +
 +      {
 +              /* Versioning code until next subversion bump goes here. */
 +
 +      }
 +}
@@@ -1962,13 -1932,6 +1962,13 @@@ static BMOpDefine bmo_wireframe_def = 
         BMO_OPTYPE_FLAG_SELECT_VALIDATE),
  };
  
-       {BMOP_POKE_MEAN_WEIGHTED, "MEAN_WEIGHTED"},
-       {BMOP_POKE_MEAN, "MEAN"},
 +static BMO_FlagSet bmo_enum_poke_center_mode[] = {
++      {BMOP_POKE_MEDIAN_WEIGHTED, "MEAN_WEIGHTED"},
++      {BMOP_POKE_MEDIAN, "MEAN"},
 +      {BMOP_POKE_BOUNDS, "BOUNDS"},
 +      {0, NULL},
 +};
 +
  /*
   * Pokes a face.
   *
index 7322f33,0000000..3037a2f
mode 100644,000000..100644
--- /dev/null
@@@ -1,5885 -1,0 +1,5885 @@@
-                       BM_face_calc_center_mean(efa, center);
 +/*
 + * ***** 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) 2017 by Blender Foundation.
 + * All rights reserved.
 + *
 + * Contributor(s): Blender Foundation, Mike Erwin, Dalai Felinto
 + *
 + * ***** END GPL LICENSE BLOCK *****
 + */
 +
 +/** \file draw_cache_impl_mesh.c
 + *  \ingroup draw
 + *
 + * \brief Mesh API for render engines
 + */
 +
 +#include "MEM_guardedalloc.h"
 +
 +#include "BLI_buffer.h"
 +#include "BLI_utildefines.h"
 +#include "BLI_math_vector.h"
 +#include "BLI_math_bits.h"
 +#include "BLI_math_color.h"
 +#include "BLI_string.h"
 +#include "BLI_alloca.h"
 +#include "BLI_edgehash.h"
 +
 +#include "DNA_mesh_types.h"
 +#include "DNA_meshdata_types.h"
 +#include "DNA_object_types.h"
 +#include "DNA_space_types.h"
 +
 +#include "BKE_customdata.h"
 +#include "BKE_deform.h"
 +#include "BKE_editmesh.h"
 +#include "BKE_editmesh_cache.h"
 +#include "BKE_editmesh_tangent.h"
 +#include "BKE_mesh.h"
 +#include "BKE_mesh_tangent.h"
 +#include "BKE_mesh_runtime.h"
 +#include "BKE_colorband.h"
 +#include "BKE_cdderivedmesh.h"
 +
 +#include "bmesh.h"
 +
 +#include "GPU_batch.h"
 +#include "GPU_batch_presets.h"
 +#include "GPU_draw.h"
 +#include "GPU_material.h"
 +
 +#include "DRW_render.h"
 +
 +#include "ED_image.h"
 +#include "ED_mesh.h"
 +#include "ED_uvedit.h"
 +
 +#include "draw_cache_impl.h"  /* own include */
 +
 +
 +static void mesh_batch_cache_clear(Mesh *me);
 +
 +
 +/* ---------------------------------------------------------------------- */
 +
 +/** \name Mesh/BMesh Interface (direct access to basic data).
 + * \{ */
 +
 +static int mesh_render_verts_len_get(Mesh *me)
 +{
 +      return me->edit_btmesh ? me->edit_btmesh->bm->totvert : me->totvert;
 +}
 +
 +static int mesh_render_edges_len_get(Mesh *me)
 +{
 +      return me->edit_btmesh ? me->edit_btmesh->bm->totedge : me->totedge;
 +}
 +
 +static int mesh_render_looptri_len_get(Mesh *me)
 +{
 +      return me->edit_btmesh ? me->edit_btmesh->tottri : poly_to_tri_count(me->totpoly, me->totloop);
 +}
 +
 +static int mesh_render_polys_len_get(Mesh *me)
 +{
 +      return me->edit_btmesh ? me->edit_btmesh->bm->totface : me->totpoly;
 +}
 +
 +static int mesh_render_mat_len_get(Mesh *me)
 +{
 +      return MAX2(1, me->totcol);
 +}
 +
 +static int UNUSED_FUNCTION(mesh_render_loops_len_get)(Mesh *me)
 +{
 +      return me->edit_btmesh ? me->edit_btmesh->bm->totloop : me->totloop;
 +}
 +
 +/** \} */
 +
 +
 +/* ---------------------------------------------------------------------- */
 +
 +/** \name Mesh/BMesh Interface (indirect, partially cached access to complex data).
 + * \{ */
 +
 +typedef struct EdgeAdjacentPolys {
 +      int count;
 +      int face_index[2];
 +} EdgeAdjacentPolys;
 +
 +typedef struct EdgeAdjacentVerts {
 +      int vert_index[2]; /* -1 if none */
 +} EdgeAdjacentVerts;
 +
 +typedef struct EdgeDrawAttr {
 +      uchar v_flag;
 +      uchar e_flag;
 +      uchar crease;
 +      uchar bweight;
 +} EdgeDrawAttr;
 +
 +typedef struct MeshRenderData {
 +      int types;
 +
 +      int vert_len;
 +      int edge_len;
 +      int tri_len;
 +      int loop_len;
 +      int poly_len;
 +      int mat_len;
 +      int loose_vert_len;
 +      int loose_edge_len;
 +
 +      /* Support for mapped mesh data. */
 +      struct {
 +              /* Must be set if we want to get mapped data. */
 +              bool use;
 +              bool supported;
 +
 +              Mesh *me_cage;
 +
 +              int vert_len;
 +              int edge_len;
 +              int tri_len;
 +              int loop_len;
 +              int poly_len;
 +
 +              int *loose_verts;
 +              int  loose_vert_len;
 +
 +              int *loose_edges;
 +              int  loose_edge_len;
 +
 +              /* origindex layers */
 +              int *v_origindex;
 +              int *e_origindex;
 +              int *l_origindex;
 +              int *p_origindex;
 +      } mapped;
 +
 +      BMEditMesh *edit_bmesh;
 +      struct EditMeshData *edit_data;
 +
 +      Mesh *me;
 +
 +      MVert *mvert;
 +      const MEdge *medge;
 +      const MLoop *mloop;
 +      const MPoly *mpoly;
 +      float (*orco)[3];  /* vertex coordinates normalized to bounding box */
 +      bool is_orco_allocated;
 +      MDeformVert *dvert;
 +      MLoopUV *mloopuv;
 +      MLoopCol *mloopcol;
 +      float (*loop_normals)[3];
 +
 +      /* CustomData 'cd' cache for efficient access. */
 +      struct {
 +              struct {
 +                      MLoopUV **uv;
 +                      int       uv_len;
 +                      int       uv_active;
 +
 +                      MLoopCol **vcol;
 +                      int        vcol_len;
 +                      int        vcol_active;
 +
 +                      float (**tangent)[4];
 +                      int      tangent_len;
 +                      int      tangent_active;
 +
 +                      bool *auto_vcol;
 +              } layers;
 +
 +              /* Custom-data offsets (only needed for BMesh access) */
 +              struct {
 +                      int crease;
 +                      int bweight;
 +                      int *uv;
 +                      int *vcol;
 +#ifdef WITH_FREESTYLE
 +                      int freestyle_edge;
 +                      int freestyle_face;
 +#endif
 +              } offset;
 +
 +              struct {
 +                      char (*auto_mix)[32];
 +                      char (*uv)[32];
 +                      char (*vcol)[32];
 +                      char (*tangent)[32];
 +              } uuid;
 +
 +              /* for certain cases we need an output loop-data storage (bmesh tangents) */
 +              struct {
 +                      CustomData ldata;
 +                      /* grr, special case variable (use in place of 'dm->tangent_mask') */
 +                      short tangent_mask;
 +              } output;
 +      } cd;
 +
 +      BMVert *eve_act;
 +      BMEdge *eed_act;
 +      BMFace *efa_act;
 +
 +      /* Data created on-demand (usually not for bmesh-based data). */
 +      EdgeAdjacentPolys *edges_adjacent_polys;
 +      MLoopTri *mlooptri;
 +      int *loose_edges;
 +      int *loose_verts;
 +
 +      float (*poly_normals)[3];
 +      float (*vert_weight);
 +      char (*vert_color)[3];
 +      GPUPackedNormal *poly_normals_pack;
 +      GPUPackedNormal *vert_normals_pack;
 +      bool *edge_select_bool;
 +      bool *edge_visible_bool;
 +} MeshRenderData;
 +
 +enum {
 +      MR_DATATYPE_VERT       = 1 << 0,
 +      MR_DATATYPE_EDGE       = 1 << 1,
 +      MR_DATATYPE_LOOPTRI    = 1 << 2,
 +      MR_DATATYPE_LOOP       = 1 << 3,
 +      MR_DATATYPE_POLY       = 1 << 4,
 +      MR_DATATYPE_OVERLAY    = 1 << 5,
 +      MR_DATATYPE_SHADING    = 1 << 6,
 +      MR_DATATYPE_DVERT      = 1 << 7,
 +      MR_DATATYPE_LOOPCOL    = 1 << 8,
 +      MR_DATATYPE_LOOPUV     = 1 << 9,
 +      MR_DATATYPE_LOOSE_VERT = 1 << 10,
 +      MR_DATATYPE_LOOSE_EDGE = 1 << 11,
 +};
 +
 +/**
 + * These functions look like they would be slow but they will typically return true on the first iteration.
 + * Only false when all attached elements are hidden.
 + */
 +static bool bm_vert_has_visible_edge(const BMVert *v)
 +{
 +      const BMEdge *e_iter, *e_first;
 +
 +      e_iter = e_first = v->e;
 +      do {
 +              if (!BM_elem_flag_test(e_iter, BM_ELEM_HIDDEN)) {
 +                      return true;
 +              }
 +      } while ((e_iter = BM_DISK_EDGE_NEXT(e_iter, v)) != e_first);
 +      return false;
 +}
 +
 +static bool bm_edge_has_visible_face(const BMEdge *e)
 +{
 +      const BMLoop *l_iter, *l_first;
 +      l_iter = l_first = e->l;
 +      do {
 +              if (!BM_elem_flag_test(l_iter->f, BM_ELEM_HIDDEN)) {
 +                      return true;
 +              }
 +      } while ((l_iter = l_iter->radial_next) != l_first);
 +      return false;
 +}
 +
 +
 +static void mesh_cd_calc_used_gpu_layers(
 +        CustomData *UNUSED(cd_vdata), uchar cd_vused[CD_NUMTYPES],
 +        CustomData *cd_ldata, ushort cd_lused[CD_NUMTYPES],
 +        struct GPUMaterial **gpumat_array, int gpumat_array_len)
 +{
 +      /* See: DM_vertex_attributes_from_gpu for similar logic */
 +      GPUVertexAttribs gattribs = {{{0}}};
 +
 +      for (int i = 0; i < gpumat_array_len; i++) {
 +              GPUMaterial *gpumat = gpumat_array[i];
 +              if (gpumat) {
 +                      GPU_material_vertex_attributes(gpumat, &gattribs);
 +                      for (int j = 0; j < gattribs.totlayer; j++) {
 +                              const char *name = gattribs.layer[j].name;
 +                              int type = gattribs.layer[j].type;
 +                              int layer = -1;
 +
 +                              if (type == CD_AUTO_FROM_NAME) {
 +                                      /* We need to deduct what exact layer is used.
 +                                       *
 +                                       * We do it based on the specified name.
 +                                       */
 +                                      if (name[0] != '\0') {
 +                                              layer = CustomData_get_named_layer(cd_ldata, CD_MLOOPUV, name);
 +                                              type = CD_MTFACE;
 +
 +                                              if (layer == -1) {
 +                                                      layer = CustomData_get_named_layer(cd_ldata, CD_MLOOPCOL, name);
 +                                                      type = CD_MCOL;
 +                                              }
 +#if 0                                 /* Tangents are always from UV's - this will never happen. */
 +                                              if (layer == -1) {
 +                                                      layer = CustomData_get_named_layer(cd_ldata, CD_TANGENT, name);
 +                                                      type = CD_TANGENT;
 +                                              }
 +#endif
 +                                              if (layer == -1) {
 +                                                      continue;
 +                                              }
 +                                      }
 +                                      else {
 +                                              /* Fall back to the UV layer, which matches old behavior. */
 +                                              type = CD_MTFACE;
 +                                      }
 +                              }
 +
 +                              switch (type) {
 +                                      case CD_MTFACE:
 +                                      {
 +                                              if (layer == -1) {
 +                                                      layer = (name[0] != '\0') ?
 +                                                              CustomData_get_named_layer(cd_ldata, CD_MLOOPUV, name) :
 +                                                              CustomData_get_active_layer(cd_ldata, CD_MLOOPUV);
 +                                              }
 +                                              if (layer != -1) {
 +                                                      cd_lused[CD_MLOOPUV] |= (1 << layer);
 +                                              }
 +                                              break;
 +                                      }
 +                                      case CD_TANGENT:
 +                                      {
 +                                              if (layer == -1) {
 +                                                      layer = (name[0] != '\0') ?
 +                                                              CustomData_get_named_layer(cd_ldata, CD_MLOOPUV, name) :
 +                                                              CustomData_get_active_layer(cd_ldata, CD_MLOOPUV);
 +                                              }
 +                                              if (layer != -1) {
 +                                                      cd_lused[CD_TANGENT] |= (1 << layer);
 +                                              }
 +                                              else {
 +                                                      /* no UV layers at all => requesting orco */
 +                                                      cd_lused[CD_TANGENT] |= DM_TANGENT_MASK_ORCO;
 +                                                      cd_vused[CD_ORCO] |= 1;
 +                                              }
 +                                              break;
 +                                      }
 +                                      case CD_MCOL:
 +                                      {
 +                                              if (layer == -1) {
 +                                                      layer = (name[0] != '\0') ?
 +                                                              CustomData_get_named_layer(cd_ldata, CD_MLOOPCOL, name) :
 +                                                              CustomData_get_active_layer(cd_ldata, CD_MLOOPCOL);
 +                                              }
 +                                              if (layer != -1) {
 +                                                      cd_lused[CD_MLOOPCOL] |= (1 << layer);
 +                                              }
 +                                              break;
 +                                      }
 +                                      case CD_ORCO:
 +                                      {
 +                                              cd_vused[CD_ORCO] |= 1;
 +                                              break;
 +                                      }
 +                              }
 +                      }
 +              }
 +      }
 +}
 +
 +
 +static void mesh_render_calc_normals_loop_and_poly(const Mesh *me, const float split_angle, MeshRenderData *rdata)
 +{
 +      BLI_assert((me->flag & ME_AUTOSMOOTH) != 0);
 +
 +      int totloop = me->totloop;
 +      int totpoly = me->totpoly;
 +      float (*loop_normals)[3] = MEM_mallocN(sizeof(*loop_normals) * totloop, __func__);
 +      float (*poly_normals)[3] = MEM_mallocN(sizeof(*poly_normals) * totpoly, __func__);
 +      short (*clnors)[2] = CustomData_get_layer(&me->ldata, CD_CUSTOMLOOPNORMAL);
 +
 +      BKE_mesh_calc_normals_poly(
 +              me->mvert, NULL, me->totvert,
 +              me->mloop, me->mpoly, totloop, totpoly, poly_normals, false);
 +
 +      BKE_mesh_normals_loop_split(
 +              me->mvert, me->totvert, me->medge, me->totedge,
 +              me->mloop, loop_normals, totloop, me->mpoly, poly_normals, totpoly,
 +              true, split_angle, NULL, clnors, NULL);
 +
 +      rdata->loop_len = totloop;
 +      rdata->poly_len = totpoly;
 +      rdata->loop_normals = loop_normals;
 +      rdata->poly_normals = poly_normals;
 +}
 +
 +
 +/**
 + * TODO(campbell): 'gpumat_array' may include materials linked to the object.
 + * While not default, object materials should be supported.
 + * Although this only impacts the data that's generated, not the materials that display.
 + */
 +static MeshRenderData *mesh_render_data_create_ex(
 +        Mesh *me, const int types,
 +        struct GPUMaterial **gpumat_array, uint gpumat_array_len)
 +{
 +      MeshRenderData *rdata = MEM_callocN(sizeof(*rdata), __func__);
 +      rdata->types = types;
 +      rdata->mat_len = mesh_render_mat_len_get(me);
 +
 +      CustomData_reset(&rdata->cd.output.ldata);
 +
 +      const bool is_auto_smooth = (me->flag & ME_AUTOSMOOTH) != 0;
 +      const float split_angle = is_auto_smooth ? me->smoothresh : (float)M_PI;
 +
 +      if (me->edit_btmesh) {
 +              BMEditMesh *embm = me->edit_btmesh;
 +              BMesh *bm = embm->bm;
 +
 +              rdata->edit_bmesh = embm;
 +              rdata->edit_data = me->runtime.edit_data;
 +
 +              if (embm->mesh_eval_cage && (embm->mesh_eval_cage->runtime.is_original == false)) {
 +                      Mesh *me_cage = embm->mesh_eval_cage;
 +
 +                      rdata->mapped.me_cage = me_cage;
 +                      if (types & MR_DATATYPE_VERT) {
 +                              rdata->mapped.vert_len = me_cage->totvert;
 +                      }
 +                      if (types & MR_DATATYPE_EDGE) {
 +                              rdata->mapped.edge_len = me_cage->totedge;
 +                      }
 +                      if (types & MR_DATATYPE_LOOP) {
 +                              rdata->mapped.loop_len = me_cage->totloop;
 +                      }
 +                      if (types & MR_DATATYPE_POLY) {
 +                              rdata->mapped.poly_len = me_cage->totpoly;
 +                      }
 +                      if (types & MR_DATATYPE_LOOPTRI) {
 +                              rdata->mapped.tri_len = poly_to_tri_count(me_cage->totpoly, me_cage->totloop);
 +                      }
 +
 +                      rdata->mapped.v_origindex = CustomData_get_layer(&me_cage->vdata, CD_ORIGINDEX);
 +                      rdata->mapped.e_origindex = CustomData_get_layer(&me_cage->edata, CD_ORIGINDEX);
 +                      rdata->mapped.l_origindex = CustomData_get_layer(&me_cage->ldata, CD_ORIGINDEX);
 +                      rdata->mapped.p_origindex = CustomData_get_layer(&me_cage->pdata, CD_ORIGINDEX);
 +                      rdata->mapped.supported = (
 +                              rdata->mapped.v_origindex &&
 +                              rdata->mapped.e_origindex &&
 +                              rdata->mapped.p_origindex);
 +              }
 +
 +              int bm_ensure_types = 0;
 +              if (types & MR_DATATYPE_VERT) {
 +                      rdata->vert_len = bm->totvert;
 +                      bm_ensure_types |= BM_VERT;
 +              }
 +              if (types & MR_DATATYPE_EDGE) {
 +                      rdata->edge_len = bm->totedge;
 +                      bm_ensure_types |= BM_EDGE;
 +              }
 +              if (types & MR_DATATYPE_LOOPTRI) {
 +                      bm_ensure_types |= BM_LOOP;
 +              }
 +              if (types & MR_DATATYPE_LOOP) {
 +                      int totloop = bm->totloop;
 +                      if (is_auto_smooth) {
 +                              rdata->loop_normals = MEM_mallocN(sizeof(*rdata->loop_normals) * totloop, __func__);
 +                              int cd_loop_clnors_offset = CustomData_get_offset(&bm->ldata, CD_CUSTOMLOOPNORMAL);
 +                              BM_loops_calc_normal_vcos(
 +                                      bm, NULL, NULL, NULL, true, split_angle, rdata->loop_normals, NULL, NULL,
 +                                      cd_loop_clnors_offset, false);
 +                      }
 +                      rdata->loop_len = totloop;
 +                      bm_ensure_types |= BM_LOOP;
 +              }
 +              if (types & MR_DATATYPE_POLY) {
 +                      rdata->poly_len = bm->totface;
 +                      bm_ensure_types |= BM_FACE;
 +              }
 +              if (types & MR_DATATYPE_OVERLAY) {
 +                      rdata->efa_act = BM_mesh_active_face_get(bm, false, true);
 +                      rdata->eed_act = BM_mesh_active_edge_get(bm);
 +                      rdata->eve_act = BM_mesh_active_vert_get(bm);
 +                      rdata->cd.offset.crease = CustomData_get_offset(&bm->edata, CD_CREASE);
 +                      rdata->cd.offset.bweight = CustomData_get_offset(&bm->edata, CD_BWEIGHT);
 +
 +#ifdef WITH_FREESTYLE
 +                      rdata->cd.offset.freestyle_edge = CustomData_get_offset(&bm->edata, CD_FREESTYLE_EDGE);
 +                      rdata->cd.offset.freestyle_face = CustomData_get_offset(&bm->pdata, CD_FREESTYLE_FACE);
 +#endif
 +              }
 +              if (types & (MR_DATATYPE_DVERT)) {
 +                      bm_ensure_types |= BM_VERT;
 +              }
 +              if (rdata->edit_data != NULL) {
 +                      bm_ensure_types |= BM_VERT;
 +              }
 +
 +              BM_mesh_elem_index_ensure(bm, bm_ensure_types);
 +              BM_mesh_elem_table_ensure(bm, bm_ensure_types & ~BM_LOOP);
 +
 +              if (types & MR_DATATYPE_LOOPTRI) {
 +                      /* Edit mode ensures this is valid, no need to calculate. */
 +                      BLI_assert((bm->totloop == 0) || (embm->looptris != NULL));
 +                      int tottri = embm->tottri;
 +                      MLoopTri *mlooptri = MEM_mallocN(sizeof(*rdata->mlooptri) * embm->tottri, __func__);
 +                      for (int index = 0; index < tottri ; index ++ ) {
 +                              BMLoop **bmtri = embm->looptris[index];
 +                              MLoopTri *mtri = &mlooptri[index];
 +                              mtri->tri[0] = BM_elem_index_get(bmtri[0]);
 +                              mtri->tri[1] = BM_elem_index_get(bmtri[1]);
 +                              mtri->tri[2] = BM_elem_index_get(bmtri[2]);
 +                      }
 +                      rdata->mlooptri = mlooptri;
 +                      rdata->tri_len = tottri;
 +              }
 +
 +              if (types & MR_DATATYPE_LOOSE_VERT) {
 +                      BLI_assert(types & MR_DATATYPE_VERT);
 +                      rdata->loose_vert_len = 0;
 +
 +                      {
 +                              int *lverts = MEM_mallocN(rdata->vert_len * sizeof(int), __func__);
 +                              BLI_assert((bm->elem_table_dirty & BM_VERT) == 0);
 +                              for (int i = 0; i < bm->totvert; i++) {
 +                                      const BMVert *eve = BM_vert_at_index(bm, i);
 +                                      if (!BM_elem_flag_test(eve, BM_ELEM_HIDDEN)) {
 +                                              /* Loose vert */
 +                                              if (eve->e == NULL || !bm_vert_has_visible_edge(eve)) {
 +                                                      lverts[rdata->loose_vert_len++] = i;
 +                                              }
 +                                      }
 +                              }
 +                              rdata->loose_verts = MEM_reallocN(lverts, rdata->loose_vert_len * sizeof(int));
 +                      }
 +
 +                      if (rdata->mapped.supported) {
 +                              Mesh *me_cage = embm->mesh_eval_cage;
 +                              rdata->mapped.loose_vert_len = 0;
 +
 +                              if (rdata->loose_vert_len) {
 +                                      int *lverts = MEM_mallocN(me_cage->totvert * sizeof(int), __func__);
 +                                      const int *v_origindex = rdata->mapped.v_origindex;
 +                                      for (int i = 0; i < me_cage->totvert; i++) {
 +                                              const int v_orig = v_origindex[i];
 +                                              if (v_orig != ORIGINDEX_NONE) {
 +                                                      BMVert *eve = BM_vert_at_index(bm, v_orig);
 +                                                      if (!BM_elem_flag_test(eve, BM_ELEM_HIDDEN)) {
 +                                                              /* Loose vert */
 +                                                              if (eve->e == NULL || !bm_vert_has_visible_edge(eve)) {
 +                                                                      lverts[rdata->mapped.loose_vert_len++] = i;
 +                                                              }
 +                                                      }
 +                                              }
 +                                      }
 +                                      rdata->mapped.loose_verts = MEM_reallocN(lverts, rdata->mapped.loose_vert_len * sizeof(int));
 +                              }
 +                      }
 +              }
 +
 +              if (types & MR_DATATYPE_LOOSE_EDGE) {
 +                      BLI_assert(types & MR_DATATYPE_EDGE);
 +                      rdata->loose_edge_len = 0;
 +
 +                      {
 +                              int *ledges = MEM_mallocN(rdata->edge_len * sizeof(int), __func__);
 +                              BLI_assert((bm->elem_table_dirty & BM_EDGE) == 0);
 +                              for (int i = 0; i < bm->totedge; i++) {
 +                                      const BMEdge *eed = BM_edge_at_index(bm, i);
 +                                      if (!BM_elem_flag_test(eed, BM_ELEM_HIDDEN)) {
 +                                              /* Loose edge */
 +                                              if (eed->l == NULL || !bm_edge_has_visible_face(eed)) {
 +                                                      ledges[rdata->loose_edge_len++] = i;
 +                                              }
 +                                      }
 +                              }
 +                              rdata->loose_edges = MEM_reallocN(ledges, rdata->loose_edge_len * sizeof(int));
 +                      }
 +
 +                      if (rdata->mapped.supported) {
 +                              Mesh *me_cage = embm->mesh_eval_cage;
 +                              rdata->mapped.loose_edge_len = 0;
 +
 +                              if (rdata->loose_edge_len) {
 +                                      int *ledges = MEM_mallocN(me_cage->totedge * sizeof(int), __func__);
 +                                      const int *e_origindex = rdata->mapped.e_origindex;
 +                                      for (int i = 0; i < me_cage->totedge; i++) {
 +                                              const int e_orig = e_origindex[i];
 +                                              if (e_orig != ORIGINDEX_NONE) {
 +                                                      BMEdge *eed = BM_edge_at_index(bm, e_orig);
 +                                                      if (!BM_elem_flag_test(eed, BM_ELEM_HIDDEN)) {
 +                                                              /* Loose edge */
 +                                                              if (eed->l == NULL || !bm_edge_has_visible_face(eed)) {
 +                                                                      ledges[rdata->mapped.loose_edge_len++] = i;
 +                                                              }
 +                                                      }
 +                                              }
 +                                      }
 +                                      rdata->mapped.loose_edges = MEM_reallocN(ledges, rdata->mapped.loose_edge_len * sizeof(int));
 +                              }
 +                      }
 +              }
 +      }
 +      else {
 +              rdata->me = me;
 +
 +              if (types & (MR_DATATYPE_VERT)) {
 +                      rdata->vert_len = me->totvert;
 +                      rdata->mvert = CustomData_get_layer(&me->vdata, CD_MVERT);
 +              }
 +              if (types & (MR_DATATYPE_EDGE)) {
 +                      rdata->edge_len = me->totedge;
 +                      rdata->medge = CustomData_get_layer(&me->edata, CD_MEDGE);
 +              }
 +              if (types & MR_DATATYPE_LOOPTRI) {
 +                      const int tri_len = rdata->tri_len = poly_to_tri_count(me->totpoly, me->totloop);
 +                      MLoopTri *mlooptri = MEM_mallocN(sizeof(*mlooptri) * tri_len, __func__);
 +                      BKE_mesh_recalc_looptri(me->mloop, me->mpoly, me->mvert, me->totloop, me->totpoly, mlooptri);
 +                      rdata->mlooptri = mlooptri;
 +              }
 +              if (types & MR_DATATYPE_LOOP) {
 +                      rdata->loop_len = me->totloop;
 +                      rdata->mloop = CustomData_get_layer(&me->ldata, CD_MLOOP);
 +
 +                      if (is_auto_smooth) {
 +                              mesh_render_calc_normals_loop_and_poly(me, split_angle, rdata);
 +                      }
 +              }
 +              if (types & MR_DATATYPE_POLY) {
 +                      rdata->poly_len = me->totpoly;
 +                      rdata->mpoly = CustomData_get_layer(&me->pdata, CD_MPOLY);
 +              }
 +              if (types & MR_DATATYPE_DVERT) {
 +                      rdata->vert_len = me->totvert;
 +                      rdata->dvert = CustomData_get_layer(&me->vdata, CD_MDEFORMVERT);
 +              }
 +              if (types & MR_DATATYPE_LOOPCOL) {
 +                      rdata->loop_len = me->totloop;
 +                      rdata->mloopcol = CustomData_get_layer(&me->ldata, CD_MLOOPCOL);
 +              }
 +              if (types & MR_DATATYPE_LOOPUV) {
 +                      rdata->loop_len = me->totloop;
 +                      rdata->mloopuv = CustomData_get_layer(&me->ldata, CD_MLOOPUV);
 +              }
 +      }
 +
 +      if (types & MR_DATATYPE_SHADING) {
 +              CustomData *cd_vdata, *cd_ldata;
 +
 +              if (me->edit_btmesh) {
 +                      BMesh *bm = me->edit_btmesh->bm;
 +                      cd_vdata = &bm->vdata;
 +                      cd_ldata = &bm->ldata;
 +              }
 +              else {
 +                      cd_vdata = &me->vdata;
 +                      cd_ldata = &me->ldata;
 +              }
 +
 +              /* Add edge/poly if we need them */
 +              uchar cd_vused[CD_NUMTYPES] = {0};
 +              ushort cd_lused[CD_NUMTYPES] = {0};
 +
 +              mesh_cd_calc_used_gpu_layers(
 +                      cd_vdata, cd_vused,
 +                      cd_ldata, cd_lused,
 +                      gpumat_array, gpumat_array_len);
 +
 +
 +              rdata->cd.layers.uv_active = CustomData_get_active_layer(cd_ldata, CD_MLOOPUV);
 +              rdata->cd.layers.vcol_active = CustomData_get_active_layer(cd_ldata, CD_MLOOPCOL);
 +              rdata->cd.layers.tangent_active = rdata->cd.layers.uv_active;
 +
 +#define CD_VALIDATE_ACTIVE_LAYER(active_index, used) \
 +              if ((active_index != -1) && (used & (1 << active_index)) == 0) { \
 +                      active_index = -1; \
 +              } ((void)0)
 +
 +              CD_VALIDATE_ACTIVE_LAYER(rdata->cd.layers.uv_active, cd_lused[CD_MLOOPUV]);
 +              CD_VALIDATE_ACTIVE_LAYER(rdata->cd.layers.tangent_active, cd_lused[CD_TANGENT]);
 +              CD_VALIDATE_ACTIVE_LAYER(rdata->cd.layers.vcol_active, cd_lused[CD_MLOOPCOL]);
 +
 +#undef CD_VALIDATE_ACTIVE_LAYER
 +
 +              rdata->is_orco_allocated = false;
 +              if (cd_vused[CD_ORCO] & 1) {
 +                      rdata->orco = CustomData_get_layer(cd_vdata, CD_ORCO);
 +                      /* If orco is not available compute it ourselves */
 +                      if (!rdata->orco) {
 +                              rdata->is_orco_allocated = true;
 +                              if (me->edit_btmesh) {
 +                                      BMesh *bm = me->edit_btmesh->bm;
 +                                      rdata->orco = MEM_mallocN(sizeof(*rdata->orco) * rdata->vert_len, "orco mesh");
 +                                      BLI_assert((bm->elem_table_dirty & BM_VERT) == 0);
 +                                      for (int i = 0; i < bm->totvert; i++) {
 +                                              copy_v3_v3(rdata->orco[i], BM_vert_at_index(bm, i)->co);
 +                                      }
 +                                      BKE_mesh_orco_verts_transform(me, rdata->orco, rdata->vert_len, 0);
 +                              }
 +                              else {
 +                                      rdata->orco = MEM_mallocN(sizeof(*rdata->orco) * rdata->vert_len, "orco mesh");
 +                                      MVert *mvert = rdata->mvert;
 +                                      for (int a = 0; a < rdata->vert_len; a++, mvert++) {
 +                                              copy_v3_v3(rdata->orco[a], mvert->co);
 +                                      }
 +                                      BKE_mesh_orco_verts_transform(me, rdata->orco, rdata->vert_len, 0);
 +                              }
 +                      }
 +              }
 +              else {
 +                      rdata->orco = NULL;
 +              }
 +
 +              /* don't access mesh directly, instead use vars taken from BMesh or Mesh */
 +#define me DONT_USE_THIS
 +#ifdef  me /* quiet warning */
 +#endif
 +              struct {
 +                      uint uv_len;
 +                      uint vcol_len;
 +              } cd_layers_src = {
 +                      .uv_len = CustomData_number_of_layers(cd_ldata, CD_MLOOPUV),
 +                      .vcol_len = CustomData_number_of_layers(cd_ldata, CD_MLOOPCOL),
 +              };
 +
 +              rdata->cd.layers.uv_len = count_bits_i(cd_lused[CD_MLOOPUV]);
 +              rdata->cd.layers.tangent_len = count_bits_i(cd_lused[CD_TANGENT]);
 +              rdata->cd.layers.vcol_len = count_bits_i(cd_lused[CD_MLOOPCOL]);
 +
 +              rdata->cd.layers.uv = MEM_mallocN(sizeof(*rdata->cd.layers.uv) * rdata->cd.layers.uv_len, __func__);
 +              rdata->cd.layers.vcol = MEM_mallocN(sizeof(*rdata->cd.layers.vcol) * rdata->cd.layers.vcol_len, __func__);
 +              rdata->cd.layers.tangent = MEM_mallocN(sizeof(*rdata->cd.layers.tangent) * rdata->cd.layers.tangent_len, __func__);
 +
 +              rdata->cd.uuid.uv = MEM_mallocN(sizeof(*rdata->cd.uuid.uv) * rdata->cd.layers.uv_len, __func__);
 +              rdata->cd.uuid.vcol = MEM_mallocN(sizeof(*rdata->cd.uuid.vcol) * rdata->cd.layers.vcol_len, __func__);
 +              rdata->cd.uuid.tangent = MEM_mallocN(sizeof(*rdata->cd.uuid.tangent) * rdata->cd.layers.tangent_len, __func__);
 +
 +              rdata->cd.offset.uv = MEM_mallocN(sizeof(*rdata->cd.offset.uv) * rdata->cd.layers.uv_len, __func__);
 +              rdata->cd.offset.vcol = MEM_mallocN(sizeof(*rdata->cd.offset.vcol) * rdata->cd.layers.vcol_len, __func__);
 +
 +              /* Allocate max */
 +              rdata->cd.layers.auto_vcol = MEM_callocN(
 +                      sizeof(*rdata->cd.layers.auto_vcol) * rdata->cd.layers.vcol_len, __func__);
 +              rdata->cd.uuid.auto_mix = MEM_mallocN(
 +                      sizeof(*rdata->cd.uuid.auto_mix) * (rdata->cd.layers.vcol_len + rdata->cd.layers.uv_len), __func__);
 +
 +              /* XXX FIXME XXX */
 +              /* We use a hash to identify each data layer based on its name.
 +               * Gawain then search for this name in the current shader and bind if it exists.
 +               * NOTE : This is prone to hash collision.
 +               * One solution to hash collision would be to format the cd layer name
 +               * to a safe glsl var name, but without name clash.
 +               * NOTE 2 : Replicate changes to code_generate_vertex_new() in gpu_codegen.c */
 +              if (rdata->cd.layers.vcol_len != 0) {
 +                      for (int i_src = 0, i_dst = 0; i_src < cd_layers_src.vcol_len; i_src++, i_dst++) {
 +                              if ((cd_lused[CD_MLOOPCOL] & (1 << i_src)) == 0) {
 +                                      i_dst--;
 +                                      if (rdata->cd.layers.vcol_active >= i_src) {
 +                                              rdata->cd.layers.vcol_active--;
 +                                      }
 +                              }
 +                              else {
 +                                      const char *name = CustomData_get_layer_name(cd_ldata, CD_MLOOPCOL, i_src);
 +                                      uint hash = BLI_ghashutil_strhash_p(name);
 +                                      BLI_snprintf(rdata->cd.uuid.vcol[i_dst], sizeof(*rdata->cd.uuid.vcol), "c%u", hash);
 +                                      rdata->cd.layers.vcol[i_dst] = CustomData_get_layer_n(cd_ldata, CD_MLOOPCOL, i_src);
 +                                      if (rdata->edit_bmesh) {
 +                                              rdata->cd.offset.vcol[i_dst] = CustomData_get_n_offset(
 +                                                      &rdata->edit_bmesh->bm->ldata, CD_MLOOPCOL, i_src);
 +                                      }
 +
 +                                      /* Gather number of auto layers. */
 +                                      /* We only do vcols that are not overridden by uvs */
 +                                      if (CustomData_get_named_layer_index(cd_ldata, CD_MLOOPUV, name) == -1) {
 +                                              BLI_snprintf(
 +                                                      rdata->cd.uuid.auto_mix[rdata->cd.layers.uv_len + i_dst],
 +                                                      sizeof(*rdata->cd.uuid.auto_mix), "a%u", hash);
 +                                              rdata->cd.layers.auto_vcol[i_dst] = true;
 +                                      }
 +                              }
 +                      }
 +              }
 +
 +              /* Start Fresh */
 +              CustomData_free_layers(cd_ldata, CD_TANGENT, rdata->loop_len);
 +              CustomData_free_layers(cd_ldata, CD_MLOOPTANGENT, rdata->loop_len);
 +
 +              if (rdata->cd.layers.uv_len != 0) {
 +                      for (int i_src = 0, i_dst = 0; i_src < cd_layers_src.uv_len; i_src++, i_dst++) {
 +                              if ((cd_lused[CD_MLOOPUV] & (1 << i_src)) == 0) {
 +                                      i_dst--;
 +                                      if (rdata->cd.layers.uv_active >= i_src) {
 +                                              rdata->cd.layers.uv_active--;
 +                                      }
 +                              }
 +                              else {
 +                                      const char *name = CustomData_get_layer_name(cd_ldata, CD_MLOOPUV, i_src);
 +                                      uint hash = BLI_ghashutil_strhash_p(name);
 +
 +                                      BLI_snprintf(rdata->cd.uuid.uv[i_dst], sizeof(*rdata->cd.uuid.uv), "u%u", hash);
 +                                      rdata->cd.layers.uv[i_dst] = CustomData_get_layer_n(cd_ldata, CD_MLOOPUV, i_src);
 +                                      if (rdata->edit_bmesh) {
 +                                              rdata->cd.offset.uv[i_dst] = CustomData_get_n_offset(
 +                                                      &rdata->edit_bmesh->bm->ldata, CD_MLOOPUV, i_src);
 +                                      }
 +                                      BLI_snprintf(rdata->cd.uuid.auto_mix[i_dst], sizeof(*rdata->cd.uuid.auto_mix), "a%u", hash);
 +                              }
 +                      }
 +              }
 +
 +              if (rdata->cd.layers.tangent_len != 0) {
 +
 +                      /* -------------------------------------------------------------------- */
 +                      /* Pre-calculate tangents into 'rdata->cd.output.ldata' */
 +
 +                      BLI_assert(!CustomData_has_layer(&rdata->cd.output.ldata, CD_TANGENT));
 +
 +                      /* Tangent Names */
 +                      char tangent_names[MAX_MTFACE][MAX_NAME];
 +                      for (int i_src = 0, i_dst = 0; i_src < cd_layers_src.uv_len; i_src++, i_dst++) {
 +                              if ((cd_lused[CD_TANGENT] & (1 << i_src)) == 0) {
 +                                      i_dst--;
 +                              }
 +                              else {
 +                                      BLI_strncpy(
 +                                              tangent_names[i_dst],
 +                                              CustomData_get_layer_name(cd_ldata, CD_MLOOPUV, i_src), MAX_NAME);
 +                              }
 +                      }
 +
 +                      /* If tangent from orco is requested, decrement tangent_len */
 +                      int actual_tangent_len = (cd_lused[CD_TANGENT] & DM_TANGENT_MASK_ORCO) ?
 +                              rdata->cd.layers.tangent_len - 1 : rdata->cd.layers.tangent_len;
 +                      if (rdata->edit_bmesh) {
 +                              BMEditMesh *em = rdata->edit_bmesh;
 +                              BMesh *bm = em->bm;
 +
 +                              if (is_auto_smooth && rdata->loop_normals == NULL) {
 +                                      /* Should we store the previous array of `loop_normals` in somewhere? */
 +                                      rdata->loop_len = bm->totloop;
 +                                      rdata->loop_normals = MEM_mallocN(sizeof(*rdata->loop_normals) * rdata->loop_len, __func__);
 +                                      BM_loops_calc_normal_vcos(bm, NULL, NULL, NULL, true, split_angle, rdata->loop_normals, NULL, NULL, -1, false);
 +                              }
 +
 +                              bool calc_active_tangent = false;
 +
 +                              BKE_editmesh_loop_tangent_calc(
 +                                      em, calc_active_tangent,
 +                                      tangent_names, actual_tangent_len,
 +                                      rdata->poly_normals, rdata->loop_normals,
 +                                      rdata->orco,
 +                                      &rdata->cd.output.ldata, bm->totloop,
 +                                      &rdata->cd.output.tangent_mask);
 +                      }
 +                      else {
 +#undef me
 +
 +                              if (is_auto_smooth && rdata->loop_normals == NULL) {
 +                                      /* Should we store the previous array of `loop_normals` in CustomData? */
 +                                      mesh_render_calc_normals_loop_and_poly(me, split_angle, rdata);
 +                              }
 +
 +                              bool calc_active_tangent = false;
 +
 +                              BKE_mesh_calc_loop_tangent_ex(
 +                                      me->mvert,
 +                                      me->mpoly, me->totpoly,
 +                                      me->mloop,
 +                                      rdata->mlooptri, rdata->tri_len,
 +                                      cd_ldata,
 +                                      calc_active_tangent,
 +                                      tangent_names, actual_tangent_len,
 +                                      rdata->poly_normals, rdata->loop_normals,
 +                                      rdata->orco,
 +                                      &rdata->cd.output.ldata, me->totloop,
 +                                      &rdata->cd.output.tangent_mask);
 +
 +                                      /* If we store tangents in the mesh, set temporary. */
 +#if 0
 +                              CustomData_set_layer_flag(cd_ldata, CD_TANGENT, CD_FLAG_TEMPORARY);
 +#endif
 +
 +#define me DONT_USE_THIS
 +#ifdef  me /* quiet warning */
 +#endif
 +                      }
 +
 +                      /* End tangent calculation */
 +                      /* -------------------------------------------------------------------- */
 +
 +                      BLI_assert(CustomData_number_of_layers(&rdata->cd.output.ldata, CD_TANGENT) == rdata->cd.layers.tangent_len);
 +
 +                      int i_dst = 0;
 +                      for (int i_src = 0; i_src < cd_layers_src.uv_len; i_src++, i_dst++) {
 +                              if ((cd_lused[CD_TANGENT] & (1 << i_src)) == 0) {
 +                                      i_dst--;
 +                                      if (rdata->cd.layers.tangent_active >= i_src) {
 +                                              rdata->cd.layers.tangent_active--;
 +                                      }
 +                              }
 +                              else {
 +                                      const char *name = CustomData_get_layer_name(cd_ldata, CD_MLOOPUV, i_src);
 +                                      uint hash = BLI_ghashutil_strhash_p(name);
 +
 +                                      BLI_snprintf(rdata->cd.uuid.tangent[i_dst], sizeof(*rdata->cd.uuid.tangent), "t%u", hash);
 +
 +                                      /* Done adding tangents. */
 +
 +                                      /* note: BKE_editmesh_loop_tangent_calc calculates 'CD_TANGENT',
 +                                       * not 'CD_MLOOPTANGENT' (as done below). It's OK, they're compatible. */
 +
 +                                      /* note: normally we'd use 'i_src' here, but 'i_dst' is in sync with 'rdata->cd.output' */
 +                                      rdata->cd.layers.tangent[i_dst] = CustomData_get_layer_n(&rdata->cd.output.ldata, CD_TANGENT, i_dst);
 +                                      if (rdata->tri_len != 0) {
 +                                              BLI_assert(rdata->cd.layers.tangent[i_dst] != NULL);
 +                                      }
 +                              }
 +                      }
 +                      if (cd_lused[CD_TANGENT] & DM_TANGENT_MASK_ORCO) {
 +                              const char *name = CustomData_get_layer_name(&rdata->cd.output.ldata, CD_TANGENT, i_dst);
 +                              uint hash = BLI_ghashutil_strhash_p(name);
 +                              BLI_snprintf(rdata->cd.uuid.tangent[i_dst], sizeof(*rdata->cd.uuid.tangent), "t%u", hash);
 +
 +                              rdata->cd.layers.tangent[i_dst] = CustomData_get_layer_n(&rdata->cd.output.ldata, CD_TANGENT, i_dst);
 +                      }
 +              }
 +
 +#undef me
 +      }
 +
 +      return rdata;
 +}
 +
 +/* Warning replace mesh pointer. */
 +#define MBC_GET_FINAL_MESH(me) \
 +      /* Hack to show the final result. */ \
 +      const bool _use_em_final = ( \
 +              (me)->edit_btmesh && \
 +              (me)->edit_btmesh->mesh_eval_final && \
 +              ((me)->edit_btmesh->mesh_eval_final->runtime.is_original == false)); \
 +      Mesh _me_fake; \
 +      if (_use_em_final) { \
 +              _me_fake = *(me)->edit_btmesh->mesh_eval_final; \
 +              _me_fake.mat = (me)->mat; \
 +              _me_fake.totcol = (me)->totcol; \
 +              (me) = &_me_fake; \
 +      } ((void)0)
 +
 +static void mesh_render_data_free(MeshRenderData *rdata)
 +{
 +      if (rdata->is_orco_allocated) {
 +              MEM_SAFE_FREE(rdata->orco);
 +      }
 +      MEM_SAFE_FREE(rdata->cd.offset.uv);
 +      MEM_SAFE_FREE(rdata->cd.offset.vcol);
 +      MEM_SAFE_FREE(rdata->cd.uuid.auto_mix);
 +      MEM_SAFE_FREE(rdata->cd.uuid.uv);
 +      MEM_SAFE_FREE(rdata->cd.uuid.vcol);
 +      MEM_SAFE_FREE(rdata->cd.uuid.tangent);
 +      MEM_SAFE_FREE(rdata->cd.layers.uv);
 +      MEM_SAFE_FREE(rdata->cd.layers.vcol);
 +      MEM_SAFE_FREE(rdata->cd.layers.tangent);
 +      MEM_SAFE_FREE(rdata->cd.layers.auto_vcol);
 +      MEM_SAFE_FREE(rdata->loose_verts);
 +      MEM_SAFE_FREE(rdata->loose_edges);
 +      MEM_SAFE_FREE(rdata->edges_adjacent_polys);
 +      MEM_SAFE_FREE(rdata->mlooptri);
 +      MEM_SAFE_FREE(rdata->loop_normals);
 +      MEM_SAFE_FREE(rdata->poly_normals);
 +      MEM_SAFE_FREE(rdata->poly_normals_pack);
 +      MEM_SAFE_FREE(rdata->vert_normals_pack);
 +      MEM_SAFE_FREE(rdata->vert_weight);
 +      MEM_SAFE_FREE(rdata->edge_select_bool);
 +      MEM_SAFE_FREE(rdata->edge_visible_bool);
 +      MEM_SAFE_FREE(rdata->vert_color);
 +
 +      MEM_SAFE_FREE(rdata->mapped.loose_verts);
 +      MEM_SAFE_FREE(rdata->mapped.loose_edges);
 +
 +      CustomData_free(&rdata->cd.output.ldata, rdata->loop_len);
 +
 +      MEM_freeN(rdata);
 +}
 +
 +static MeshRenderData *mesh_render_data_create(Mesh *me, const int types)
 +{
 +      return mesh_render_data_create_ex(me, types, NULL, 0);
 +}
 +
 +/** \} */
 +
 +/* ---------------------------------------------------------------------- */
 +
 +/** \name Accessor Functions
 + * \{ */
 +
 +static const char *mesh_render_data_uv_auto_layer_uuid_get(const MeshRenderData *rdata, int layer)
 +{
 +      BLI_assert(rdata->types & MR_DATATYPE_SHADING);
 +      return rdata->cd.uuid.auto_mix[layer];
 +}
 +
 +static const char *mesh_render_data_vcol_auto_layer_uuid_get(const MeshRenderData *rdata, int layer)
 +{
 +      BLI_assert(rdata->types & MR_DATATYPE_SHADING);
 +      return rdata->cd.uuid.auto_mix[rdata->cd.layers.uv_len + layer];
 +}
 +
 +static const char *mesh_render_data_uv_layer_uuid_get(const MeshRenderData *rdata, int layer)
 +{
 +      BLI_assert(rdata->types & MR_DATATYPE_SHADING);
 +      return rdata->cd.uuid.uv[layer];
 +}
 +
 +static const char *mesh_render_data_vcol_layer_uuid_get(const MeshRenderData *rdata, int layer)
 +{
 +      BLI_assert(rdata->types & MR_DATATYPE_SHADING);
 +      return rdata->cd.uuid.vcol[layer];
 +}
 +
 +static const char *mesh_render_data_tangent_layer_uuid_get(const MeshRenderData *rdata, int layer)
 +{
 +      BLI_assert(rdata->types & MR_DATATYPE_SHADING);
 +      return rdata->cd.uuid.tangent[layer];
 +}
 +
 +static int mesh_render_data_verts_len_get(const MeshRenderData *rdata)
 +{
 +      BLI_assert(rdata->types & MR_DATATYPE_VERT);
 +      return rdata->vert_len;
 +}
 +static int mesh_render_data_verts_len_get_maybe_mapped(const MeshRenderData *rdata)
 +{
 +      BLI_assert(rdata->types & MR_DATATYPE_VERT);
 +      return ((rdata->mapped.use == false) ? rdata->vert_len : rdata->mapped.vert_len);
 +}
 +
 +static int UNUSED_FUNCTION(mesh_render_data_loose_verts_len_get)(const MeshRenderData *rdata)
 +{
 +      BLI_assert(rdata->types & MR_DATATYPE_OVERLAY);
 +      return rdata->loose_vert_len;
 +}
 +static int mesh_render_data_loose_verts_len_get_maybe_mapped(const MeshRenderData *rdata)
 +{
 +      BLI_assert(rdata->types & MR_DATATYPE_OVERLAY);
 +      return ((rdata->mapped.use == false) ? rdata->loose_vert_len : rdata->mapped.loose_vert_len);
 +}
 +
 +static int mesh_render_data_edges_len_get(const MeshRenderData *rdata)
 +{
 +      BLI_assert(rdata->types & MR_DATATYPE_EDGE);
 +      return rdata->edge_len;
 +}
 +static int mesh_render_data_edges_len_get_maybe_mapped(const MeshRenderData *rdata)
 +{
 +      BLI_assert(rdata->types & MR_DATATYPE_EDGE);
 +      return ((rdata->mapped.use == false) ? rdata->edge_len : rdata->mapped.edge_len);
 +}
 +
 +static int UNUSED_FUNCTION(mesh_render_data_loose_edges_len_get)(const MeshRenderData *rdata)
 +{
 +      BLI_assert(rdata->types & MR_DATATYPE_OVERLAY);
 +      return rdata->loose_edge_len;
 +}
 +static int mesh_render_data_loose_edges_len_get_maybe_mapped(const MeshRenderData *rdata)
 +{
 +      BLI_assert(rdata->types & MR_DATATYPE_OVERLAY);
 +      return ((rdata->mapped.use == false) ? rdata->loose_edge_len : rdata->mapped.loose_edge_len);
 +}
 +
 +static int mesh_render_data_looptri_len_get(const MeshRenderData *rdata)
 +{
 +      BLI_assert(rdata->types & MR_DATATYPE_LOOPTRI);
 +      return rdata->tri_len;
 +}
 +static int mesh_render_data_looptri_len_get_maybe_mapped(const MeshRenderData *rdata)
 +{
 +      BLI_assert(rdata->types & MR_DATATYPE_LOOPTRI);
 +      return ((rdata->mapped.use == false) ? rdata->tri_len : rdata->mapped.tri_len);
 +}
 +
 +static int mesh_render_data_mat_len_get(const MeshRenderData *rdata)
 +{
 +      BLI_assert(rdata->types & MR_DATATYPE_POLY);
 +      return rdata->mat_len;
 +}
 +
 +static int mesh_render_data_loops_len_get(const MeshRenderData *rdata)
 +{
 +      BLI_assert(rdata->types & MR_DATATYPE_LOOP);
 +      return rdata->loop_len;
 +}
 +
 +static int mesh_render_data_polys_len_get(const MeshRenderData *rdata)
 +{
 +      BLI_assert(rdata->types & MR_DATATYPE_POLY);
 +      return rdata->poly_len;
 +}
 +static int mesh_render_data_polys_len_get_maybe_mapped(const MeshRenderData *rdata)
 +{
 +      BLI_assert(rdata->types & MR_DATATYPE_POLY);
 +      return ((rdata->mapped.use == false) ? rdata->poly_len : rdata->mapped.poly_len);
 +}
 +
 +/** \} */
 +
 +
 +/* ---------------------------------------------------------------------- */
 +
 +/* TODO remove prototype. */
 +static void mesh_create_edit_facedots(MeshRenderData *rdata, GPUVertBuf *vbo_pos_nor_data_facedots);
 +
 +/** \name Internal Cache (Lazy Initialization)
 + * \{ */
 +
 +/** Ensure #MeshRenderData.poly_normals_pack */
 +static void mesh_render_data_ensure_poly_normals_pack(MeshRenderData *rdata)
 +{
 +      GPUPackedNormal *pnors_pack = rdata->poly_normals_pack;
 +      if (pnors_pack == NULL) {
 +              if (rdata->edit_bmesh) {
 +                      BMesh *bm = rdata->edit_bmesh->bm;
 +                      BMIter fiter;
 +                      BMFace *efa;
 +                      int i;
 +
 +                      pnors_pack = rdata->poly_normals_pack = MEM_mallocN(sizeof(*pnors_pack) * rdata->poly_len, __func__);
 +                      if (rdata->edit_data && rdata->edit_data->vertexCos != NULL) {
 +                              BKE_editmesh_cache_ensure_poly_normals(rdata->edit_bmesh, rdata->edit_data);
 +                              const float (*pnors)[3] = rdata->edit_data->polyNos;
 +                              for (i = 0; i < bm->totface; i++) {
 +                                      pnors_pack[i] = GPU_normal_convert_i10_v3(pnors[i]);
 +                              }
 +                      }
 +                      else {
 +                              BM_ITER_MESH_INDEX(efa, &fiter, bm, BM_FACES_OF_MESH, i) {
 +                                      pnors_pack[i] = GPU_normal_convert_i10_v3(efa->no);
 +                              }
 +                      }
 +              }
 +              else {
 +                      float (*pnors)[3] = rdata->poly_normals;
 +
 +                      if (!pnors) {
 +                              pnors = rdata->poly_normals = MEM_mallocN(sizeof(*pnors) * rdata->poly_len, __func__);
 +                              BKE_mesh_calc_normals_poly(
 +                                      rdata->mvert, NULL, rdata->vert_len,
 +                                      rdata->mloop, rdata->mpoly, rdata->loop_len, rdata->poly_len, pnors, true);
 +                      }
 +
 +                      pnors_pack = rdata->poly_normals_pack = MEM_mallocN(sizeof(*pnors_pack) * rdata->poly_len, __func__);
 +                      for (int i = 0; i < rdata->poly_len; i++) {
 +                              pnors_pack[i] = GPU_normal_convert_i10_v3(pnors[i]);
 +                      }
 +              }
 +      }
 +}
 +
 +/** Ensure #MeshRenderData.vert_normals_pack */
 +static void mesh_render_data_ensure_vert_normals_pack(MeshRenderData *rdata)
 +{
 +      GPUPackedNormal *vnors_pack = rdata->vert_normals_pack;
 +      if (vnors_pack == NULL) {
 +              if (rdata->edit_bmesh) {
 +                      BMesh *bm = rdata->edit_bmesh->bm;
 +                      BMIter viter;
 +                      BMVert *eve;
 +                      int i;
 +
 +                      vnors_pack = rdata->vert_normals_pack = MEM_mallocN(sizeof(*vnors_pack) * rdata->vert_len, __func__);
 +                      BM_ITER_MESH_INDEX(eve, &viter, bm, BM_VERT, i) {
 +                              vnors_pack[i] = GPU_normal_convert_i10_v3(eve->no);
 +                      }
 +              }
 +              else {
 +                      /* data from mesh used directly */
 +                      BLI_assert(0);
 +              }
 +      }
 +}
 +
 +
 +/** Ensure #MeshRenderData.vert_color */
 +static void mesh_render_data_ensure_vert_color(MeshRenderData *rdata)
 +{
 +      char (*vcol)[3] = rdata->vert_color;
 +      if (vcol == NULL) {
 +              if (rdata->edit_bmesh) {
 +                      BMesh *bm = rdata->edit_bmesh->bm;
 +                      const int cd_loop_color_offset = CustomData_get_offset(&bm->ldata, CD_MLOOPCOL);
 +                      if (cd_loop_color_offset == -1) {
 +                              goto fallback;
 +                      }
 +
 +                      vcol = rdata->vert_color = MEM_mallocN(sizeof(*vcol) * rdata->loop_len, __func__);
 +
 +                      BMIter fiter;
 +                      BMFace *efa;
 +                      int i = 0;
 +
 +                      BM_ITER_MESH(efa, &fiter, bm, BM_FACES_OF_MESH) {
 +                              BMLoop *l_iter, *l_first;
 +                              l_iter = l_first = BM_FACE_FIRST_LOOP(efa);
 +                              do {
 +                                      const MLoopCol *lcol = BM_ELEM_CD_GET_VOID_P(l_iter, cd_loop_color_offset);
 +                                      vcol[i][0] = lcol->r;
 +                                      vcol[i][1] = lcol->g;
 +                                      vcol[i][2] = lcol->b;
 +                                      i += 1;
 +                              } while ((l_iter = l_iter->next) != l_first);
 +                      }
 +                      BLI_assert(i == rdata->loop_len);
 +              }
 +              else {
 +                      if (rdata->mloopcol == NULL) {
 +                              goto fallback;
 +                      }
 +
 +                      vcol = rdata->vert_color = MEM_mallocN(sizeof(*vcol) * rdata->loop_len, __func__);
 +
 +                      for (int i = 0; i < rdata->loop_len; i++) {
 +                              vcol[i][0] = rdata->mloopcol[i].r;
 +                              vcol[i][1] = rdata->mloopcol[i].g;
 +                              vcol[i][2] = rdata->mloopcol[i].b;
 +                      }
 +              }
 +      }
 +      return;
 +
 +fallback:
 +      vcol = rdata->vert_color = MEM_mallocN(sizeof(*vcol) * rdata->loop_len, __func__);
 +
 +      for (int i = 0; i < rdata->loop_len; i++) {
 +              vcol[i][0] = 255;
 +              vcol[i][1] = 255;
 +              vcol[i][2] = 255;
 +      }
 +}
 +
 +static float evaluate_vertex_weight(const MDeformVert *dvert, const struct DRW_MeshWeightState *wstate)
 +{
 +      float input = 0.0f;
 +      bool show_alert_color = false;
 +
 +      if (wstate->flags & DRW_MESH_WEIGHT_STATE_MULTIPAINT) {
 +              /* Multi-Paint feature */
 +              input = BKE_defvert_multipaint_collective_weight(
 +                      dvert, wstate->defgroup_len, wstate->defgroup_sel, wstate->defgroup_sel_count,
 +                      (wstate->flags & DRW_MESH_WEIGHT_STATE_AUTO_NORMALIZE) != 0);
 +
 +              /* make it black if the selected groups have no weight on a vertex */
 +              if (input == 0.0f) {
 +                      show_alert_color = true;
 +              }
 +      }
 +      else {
 +              /* default, non tricky behavior */
 +              input = defvert_find_weight(dvert, wstate->defgroup_active);
 +
 +              if (input == 0.0f) {
 +                      switch (wstate->alert_mode) {
 +                              case OB_DRAW_GROUPUSER_ACTIVE:
 +                                      show_alert_color = true;
 +                                      break;
 +
 +                              case OB_DRAW_GROUPUSER_ALL:
 +                                      show_alert_color = defvert_is_weight_zero(dvert, wstate->defgroup_len);
 +                                      break;
 +                      }
 +              }
 +      }
 +
 +      if (show_alert_color) {
 +              return -1.0f;
 +      }
 +      else {
 +              CLAMP(input, 0.0f, 1.0f);
 +              return input;
 +      }
 +}
 +
 +/** Ensure #MeshRenderData.vert_weight */
 +static void mesh_render_data_ensure_vert_weight(MeshRenderData *rdata, const struct DRW_MeshWeightState *wstate)
 +{
 +      float (*vweight) = rdata->vert_weight;
 +      if (vweight == NULL) {
 +              if (wstate->defgroup_active == -1) {
 +                      goto fallback;
 +              }
 +
 +              if (rdata->edit_bmesh) {
 +                      BMesh *bm = rdata->edit_bmesh->bm;
 +                      const int cd_dvert_offset = CustomData_get_offset(&bm->vdata, CD_MDEFORMVERT);
 +                      if (cd_dvert_offset == -1) {
 +                              goto fallback;
 +                      }
 +
 +                      BMIter viter;
 +                      BMVert *eve;
 +                      int i;
 +
 +                      vweight = rdata->vert_weight = MEM_mallocN(sizeof(*vweight) * rdata->vert_len, __func__);
 +                      BM_ITER_MESH_INDEX(eve, &viter, bm, BM_VERT, i) {
 +                              const MDeformVert *dvert = BM_ELEM_CD_GET_VOID_P(eve, cd_dvert_offset);
 +                              vweight[i] = evaluate_vertex_weight(dvert, wstate);
 +                      }
 +              }
 +              else {
 +                      if (rdata->dvert == NULL) {
 +                              goto fallback;
 +                      }
 +
 +                      vweight = rdata->vert_weight = MEM_mallocN(sizeof(*vweight) * rdata->vert_len, __func__);
 +                      for (int i = 0; i < rdata->vert_len; i++) {
 +                              vweight[i] = evaluate_vertex_weight(&rdata->dvert[i], wstate);
 +                      }
 +              }
 +      }
 +      return;
 +
 +fallback:
 +      vweight = rdata->vert_weight = MEM_callocN(sizeof(*vweight) * rdata->vert_len, __func__);
 +
 +      if ((wstate->defgroup_active < 0) && (wstate->defgroup_len > 0)) {
 +              copy_vn_fl(vweight, rdata->vert_len, -2.0f);
 +      }
 +      else if (wstate->alert_mode != OB_DRAW_GROUPUSER_NONE) {
 +              copy_vn_fl(vweight, rdata->vert_len, -1.0f);
 +      }
 +}
 +
 +
 +/** Ensure #MeshRenderData.edge_select_bool */
 +static void mesh_render_data_ensure_edge_select_bool(MeshRenderData *rdata, bool use_wire)
 +{
 +      bool *edge_select_bool = rdata->edge_select_bool;
 +      if (edge_select_bool == NULL) {
 +              edge_select_bool = rdata->edge_select_bool =
 +                      MEM_callocN(sizeof(*edge_select_bool) * rdata->edge_len, __func__);
 +
 +              for (int i = 0; i < rdata->poly_len; i++) {
 +                      const MPoly *poly = &rdata->mpoly[i];
 +
 +                      if (poly->flag & ME_FACE_SEL) {
 +                              for (int j = 0; j < poly->totloop; j++) {
 +                                      const MLoop *loop = &rdata->mloop[poly->loopstart + j];
 +                                      if (use_wire) {
 +                                              edge_select_bool[loop->e] = true;
 +                                      }
 +                                      else {
 +                                              /* Not totally correct, will cause problems for edges with 3x faces. */
 +                                              edge_select_bool[loop->e] = !edge_select_bool[loop->e];
 +                                      }
 +                              }
 +                      }
 +              }
 +      }
 +}
 +
 +/** Ensure #MeshRenderData.edge_visible_bool */
 +static void mesh_render_data_ensure_edge_visible_bool(MeshRenderData *rdata)
 +{
 +      bool *edge_visible_bool = rdata->edge_visible_bool;
 +      if (edge_visible_bool == NULL) {
 +              edge_visible_bool = rdata->edge_visible_bool =
 +                      MEM_callocN(sizeof(*edge_visible_bool) * rdata->edge_len, __func__);
 +
 +              /* If original index is available, hide edges within the same original poly. */
 +              const int *p_origindex = NULL;
 +              int *index_table = NULL;
 +
 +              if (rdata->me != NULL) {
 +                      p_origindex = CustomData_get_layer(&rdata->me->pdata, CD_ORIGINDEX);
 +                      if (p_origindex != NULL) {
 +                              index_table = MEM_malloc_arrayN(sizeof(int), rdata->edge_len, __func__);
 +                              memset(index_table, -1, sizeof(int) * rdata->edge_len);
 +                      }
 +              }
 +
 +              for (int i = 0; i < rdata->poly_len; i++) {
 +                      const MPoly *poly = &rdata->mpoly[i];
 +                      int p_orig = p_origindex ? p_origindex[i] : ORIGINDEX_NONE;
 +
 +                      if (!(poly->flag & ME_HIDE)) {
 +                              for (int j = 0; j < poly->totloop; j++) {
 +                                      const MLoop *loop = &rdata->mloop[poly->loopstart + j];
 +
 +                                      if (p_orig != ORIGINDEX_NONE) {
 +                                              /* Boundary edge is visible. */
 +                                              if (index_table[loop->e] == -1) {
 +                                                      index_table[loop->e] = p_orig;
 +                                                      edge_visible_bool[loop->e] = true;
 +                                              }
 +                                              /* Edge between two faces with the same original is hidden. */
 +                                              else if (index_table[loop->e] == p_orig) {
 +                                                      edge_visible_bool[loop->e] = false;
 +                                              }
 +                                              /* Edge between two different original faces is visible. */
 +                                              else {
 +                                                      index_table[loop->e] = -2;
 +                                                      edge_visible_bool[loop->e] = true;
 +                                              }
 +                                      }
 +                                      else {
 +                                              if (index_table != NULL) {
 +                                                      index_table[loop->e] = -2;
 +                                              }
 +                                              edge_visible_bool[loop->e] = true;
 +                                      }
 +                              }
 +                      }
 +              }
 +
 +              if (index_table != NULL) {
 +                      MEM_freeN(index_table);
 +              }
 +      }
 +}
 +
 +/** \} */
 +
 +/* ---------------------------------------------------------------------- */
 +
 +/** \name Internal Cache Generation
 + * \{ */
 +
 +static bool mesh_render_data_edge_vcos_manifold_pnors(
 +        MeshRenderData *rdata, const int edge_index,
 +        float **r_vco1, float **r_vco2, float **r_pnor1, float **r_pnor2, bool *r_is_manifold)
 +{
 +      BLI_assert(rdata->types & (MR_DATATYPE_VERT | MR_DATATYPE_EDGE | MR_DATATYPE_LOOP | MR_DATATYPE_POLY));
 +
 +      if (rdata->edit_bmesh) {
 +              BMesh *bm = rdata->edit_bmesh->bm;
 +              BMEdge *eed = BM_edge_at_index(bm, edge_index);
 +              if (BM_elem_flag_test(eed, BM_ELEM_HIDDEN)) {
 +                      return false;
 +              }
 +              *r_vco1 = eed->v1->co;
 +              *r_vco2 = eed->v2->co;
 +              if (BM_edge_is_manifold(eed)) {
 +                      *r_pnor1 = eed->l->f->no;
 +                      *r_pnor2 = eed->l->radial_next->f->no;
 +                      *r_is_manifold = true;
 +              }
 +              else if (eed->l != NULL) {
 +                      *r_pnor1 = eed->l->f->no;
 +                      *r_pnor2 = eed->l->f->no;
 +                      *r_is_manifold = false;
 +              }
 +              else {
 +                      *r_pnor1 = eed->v1->no;
 +                      *r_pnor2 = eed->v1->no;
 +                      *r_is_manifold = false;
 +              }
 +      }
 +      else {
 +              MVert *mvert = rdata->mvert;
 +              const MEdge *medge = rdata->medge;
 +              EdgeAdjacentPolys *eap = rdata->edges_adjacent_polys;
 +              float (*pnors)[3] = rdata->poly_normals;
 +
 +              if (!eap) {
 +                      const MLoop *mloop = rdata->mloop;
 +                      const MPoly *mpoly = rdata->mpoly;
 +                      const int poly_len = rdata->poly_len;
 +                      const bool do_pnors = (poly_len != 0 && pnors == NULL);
 +
 +                      eap = rdata->edges_adjacent_polys = MEM_mallocN(sizeof(*eap) * rdata->edge_len, __func__);
 +                      for (int i = 0; i < rdata->edge_len; i++) {
 +                              eap[i].count = 0;
 +                              eap[i].face_index[0] = -1;
 +                              eap[i].face_index[1] = -1;
 +                      }
 +                      if (do_pnors) {
 +                              pnors = rdata->poly_normals = MEM_mallocN(sizeof(*pnors) * poly_len, __func__);
 +                      }
 +
 +                      for (int i = 0; i < poly_len; i++, mpoly++) {
 +                              if (do_pnors) {
 +                                      BKE_mesh_calc_poly_normal(mpoly, mloop + mpoly->loopstart, mvert, pnors[i]);
 +                              }
 +
 +                              const int loopend = mpoly->loopstart + mpoly->totloop;
 +                              for (int j = mpoly->loopstart; j < loopend; j++) {
 +                                      const int edge_idx = mloop[j].e;
 +                                      if (eap[edge_idx].count < 2) {
 +                                              eap[edge_idx].face_index[eap[edge_idx].count] = i;
 +                                      }
 +                                      eap[edge_idx].count++;
 +                              }
 +                      }
 +              }
 +              BLI_assert(eap && (rdata->poly_len == 0 || pnors != NULL));
 +
 +              *r_vco1 = mvert[medge[edge_index].v1].co;
 +              *r_vco2 = mvert[medge[edge_index].v2].co;
 +              if (eap[edge_index].face_index[0] == -1) {
 +                      /* Edge has no poly... */
 +                      *r_pnor1 = *r_pnor2 = mvert[medge[edge_index].v1].co; /* XXX mvert.no are shorts... :( */
 +                      *r_is_manifold = false;
 +              }
 +              else {
 +                      *r_pnor1 = pnors[eap[edge_index].face_index[0]];
 +
 +                      float nor[3], v1[3], v2[3], r_center[3];
 +                      const MPoly *mpoly = rdata->mpoly + eap[edge_index].face_index[0];
 +                      const MLoop *mloop = rdata->mloop + mpoly->loopstart;
 +
 +                      BKE_mesh_calc_poly_center(mpoly, mloop, mvert, r_center);
 +                      sub_v3_v3v3(v1, *r_vco2, *r_vco1);
 +                      sub_v3_v3v3(v2, r_center, *r_vco1);
 +                      cross_v3_v3v3(nor, v1, v2);
 +
 +                      if (dot_v3v3(nor, *r_pnor1) < 0.0) {
 +                              SWAP(float *, *r_vco1, *r_vco2);
 +                      }
 +
 +                      if (eap[edge_index].count == 2) {
 +                              BLI_assert(eap[edge_index].face_index[1] >= 0);
 +                              *r_pnor2 = pnors[eap[edge_index].face_index[1]];
 +                              *r_is_manifold = true;
 +                      }
 +                      else {
 +                              *r_pnor2 = pnors[eap[edge_index].face_index[0]];
 +                              *r_is_manifold = false;
 +                      }
 +              }
 +      }
 +
 +      return true;
 +}
 +
 +static uchar mesh_render_data_looptri_flag(MeshRenderData *rdata, const BMFace *efa)
 +{
 +      uchar fflag = 0;
 +
 +      if (efa == rdata->efa_act)
 +              fflag |= VFLAG_FACE_ACTIVE;
 +
 +      if (BM_elem_flag_test(efa, BM_ELEM_SELECT))
 +              fflag |= VFLAG_FACE_SELECTED;
 +
 +#ifdef WITH_FREESTYLE
 +      if (rdata->cd.offset.freestyle_face != -1) {
 +              const FreestyleFace *ffa = BM_ELEM_CD_GET_VOID_P(efa, rdata->cd.offset.freestyle_face);
 +              if (ffa->flag & FREESTYLE_FACE_MARK) {
 +                      fflag |= VFLAG_FACE_FREESTYLE;
 +              }
 +      }
 +#endif
 +
 +      return fflag;
 +}
 +
 +static void mesh_render_data_edge_flag(
 +        const MeshRenderData *rdata, const BMEdge *eed,
 +        EdgeDrawAttr *eattr)
 +{
 +      eattr->e_flag |= VFLAG_EDGE_EXISTS;
 +
 +      if (eed == rdata->eed_act)
 +              eattr->e_flag |= VFLAG_EDGE_ACTIVE;
 +
 +      if (BM_elem_flag_test(eed, BM_ELEM_SELECT))
 +              eattr->e_flag |= VFLAG_EDGE_SELECTED;
 +
 +      if (BM_elem_flag_test(eed, BM_ELEM_SEAM))
 +              eattr->e_flag |= VFLAG_EDGE_SEAM;
 +
 +      if (!BM_elem_flag_test(eed, BM_ELEM_SMOOTH))
 +              eattr->e_flag |= VFLAG_EDGE_SHARP;
 +
 +      /* Use a byte for value range */
 +      if (rdata->cd.offset.crease != -1) {
 +              float crease = BM_ELEM_CD_GET_FLOAT(eed, rdata->cd.offset.crease);
 +              if (crease > 0) {
 +                      eattr->crease = (uchar)(crease * 255.0f);
 +              }
 +      }
 +
 +      /* Use a byte for value range */
 +      if (rdata->cd.offset.bweight != -1) {
 +              float bweight = BM_ELEM_CD_GET_FLOAT(eed, rdata->cd.offset.bweight);
 +              if (bweight > 0) {
 +                      eattr->bweight = (uchar)(bweight * 255.0f);
 +              }
 +      }
 +
 +#ifdef WITH_FREESTYLE
 +      if (rdata->cd.offset.freestyle_edge != -1) {
 +              const FreestyleEdge *fed = BM_ELEM_CD_GET_VOID_P(eed, rdata->cd.offset.freestyle_edge);
 +              if (fed->flag & FREESTYLE_EDGE_MARK) {
 +                      eattr->e_flag |= VFLAG_EDGE_FREESTYLE;
 +              }
 +      }
 +#endif
 +}
 +
 +static uchar mesh_render_data_vertex_flag(MeshRenderData *rdata, const BMVert *eve)
 +{
 +      uchar vflag = VFLAG_VERTEX_EXISTS;
 +
 +      /* Current vertex */
 +      if (eve == rdata->eve_act)
 +              vflag |= VFLAG_VERTEX_ACTIVE;
 +
 +      if (BM_elem_flag_test(eve, BM_ELEM_SELECT))
 +              vflag |= VFLAG_VERTEX_SELECTED;
 +
 +      return vflag;
 +}
 +
 +static void add_edit_tri(
 +        MeshRenderData *rdata, GPUVertBuf *vbo_pos_nor, GPUVertBuf *vbo_lnor, GPUVertBuf *vbo_data, GPUIndexBufBuilder *elb,
 +        const uint pos_id, const uint vnor_id, const uint lnor_id, const uint data_id,
 +        const BMLoop **bm_looptri, const int base_vert_idx)
 +{
 +      uchar fflag;
 +      uchar vflag;
 +
 +      /* Only draw vertices once. */
 +      if (elb) {
 +              for (int i = 0; i < 3; ++i) {
 +                      if (!BM_elem_flag_test(bm_looptri[i]->v, BM_ELEM_TAG)) {
 +                              BM_elem_flag_enable(bm_looptri[i]->v, BM_ELEM_TAG);
 +                              GPU_indexbuf_add_generic_vert(elb, base_vert_idx + i);
 +                      }
 +              }
 +      }
 +
 +      if (vbo_pos_nor) {
 +              /* TODO(sybren): deduplicate this and all the other places it's pasted to in this file. */
 +              if (rdata->edit_data && rdata->edit_data->vertexCos) {
 +                      for (uint i = 0; i < 3; i++) {
 +                              int vidx = BM_elem_index_get(bm_looptri[i]->v);
 +                              const float *pos = rdata->edit_data->vertexCos[vidx];
 +                              GPU_vertbuf_attr_set(vbo_pos_nor, pos_id, base_vert_idx + i, pos);
 +                      }
 +              }
 +              else {
 +                      for (uint i = 0; i < 3; i++) {
 +                              const float *pos = bm_looptri[i]->v->co;
 +                              GPU_vertbuf_attr_set(vbo_pos_nor, pos_id, base_vert_idx + i, pos);
 +                      }
 +              }
 +
 +              for (uint i = 0; i < 3; i++) {
 +                      GPUPackedNormal vnor = GPU_normal_convert_i10_v3(bm_looptri[i]->v->no);
 +                      GPU_vertbuf_attr_set(vbo_pos_nor, vnor_id, base_vert_idx + i, &vnor);
 +              }
 +      }
 +
 +      if (vbo_lnor) {
 +              float (*lnors)[3] = rdata->loop_normals;
 +              for (uint i = 0; i < 3; i++) {
 +                      const float *nor = (lnors) ? lnors[BM_elem_index_get(bm_looptri[i])] : bm_looptri[0]->f->no;
 +                      GPUPackedNormal lnor = GPU_normal_convert_i10_v3(nor);
 +                      GPU_vertbuf_attr_set(vbo_lnor, lnor_id, base_vert_idx + i, &lnor);
 +              }
 +      }
 +
 +      if (vbo_data) {
 +              fflag = mesh_render_data_looptri_flag(rdata, bm_looptri[0]->f);
 +              for (uint i = 0; i < 3; i++) {
 +                      const int i_next = (i + 1) % 3;
 +                      const int i_prev = (i + 2) % 3;
 +                      vflag = mesh_render_data_vertex_flag(rdata, bm_looptri[i]->v);
 +                      /* Opposite edge to the vertex at 'i'. */
 +                      EdgeDrawAttr eattr = {0};
 +                      const bool is_edge_real = (bm_looptri[i_next] == bm_looptri[i_prev]->prev);
 +                      if (is_edge_real) {
 +                              mesh_render_data_edge_flag(rdata, bm_looptri[i_next]->e, &eattr);
 +                      }
 +                      eattr.v_flag = fflag | vflag;
 +                      GPU_vertbuf_attr_set(vbo_data, data_id, base_vert_idx + i, &eattr);
 +              }
 +      }
 +}
 +static bool add_edit_tri_mapped(
 +        MeshRenderData *rdata, GPUVertBuf *vbo_pos_nor, GPUVertBuf *vbo_lnor, GPUVertBuf *vbo_data, GPUIndexBufBuilder *elb,
 +        const uint pos_id, const uint vnor_id, const uint lnor_id, const uint data_id,
 +        BMFace *efa, const MLoopTri *mlt, const float (*poly_normals)[3], const float (*loop_normals)[3], const int base_vert_idx)
 +{
 +      if (BM_elem_flag_test(efa, BM_ELEM_HIDDEN)) {
 +              return false;
 +      }
 +
 +      BMEditMesh *embm = rdata->edit_bmesh;
 +      BMesh *bm = embm->bm;
 +      Mesh *me_cage = embm->mesh_eval_cage;
 +
 +      const MVert *mvert = me_cage->mvert;
 +      const MEdge *medge = me_cage->medge;
 +      const MLoop *mloop = me_cage->mloop;
 +
 +      const int *v_origindex = rdata->mapped.v_origindex;
 +      const int *e_origindex = rdata->mapped.e_origindex;
 +
 +      if (elb) {
 +              for (int i = 0; i < 3; ++i) {
 +                      const int v_orig = v_origindex[mloop[mlt->tri[i]].v];
 +                      if (v_orig == ORIGINDEX_NONE) {
 +                              continue;
 +                      }
 +                      BMVert *v = BM_vert_at_index(bm, v_orig);
 +                      if (!BM_elem_flag_test(v, BM_ELEM_TAG)) {
 +                              BM_elem_flag_enable(v, BM_ELEM_TAG);
 +                              GPU_indexbuf_add_generic_vert(elb, base_vert_idx + i);
 +                      }
 +              }
 +      }
 +
 +      if (vbo_pos_nor) {
 +              for (uint i = 0; i < 3; i++) {
 +                      const float *pos = mvert[mloop[mlt->tri[i]].v].co;
 +                      GPUPackedNormal vnor = GPU_normal_convert_i10_s3(mvert[mloop[mlt->tri[i]].v].no);
 +                      GPU_vertbuf_attr_set(vbo_pos_nor, pos_id, base_vert_idx + i, pos);
 +                      GPU_vertbuf_attr_set(vbo_pos_nor, vnor_id, base_vert_idx + i, &vnor);
 +              }
 +      }
 +
 +      if (vbo_lnor) {
 +              for (uint i = 0; i < 3; i++) {
 +                      const float *nor = loop_normals ? loop_normals[mlt->tri[i]] : poly_normals[mlt->poly];
 +                      GPUPackedNormal lnor = GPU_normal_convert_i10_v3(nor);
 +                      GPU_vertbuf_attr_set(vbo_lnor, lnor_id, base_vert_idx + i, &lnor);
 +              }
 +      }
 +
 +      if (vbo_data) {
 +              EdgeDrawAttr eattr[3] = {{0}}; /* Importantly VFLAG_VERTEX_EXISTS is not set. */
 +              uchar fflag = mesh_render_data_looptri_flag(rdata, efa);
 +              for (uint i = 0; i < 3; i++) {
 +                      const int i_next = (i + 1) % 3;
 +                      const int i_prev = (i + 2) % 3;
 +                      const int v_orig = v_origindex[mloop[mlt->tri[i]].v];
 +                      if (v_orig != ORIGINDEX_NONE) {
 +                              BMVert *v = BM_vert_at_index(bm, v_orig);
 +                              eattr[i].v_flag |= mesh_render_data_vertex_flag(rdata, v);
 +                      }
 +                      /* Opposite edge to the vertex at 'i'. */
 +                      const int e_idx = mloop[mlt->tri[i_next]].e;
 +                      const int e_orig = e_origindex[e_idx];
 +                      if (e_orig != ORIGINDEX_NONE) {
 +                              const MEdge *ed = &medge[e_idx];
 +                              const uint tri_edge[2]  = {mloop[mlt->tri[i_prev]].v, mloop[mlt->tri[i_next]].v};
 +                              const bool is_edge_real = (
 +                                      ((ed->v1 == tri_edge[0]) && (ed->v2 == tri_edge[1])) ||
 +                                      ((ed->v1 == tri_edge[1]) && (ed->v2 == tri_edge[0])));
 +                              if (is_edge_real) {
 +                                      BMEdge *eed = BM_edge_at_index(bm, e_orig);
 +                                      mesh_render_data_edge_flag(rdata, eed, &eattr[i]);
 +                                      /* Set vertex selected if both original verts are selected. */
 +                                      if (BM_elem_flag_test(eed->v1, BM_ELEM_SELECT) &&
 +                                          BM_elem_flag_test(eed->v2, BM_ELEM_SELECT))
 +                                      {
 +                                              eattr[i_next].v_flag |= VFLAG_VERTEX_SELECTED;
 +                                              eattr[i_prev].v_flag |= VFLAG_VERTEX_SELECTED;
 +                                      }
 +                              }
 +                      }
 +              }
 +              for (uint i = 0; i < 3; i++) {
 +                      eattr[i].v_flag |= fflag;
 +                      GPU_vertbuf_attr_set(vbo_data, data_id, base_vert_idx + i, &eattr[i]);
 +              }
 +      }
 +
 +      return true;
 +}
 +
 +static void add_edit_loose_edge(
 +        MeshRenderData *rdata, GPUVertBuf *vbo_pos_nor, GPUVertBuf *vbo_data,
 +        const uint pos_id, const uint vnor_id, const uint data_id,
 +        const BMEdge *eed, const int base_vert_idx)
 +{
 +      if (vbo_pos_nor) {
 +              /* TODO(sybren): deduplicate this and all the other places it's pasted to in this file. */
 +              if (rdata->edit_data && rdata->edit_data->vertexCos) {
 +                      for (uint i = 0; i < 2; i++) {
 +                              int vidx = BM_elem_index_get((&eed->v1)[i]);
 +                              const float *pos = rdata->edit_data->vertexCos[vidx];
 +                              GPU_vertbuf_attr_set(vbo_pos_nor, pos_id, base_vert_idx + i, pos);
 +                      }
 +              }
 +              else {
 +                      for (int i = 0; i < 2; i++) {
 +                              const float *pos = (&eed->v1)[i]->co;
 +                              GPU_vertbuf_attr_set(vbo_pos_nor, pos_id, base_vert_idx + i, pos);
 +                      }
 +              }
 +
 +              for (int i = 0; i < 2; i++) {
 +                      GPUPackedNormal vnor = GPU_normal_convert_i10_v3((&eed->v1)[i]->no);
 +                      GPU_vertbuf_attr_set(vbo_pos_nor, vnor_id, base_vert_idx + i, &vnor);
 +              }
 +      }
 +
 +      if (vbo_data) {
 +              EdgeDrawAttr eattr = {0};
 +              mesh_render_data_edge_flag(rdata, eed, &eattr);
 +              for (int i = 0; i < 2; i++) {
 +                      eattr.v_flag = mesh_render_data_vertex_flag(rdata, (&eed->v1)[i]);
 +                      GPU_vertbuf_attr_set(vbo_data, data_id, base_vert_idx + i, &eattr);
 +              }
 +      }
 +}
 +static void add_edit_loose_edge_mapped(
 +        MeshRenderData *rdata, GPUVertBuf *vbo_pos_nor, GPUVertBuf *vbo_data,
 +        const uint pos_id, const uint vnor_id, const uint data_id,
 +        BMEdge *eed, const MVert *mvert, const MEdge *ed, const int base_vert_idx)
 +{
 +      if (vbo_pos_nor) {
 +              /* TODO(sybren): deduplicate this and all the other places it's pasted to in this file. */
 +              for (int i = 0; i < 2; i++) {
 +                      const float *pos = mvert[*(&ed->v1 + i)].co;
 +                      GPU_vertbuf_attr_set(vbo_pos_nor, pos_id, base_vert_idx + i, pos);
 +
 +                      GPUPackedNormal vnor = GPU_normal_convert_i10_s3(mvert[*(&ed->v1 + i)].no);
 +                      GPU_vertbuf_attr_set(vbo_pos_nor, vnor_id, base_vert_idx + i, &vnor);
 +              }
 +      }
 +
 +      if (vbo_data) {
 +              EdgeDrawAttr eattr = {0};
 +              mesh_render_data_edge_flag(rdata, eed, &eattr);
 +              for (int i = 0; i < 2; i++) {
 +                      const int v_orig = rdata->mapped.v_origindex[*(&ed->v1 + i)];
 +                      eattr.v_flag = (v_orig != ORIGINDEX_NONE) ? mesh_render_data_vertex_flag(rdata, (&eed->v1)[i]) : 0;
 +                      GPU_vertbuf_attr_set(vbo_data, data_id, base_vert_idx + i, &eattr);
 +              }
 +      }
 +}
 +
 +static void add_edit_loose_vert(
 +        MeshRenderData *rdata, GPUVertBuf *vbo_pos_nor, GPUVertBuf *vbo_data,
 +        const uint pos_id, const uint vnor_id, const uint data_id,
 +        const BMVert *eve, const int base_vert_idx)
 +{
 +      if (vbo_pos_nor) {
 +              /* TODO(sybren): deduplicate this and all the other places it's pasted to in this file. */
 +              if (rdata->edit_data && rdata->edit_data->vertexCos) {
 +                      int vidx = BM_elem_index_get(eve);
 +                      const float *pos = rdata->edit_data->vertexCos[vidx];
 +                      GPU_vertbuf_attr_set(vbo_pos_nor, pos_id, base_vert_idx, pos);
 +              }
 +              else {
 +                      const float *pos = eve->co;
 +                      GPU_vertbuf_attr_set(vbo_pos_nor, pos_id, base_vert_idx, pos);
 +              }
 +
 +              GPUPackedNormal vnor = GPU_normal_convert_i10_v3(eve->no);
 +              GPU_vertbuf_attr_set(vbo_pos_nor, vnor_id, base_vert_idx, &vnor);
 +      }
 +
 +      if (vbo_data) {
 +              uchar vflag[4] = {0, 0, 0, 0};
 +              vflag[0] = mesh_render_data_vertex_flag(rdata, eve);
 +              GPU_vertbuf_attr_set(vbo_data, data_id, base_vert_idx, vflag);
 +      }
 +}
 +static void add_edit_loose_vert_mapped(
 +        MeshRenderData *rdata, GPUVertBuf *vbo_pos_nor, GPUVertBuf *vbo_data,
 +        const uint pos_id, const uint vnor_id, const uint data_id,
 +        const BMVert *eve, const MVert *mv, const int base_vert_idx)
 +{
 +      if (vbo_pos_nor) {
 +              const float *pos = mv->co;
 +              GPU_vertbuf_attr_set(vbo_pos_nor, pos_id, base_vert_idx, pos);
 +
 +              GPUPackedNormal vnor = GPU_normal_convert_i10_s3(mv->no);
 +              GPU_vertbuf_attr_set(vbo_pos_nor, vnor_id, base_vert_idx, &vnor);
 +      }
 +
 +      if (vbo_data) {
 +              uchar vflag[4] = {0, 0, 0, 0};
 +              vflag[0] = mesh_render_data_vertex_flag(rdata, eve);
 +              GPU_vertbuf_attr_set(vbo_data, data_id, base_vert_idx, vflag);
 +      }
 +}
 +
 +static bool add_edit_facedot(
 +        MeshRenderData *rdata, GPUVertBuf *vbo,
 +        const uint fdot_pos_id, const uint fdot_nor_flag_id,
 +        const int poly, const int base_vert_idx)
 +{
 +      BLI_assert(rdata->types & (MR_DATATYPE_VERT | MR_DATATYPE_LOOP | MR_DATATYPE_POLY));
 +      float pnor[3], center[3];
 +      bool selected;
 +      if (rdata->edit_bmesh) {
 +              const BMFace *efa = BM_face_at_index(rdata->edit_bmesh->bm, poly);
 +              if (BM_elem_flag_test(efa, BM_ELEM_HIDDEN)) {
 +                      return false;
 +              }
 +              if (rdata->edit_data && rdata->edit_data->vertexCos) {
 +                      copy_v3_v3(center, rdata->edit_data->polyCos[poly]);
 +                      copy_v3_v3(pnor, rdata->edit_data->polyNos[poly]);
 +              }
 +              else {
++                      BM_face_calc_center_median(efa, center);
 +                      copy_v3_v3(pnor, efa->no);
 +              }
 +              selected = (BM_elem_flag_test(efa, BM_ELEM_SELECT) != 0) ? true : false;
 +      }
 +      else {
 +              MVert *mvert = rdata->mvert;
 +              const MPoly *mpoly = rdata->mpoly + poly;
 +              const MLoop *mloop = rdata->mloop + mpoly->loopstart;
 +
 +              BKE_mesh_calc_poly_center(mpoly, mloop, mvert, center);
 +              BKE_mesh_calc_poly_normal(mpoly, mloop, mvert, pnor);
 +
 +              selected = false; /* No selection if not in edit mode */
 +      }
 +
 +      GPUPackedNormal nor = GPU_normal_convert_i10_v3(pnor);
 +      nor.w = (selected) ? 1 : 0;
 +      GPU_vertbuf_attr_set(vbo, fdot_nor_flag_id, base_vert_idx, &nor);
 +      GPU_vertbuf_attr_set(vbo, fdot_pos_id, base_vert_idx, center);
 +
 +      return true;
 +}
 +static bool add_edit_facedot_mapped(
 +        MeshRenderData *rdata, GPUVertBuf *vbo,
 +        const uint fdot_pos_id, const uint fdot_nor_flag_id,
 +        const int poly, const int base_vert_idx)
 +{
 +      BLI_assert(rdata->types & (MR_DATATYPE_VERT | MR_DATATYPE_LOOP | MR_DATATYPE_POLY));
 +      float pnor[3], center[3];
 +      const int *p_origindex = rdata->mapped.p_origindex;
 +      const int p_orig = p_origindex[poly];
 +      if (p_orig == ORIGINDEX_NONE) {
 +              return false;
 +      }
 +      BMEditMesh *em = rdata->edit_bmesh;
 +      const BMFace *efa = BM_face_at_index(rdata->edit_bmesh->bm, p_orig);
 +      if (BM_elem_flag_test(efa, BM_ELEM_HIDDEN)) {
 +              return false;
 +      }
 +
 +      Mesh *me_cage = em->mesh_eval_cage;
 +      const MVert *mvert = me_cage->mvert;
 +      const MLoop *mloop = me_cage->mloop;
 +      const MPoly *mpoly = me_cage->mpoly;
 +
 +      const MPoly *mp = mpoly + poly;
 +      const MLoop *ml = mloop + mp->loopstart;
 +
 +      BKE_mesh_calc_poly_center(mp, ml, mvert, center);
 +      BKE_mesh_calc_poly_normal(mp, ml, mvert, pnor);
 +
 +      GPUPackedNormal nor = GPU_normal_convert_i10_v3(pnor);
 +      nor.w = (BM_elem_flag_test(efa, BM_ELEM_SELECT) != 0) ? 1 : 0;
 +      GPU_vertbuf_attr_set(vbo, fdot_nor_flag_id, base_vert_idx, &nor);
 +      GPU_vertbuf_attr_set(vbo, fdot_pos_id, base_vert_idx, center);
 +
 +      return true;
 +}
 +
 +/** \} */
 +
 +/* ---------------------------------------------------------------------- */
 +
 +/** \name Vertex Group Selection
 + * \{ */
 +
 +/** Reset the selection structure, deallocating heap memory as appropriate. */
 +void DRW_mesh_weight_state_clear(struct DRW_MeshWeightState *wstate)
 +{
 +      MEM_SAFE_FREE(wstate->defgroup_sel);
 +
 +      memset(wstate, 0, sizeof(*wstate));
 +
 +      wstate->defgroup_active = -1;
 +}
 +
 +/** Copy selection data from one structure to another, including heap memory. */
 +void DRW_mesh_weight_state_copy(struct DRW_MeshWeightState *wstate_dst, const struct DRW_MeshWeightState *wstate_src)
 +{
 +      MEM_SAFE_FREE(wstate_dst->defgroup_sel);
 +
 +      memcpy(wstate_dst, wstate_src, sizeof(*wstate_dst));
 +
 +      if (wstate_src->defgroup_sel) {
 +              wstate_dst->defgroup_sel = MEM_dupallocN(wstate_src->defgroup_sel);
 +      }
 +}
 +
 +/** Compare two selection structures. */
 +bool DRW_mesh_weight_state_compare(const struct DRW_MeshWeightState *a, const struct DRW_MeshWeightState *b)
 +{
 +      return a->defgroup_active == b->defgroup_active &&
 +             a->defgroup_len == b->defgroup_len &&
 +             a->flags == b->flags &&
 +             a->alert_mode == b->alert_mode &&
 +             a->defgroup_sel_count == b->defgroup_sel_count &&
 +             ((!a->defgroup_sel && !b->defgroup_sel) ||
 +              (a->defgroup_sel && b->defgroup_sel &&
 +               memcmp(a->defgroup_sel, b->defgroup_sel, a->defgroup_len * sizeof(bool)) == 0));
 +}
 +
 +/** \} */
 +
 +/* ---------------------------------------------------------------------- */
 +
 +/** \name Mesh GPUBatch Cache
 + * \{ */
 +
 +typedef struct MeshBatchCache {
 +      /* In order buffers: All verts only specified once.
 +       * To be used with a GPUIndexBuf. */
 +      struct {
 +              GPUVertBuf *pos_nor;
 +      } ordered;
 +
 +      /* Tesselated: (all verts specified for each triangles).
 +       * Indices does not match the CPU data structure's. */
 +      struct {
 +              GPUVertBuf *pos_nor;
 +
 +              GPUVertBuf *wireframe_data;
 +      } tess;
 +
 +      /* Edit Mesh Data:
 +       * Data is also tesselated because of barycentric wireframe rendering. */
 +      struct {
 +              GPUVertBuf *pos_nor;
 +              GPUVertBuf *pos_nor_ledges;
 +              GPUVertBuf *pos_nor_lverts;
 +              GPUVertBuf *pos_nor_data_facedots;
 +              GPUVertBuf *data;
 +              GPUVertBuf *data_ledges;
 +              GPUVertBuf *data_lverts;
 +              GPUVertBuf *lnor;
 +      } edit;
 +
 +      /* Index Buffers:
 +       * Only need to be updated when topology changes. */
 +      struct {
 +              /* Contains indices to unique edit vertices to not
 +               * draw the same vert multiple times (because of tesselation). */
 +              GPUIndexBuf *edit_verts;
 +      } ibo;
 +
 +      struct {
 +              /* Surfaces / Render */
 +              /* Edit mode */
 +              GPUBatch *edit_triangles;
 +              GPUBatch *edit_vertices;
 +              GPUBatch *edit_loose_edges;
 +              GPUBatch *edit_loose_verts;
 +              GPUBatch *edit_triangles_nor;
 +              GPUBatch *edit_triangles_lnor;
 +              GPUBatch *edit_loose_edges_nor;
 +              GPUBatch *edit_facedots;
 +              /* Common display / Other */
 +              GPUBatch *all_verts;
 +              GPUBatch *wire_triangles; /* Triangles for object mode wireframe. */
 +      } batch;
 +
 +      /* OLD BATCH METHOD, thoses needs to be ported and added in the structs above. */
 +
 +      /* Indices buffers. */
 +      GPUIndexBuf *edges_in_order;
 +      GPUIndexBuf *edges_adjacency; /* Store edges with adjacent vertices. */
 +      GPUIndexBuf *triangles_in_order;
 +      GPUIndexBuf *ledges_in_order;
 +
 +      GPUBatch *all_edges;
 +      GPUBatch *all_triangles;
 +
 +      GPUVertBuf *pos_with_normals;
 +      GPUVertBuf *pos_with_normals_visible_only;
 +      GPUVertBuf *pos_with_normals_edit;
 +      GPUVertBuf *pos_with_normals_visible_only_edit;
 +      GPUVertBuf *tri_aligned_uv;  /* Active UV layer (mloopuv) */
 +
 +      /**
 +       * Other uses are all positions or loose elements.
 +       * This stores all visible elements, needed for selection.
 +       */
 +      GPUVertBuf *ed_fcenter_pos_with_nor_and_sel;
 +      GPUVertBuf *ed_edge_pos;
 +      GPUVertBuf *ed_vert_pos;
 +
 +      GPUBatch *triangles_with_normals;
 +      GPUBatch *ledges_with_normals;
 +
 +      /* Skip hidden (depending on paint select mode) */
 +      GPUBatch *triangles_with_weights;
 +      GPUBatch *triangles_with_vert_colors;
 +      /* Always skip hidden */
 +      GPUBatch *triangles_with_select_mask;
 +      GPUBatch *triangles_with_select_id;
 +      uint       triangles_with_select_id_offset;
 +
 +      GPUBatch *facedot_with_select_id;  /* shares vbo with 'edit_facedots' */
 +      GPUBatch *edges_with_select_id;
 +      GPUBatch *verts_with_select_id;
 +
 +      uint facedot_with_select_id_offset;
 +      uint edges_with_select_id_offset;
 +      uint verts_with_select_id_offset;
 +
 +      GPUBatch *points_with_normals;
 +      GPUBatch *fancy_edges; /* owns its vertex buffer (not shared) */
 +
 +      GPUBatch *edge_detection;
 +
 +
 +      /* Maybe have shaded_triangles_data split into pos_nor and uv_tangent
 +       * to minimize data transfer for skinned mesh. */
 +      GPUVertFormat shaded_triangles_format;
 +      GPUVertBuf *shaded_triangles_data;
 +      GPUIndexBuf **shaded_triangles_in_order;
 +      GPUBatch **shaded_triangles;
 +
 +      /* Texture Paint.*/
 +      /* per-texture batch */
 +      GPUBatch **texpaint_triangles;
 +      GPUBatch  *texpaint_triangles_single;
 +
 +      GPUBatch *overlay_weight_faces;
 +      GPUBatch *overlay_weight_verts;
 +      GPUBatch *overlay_paint_edges;
 +
 +      /* 2D/UV edit */
 +      GPUVertBuf *edituv_pos;
 +      GPUVertBuf *edituv_area;
 +      GPUVertBuf *edituv_angle;
 +      GPUVertBuf *edituv_data;
 +
 +      GPUIndexBuf *edituv_visible_faces;
 +      GPUIndexBuf *edituv_visible_edges;
 +
 +      GPUBatch *texpaint_uv_loops;
 +
 +      GPUBatch *edituv_faces_strech_area;
 +      GPUBatch *edituv_faces_strech_angle;
 +      GPUBatch *edituv_faces;
 +      GPUBatch *edituv_edges;
 +      GPUBatch *edituv_verts;
 +      GPUBatch *edituv_facedots;
 +
 +      char edituv_state;
 +
 +      /* arrays of bool uniform names (and value) that will be use to
 +       * set srgb conversion for auto attribs.*/
 +      char *auto_layer_names;
 +      int *auto_layer_is_srgb;
 +      int auto_layer_len;
 +
 +      /* settings to determine if cache is invalid */
 +      bool is_maybe_dirty;
 +      bool is_dirty; /* Instantly invalidates cache, skipping mesh check */
 +      int edge_len;
 +      int tri_len;
 +      int poly_len;
 +      int vert_len;
 +      int mat_len;
 +      bool is_editmode;
 +
 +      struct DRW_MeshWeightState weight_state;
 +
 +      /* XXX, only keep for as long as sculpt mode uses shaded drawing. */
 +      bool is_sculpt_points_tag;
 +
 +      /* Valid only if edges_adjacency is up to date. */
 +      bool is_manifold;
 +} MeshBatchCache;
 +
 +/* GPUBatch cache management. */
 +
 +static bool mesh_batch_cache_valid(Mesh *me)
 +{
 +      MeshBatchCache *cache = me->runtime.batch_cache;
 +
 +      if (cache == NULL) {
 +              return false;
 +      }
 +
 +      /* XXX find another place for this */
 +      if (cache->mat_len != mesh_render_mat_len_get(me)) {
 +              cache->is_maybe_dirty = true;
 +      }
 +
 +      if (cache->is_editmode != (me->edit_btmesh != NULL)) {
 +              return false;
 +      }
 +
 +      if (cache->is_dirty) {
 +              return false;
 +      }
 +
 +      if (cache->is_maybe_dirty == false) {
 +              return true;
 +      }
 +      else {
 +              if (cache->is_editmode) {
 +                      return false;
 +              }
 +              else if ((cache->vert_len != mesh_render_verts_len_get(me)) ||
 +                       (cache->edge_len != mesh_render_edges_len_get(me)) ||
 +                       (cache->tri_len  != mesh_render_looptri_len_get(me)) ||
 +                       (cache->poly_len != mesh_render_polys_len_get(me)) ||
 +                       (cache->mat_len   != mesh_render_mat_len_get(me)))
 +              {
 +                      return false;
 +              }
 +      }
 +
 +      return true;
 +}
 +
 +static void mesh_batch_cache_init(Mesh *me)
 +{
 +      MeshBatchCache *cache = me->runtime.batch_cache;
 +
 +      if (!cache) {
 +              cache = me->runtime.batch_cache = MEM_callocN(sizeof(*cache), __func__);
 +      }
 +      else {
 +              memset(cache, 0, sizeof(*cache));
 +      }
 +
 +      cache->is_editmode = me->edit_btmesh != NULL;
 +
 +      if (cache->is_editmode == false) {
 +              cache->edge_len = mesh_render_edges_len_get(me);
 +              cache->tri_len = mesh_render_looptri_len_get(me);
 +              cache->poly_len = mesh_render_polys_len_get(me);
 +              cache->vert_len = mesh_render_verts_len_get(me);
 +      }
 +
 +      cache->mat_len = mesh_render_mat_len_get(me);
 +
 +      cache->is_maybe_dirty = false;
 +      cache->is_dirty = false;
 +
 +      DRW_mesh_weight_state_clear(&cache->weight_state);
 +}
 +
 +static MeshBatchCache *mesh_batch_cache_get(Mesh *me)
 +{
 +      if (!mesh_batch_cache_valid(me)) {
 +              mesh_batch_cache_clear(me);
 +              mesh_batch_cache_init(me);
 +      }
 +      return me->runtime.batch_cache;
 +}
 +
 +static void mesh_batch_cache_check_vertex_group(MeshBatchCache *cache, const struct DRW_MeshWeightState *wstate)
 +{
 +      if (!DRW_mesh_weight_state_compare(&cache->weight_state, wstate)) {
 +              GPU_BATCH_DISCARD_SAFE(cache->triangles_with_weights);
 +
 +              DRW_mesh_weight_state_clear(&cache->weight_state);
 +      }
 +}
 +
 +static void mesh_batch_cache_discard_shaded_tri(MeshBatchCache *cache)
 +{
 +      GPU_VERTBUF_DISCARD_SAFE(cache->shaded_triangles_data);
 +      if (cache->shaded_triangles_in_order) {
 +              for (int i = 0; i < cache->mat_len; i++) {
 +                      GPU_INDEXBUF_DISCARD_SAFE(cache->shaded_triangles_in_order[i]);
 +              }
 +      }
 +      if (cache->shaded_triangles) {
 +              for (int i = 0; i < cache->mat_len; i++) {
 +                      GPU_BATCH_DISCARD_SAFE(cache->shaded_triangles[i]);
 +              }
 +      }
 +      if (cache->texpaint_triangles) {
 +              for (int i = 0; i < cache->mat_len; i++) {
 +                      /* They use shaded_triangles_in_order */
 +                      GPU_BATCH_DISCARD_SAFE(cache->texpaint_triangles[i]);
 +              }
 +      }
 +      MEM_SAFE_FREE(cache->shaded_triangles_in_order);
 +      MEM_SAFE_FREE(cache->shaded_triangles);
 +      MEM_SAFE_FREE(cache->texpaint_triangles);
 +
 +      MEM_SAFE_FREE(cache->auto_layer_names);
 +      MEM_SAFE_FREE(cache->auto_layer_is_srgb);
 +}
 +
 +static void mesh_batch_cache_discard_uvedit(MeshBatchCache *cache)
 +{
 +      GPU_VERTBUF_DISCARD_SAFE(cache->edituv_pos);
 +      GPU_VERTBUF_DISCARD_SAFE(cache->edituv_area);
 +      GPU_VERTBUF_DISCARD_SAFE(cache->edituv_angle);
 +      GPU_VERTBUF_DISCARD_SAFE(cache->edituv_data);
 +
 +      GPU_INDEXBUF_DISCARD_SAFE(cache->edituv_visible_faces);
 +      GPU_INDEXBUF_DISCARD_SAFE(cache->edituv_visible_edges);
 +
 +      if (cache->edituv_faces_strech_area) {
 +              gpu_batch_presets_unregister(cache->edituv_faces_strech_area);
 +      }
 +      if (cache->edituv_faces_strech_angle) {
 +              gpu_batch_presets_unregister(cache->edituv_faces_strech_angle);
 +      }
 +      if (cache->edituv_faces) {
 +              gpu_batch_presets_unregister(cache->edituv_faces);
 +      }
 +      if (cache->edituv_edges) {
 +              gpu_batch_presets_unregister(cache->edituv_edges);
 +      }
 +      if (cache->edituv_verts) {
 +              gpu_batch_presets_unregister(cache->edituv_verts);
 +      }
 +      if (cache->edituv_facedots) {
 +              gpu_batch_presets_unregister(cache->edituv_facedots);
 +      }
 +
 +      GPU_BATCH_DISCARD_SAFE(cache->edituv_faces_strech_area);
 +      GPU_BATCH_DISCARD_SAFE(cache->edituv_faces_strech_angle);
 +      GPU_BATCH_DISCARD_SAFE(cache->edituv_faces);
 +      GPU_BATCH_DISCARD_SAFE(cache->edituv_edges);
 +      GPU_BATCH_DISCARD_SAFE(cache->edituv_verts);
 +      GPU_BATCH_DISCARD_SAFE(cache->edituv_facedots);
 +
 +      gpu_batch_presets_unregister(cache->texpaint_uv_loops);
 +
 +      GPU_BATCH_DISCARD_SAFE(cache->texpaint_uv_loops);
 +
 +      cache->edituv_state = 0;
 +}
 +
 +void DRW_mesh_batch_cache_dirty_tag(Mesh *me, int mode)
 +{
 +      MeshBatchCache *cache = me->runtime.batch_cache;
 +      if (cache == NULL) {
 +              return;
 +      }
 +      switch (mode) {
 +              case BKE_MESH_BATCH_DIRTY_MAYBE_ALL:
 +                      cache->is_maybe_dirty = true;
 +                      break;
 +              case BKE_MESH_BATCH_DIRTY_SELECT:
 +                      GPU_VERTBUF_DISCARD_SAFE(cache->edit.data);
 +                      GPU_VERTBUF_DISCARD_SAFE(cache->edit.data_ledges);
 +                      GPU_VERTBUF_DISCARD_SAFE(cache->edit.data_lverts);
 +                      GPU_VERTBUF_DISCARD_SAFE(cache->edit.pos_nor_data_facedots);
 +                      GPU_BATCH_DISCARD_SAFE(cache->batch.edit_triangles);
 +                      GPU_BATCH_DISCARD_SAFE(cache->batch.edit_vertices);
 +                      GPU_BATCH_DISCARD_SAFE(cache->batch.edit_loose_verts);
 +                      GPU_BATCH_DISCARD_SAFE(cache->batch.edit_loose_edges);
 +                      GPU_BATCH_DISCARD_SAFE(cache->batch.edit_facedots);
 +                      GPU_VERTBUF_DISCARD_SAFE(cache->ed_edge_pos);
 +                      GPU_VERTBUF_DISCARD_SAFE(cache->ed_vert_pos);
 +                      /* Edit mode selection. */
 +                      GPU_BATCH_DISCARD_SAFE(cache->facedot_with_select_id);
 +                      GPU_BATCH_DISCARD_SAFE(cache->edges_with_select_id);
 +                      GPU_BATCH_DISCARD_SAFE(cache->verts_with_select_id);
 +                      /* Paint mode selection */
 +                      GPU_BATCH_DISCARD_SAFE(cache->overlay_paint_edges);
 +                      GPU_BATCH_DISCARD_SAFE(cache->overlay_weight_faces);
 +                      GPU_BATCH_DISCARD_SAFE(cache->overlay_weight_verts);
 +                      /* Because visible UVs depends on edit mode selection, discard everything. */
 +                      mesh_batch_cache_discard_uvedit(cache);
 +                      break;
 +              case BKE_MESH_BATCH_DIRTY_ALL:
 +                      cache->is_dirty = true;
 +                      break;
 +              case BKE_MESH_BATCH_DIRTY_SHADING:
 +                      mesh_batch_cache_discard_shaded_tri(cache);
 +                      mesh_batch_cache_discard_uvedit(cache);
 +                      break;
 +              case BKE_MESH_BATCH_DIRTY_SCULPT_COORDS:
 +                      cache->is_sculpt_points_tag = true;
 +                      break;
 +              case BKE_MESH_BATCH_DIRTY_UVEDIT_ALL:
 +                      mesh_batch_cache_discard_uvedit(cache);
 +                      break;
 +              case BKE_MESH_BATCH_DIRTY_UVEDIT_SELECT:
 +                      /* For now same as above. */
 +                      mesh_batch_cache_discard_uvedit(cache);
 +                      break;
 +              default:
 +                      BLI_assert(0);
 +      }
 +}
 +
 +/**
 + * This only clear the batches associated to the given vertex buffer.
 + **/
 +static void mesh_batch_cache_clear_selective(Mesh *me, GPUVertBuf *vert)
 +{
 +      MeshBatchCache *cache = me->runtime.batch_cache;
 +      if (!cache) {
 +              return;
 +      }
 +
 +      BLI_assert(vert != NULL);
 +
 +      if (ELEM(vert, cache->pos_with_normals, cache->pos_with_normals_visible_only,
 +                     cache->pos_with_normals_edit, cache->pos_with_normals_visible_only_edit))
 +      {
 +              GPU_BATCH_DISCARD_SAFE(cache->triangles_with_normals);
 +              GPU_BATCH_DISCARD_SAFE(cache->triangles_with_weights);
 +              GPU_BATCH_DISCARD_SAFE(cache->triangles_with_vert_colors);
 +              GPU_BATCH_DISCARD_SAFE(cache->triangles_with_select_id);
 +              GPU_BATCH_DISCARD_SAFE(cache->triangles_with_select_mask);
 +              GPU_BATCH_DISCARD_SAFE(cache->points_with_normals);
 +              GPU_BATCH_DISCARD_SAFE(cache->ledges_with_normals);
 +              if (cache->shaded_triangles) {
 +                      for (int i = 0; i < cache->mat_len; i++) {
 +                              GPU_BATCH_DISCARD_SAFE(cache->shaded_triangles[i]);
 +                      }
 +              }
 +              MEM_SAFE_FREE(cache->shaded_triangles);
 +              if (cache->texpaint_triangles) {
 +                      for (int i = 0; i < cache->mat_len; i++) {
 +                              GPU_BATCH_DISCARD_SAFE(cache->texpaint_triangles[i]);
 +                      }
 +              }
 +              MEM_SAFE_FREE(cache->texpaint_triangles);
 +              GPU_BATCH_DISCARD_SAFE(cache->texpaint_triangles_single);
 +      }
 +      /* TODO: add the other ones if needed. */
 +      else {
 +              /* Does not match any vertbuf in the batch cache! */
 +              BLI_assert(0);
 +      }
 +}
 +
 +static void mesh_batch_cache_clear(Mesh *me)
 +{
 +      MeshBatchCache *cache = me->runtime.batch_cache;
 +      if (!cache) {
 +              return;
 +      }
 +
 +      for (int i = 0; i < sizeof(cache->ordered) / sizeof(void *); ++i) {
 +              GPUVertBuf **vbo = (GPUVertBuf **)&cache->ordered;
 +              GPU_VERTBUF_DISCARD_SAFE(vbo[i]);
 +      }
 +      for (int i = 0; i < sizeof(cache->tess) / sizeof(void *); ++i) {
 +              GPUVertBuf **vbo = (GPUVertBuf **)&cache->tess;
 +              GPU_VERTBUF_DISCARD_SAFE(vbo[i]);
 +      }
 +      for (int i = 0; i < sizeof(cache->edit) / sizeof(void *); ++i) {
 +              GPUVertBuf **vbo = (GPUVertBuf **)&cache->edit;
 +              GPU_VERTBUF_DISCARD_SAFE(vbo[i]);
 +      }
 +      for (int i = 0; i < sizeof(cache->ibo) / sizeof(void *); ++i) {
 +              GPUIndexBuf **ibo = (GPUIndexBuf **)&cache->ibo;
 +              GPU_INDEXBUF_DISCARD_SAFE(ibo[i]);
 +      }
 +      for (int i = 0; i < sizeof(cache->batch) / sizeof(void *); ++i) {
 +              GPUBatch **batch = (GPUBatch **)&cache->batch;
 +              GPU_BATCH_DISCARD_SAFE(batch[i]);
 +      }
 +
 +      GPU_BATCH_DISCARD_SAFE(cache->all_edges);
 +      GPU_BATCH_DISCARD_SAFE(cache->all_triangles);
 +
 +      GPU_INDEXBUF_DISCARD_SAFE(cache->edges_in_order);
 +      GPU_INDEXBUF_DISCARD_SAFE(cache->triangles_in_order);
 +      GPU_INDEXBUF_DISCARD_SAFE(cache->ledges_in_order);
 +
 +      GPU_BATCH_DISCARD_SAFE(cache->overlay_weight_faces);
 +      GPU_BATCH_DISCARD_SAFE(cache->overlay_weight_verts);
 +      GPU_BATCH_DISCARD_SAFE(cache->overlay_paint_edges);
 +
 +      GPU_BATCH_DISCARD_SAFE(cache->triangles_with_normals);
 +      GPU_BATCH_DISCARD_SAFE(cache->points_with_normals);
 +      GPU_BATCH_DISCARD_SAFE(cache->ledges_with_normals);
 +      GPU_VERTBUF_DISCARD_SAFE(cache->pos_with_normals);
 +      GPU_VERTBUF_DISCARD_SAFE(cache->pos_with_normals_visible_only);
 +      GPU_VERTBUF_DISCARD_SAFE(cache->pos_with_normals_edit);
 +      GPU_VERTBUF_DISCARD_SAFE(cache->pos_with_normals_visible_only_edit);
 +      GPU_BATCH_DISCARD_SAFE(cache->triangles_with_weights);
 +      GPU_BATCH_DISCARD_SAFE(cache->triangles_with_vert_colors);
 +      GPU_VERTBUF_DISCARD_SAFE(cache->tri_aligned_uv);
 +      GPU_VERTBUF_DISCARD_SAFE(cache->ed_fcenter_pos_with_nor_and_sel);
 +      GPU_VERTBUF_DISCARD_SAFE(cache->ed_edge_pos);
 +      GPU_VERTBUF_DISCARD_SAFE(cache->ed_vert_pos);
 +      GPU_BATCH_DISCARD_SAFE(cache->triangles_with_select_mask);
 +      GPU_BATCH_DISCARD_SAFE(cache->triangles_with_select_id);
 +      GPU_BATCH_DISCARD_SAFE(cache->facedot_with_select_id);
 +      GPU_BATCH_DISCARD_SAFE(cache->edges_with_select_id);
 +      GPU_BATCH_DISCARD_SAFE(cache->verts_with_select_id);
 +
 +      GPU_BATCH_DISCARD_SAFE(cache->fancy_edges);
 +
 +      GPU_INDEXBUF_DISCARD_SAFE(cache->edges_adjacency);
 +      GPU_BATCH_DISCARD_SAFE(cache->edge_detection);
 +
 +      mesh_batch_cache_discard_shaded_tri(cache);
 +
 +      mesh_batch_cache_discard_uvedit(cache);
 +
 +      if (cache->texpaint_triangles) {
 +              for (int i = 0; i < cache->mat_len; i++) {
 +                      GPU_BATCH_DISCARD_SAFE(cache->texpaint_triangles[i]);
 +              }
 +      }
 +      MEM_SAFE_FREE(cache->texpaint_triangles);
 +
 +      GPU_BATCH_DISCARD_SAFE(cache->texpaint_triangles_single);
 +
 +      DRW_mesh_weight_state_clear(&cache->weight_state);
 +}
 +
 +void DRW_mesh_batch_cache_free(Mesh *me)
 +{
 +      mesh_batch_cache_clear(me);
 +      MEM_SAFE_FREE(me->runtime.batch_cache);
 +}
 +
 +/* GPUBatch cache usage. */
 +
 +static GPUVertBuf *mesh_batch_cache_get_tri_shading_data(MeshRenderData *rdata, MeshBatchCache *cache)
 +{
 +      BLI_assert(rdata->types & (MR_DATATYPE_VERT | MR_DATATYPE_LOOPTRI | MR_DATATYPE_LOOP | MR_DATATYPE_POLY));
 +
 +      if (cache->shaded_triangles_data == NULL) {
 +              const uint uv_len = rdata->cd.layers.uv_len;
 +              const uint tangent_len = rdata->cd.layers.tangent_len;
 +              const uint vcol_len = rdata->cd.layers.vcol_len;
 +              const uint layers_combined_len = uv_len + vcol_len + tangent_len;
 +              cache->auto_layer_len = 0;
 +
 +              if (layers_combined_len == 0) {
 +                      return NULL;
 +              }
 +
 +              GPUVertFormat *format = &cache->shaded_triangles_format;
 +
 +              GPU_vertformat_clear(format);
 +
 +              /* initialize vertex format */
 +              uint *layers_combined_id = BLI_array_alloca(layers_combined_id, layers_combined_len);
 +              uint *uv_id = layers_combined_id;
 +              uint *tangent_id = uv_id + uv_len;
 +              uint *vcol_id = tangent_id + tangent_len;
 +
 +              /* Not needed, just for sanity. */
 +              if (uv_len == 0) { uv_id = NULL; }
 +              if (tangent_len == 0) { tangent_id = NULL; }
 +              if (vcol_len == 0) { vcol_id = NULL; }
 +
 +              /* Count number of auto layer and allocate big enough name buffer. */
 +              uint auto_names_len = 0;
 +              uint auto_ofs = 0;
 +              uint auto_id = 0;
 +              for (uint i = 0; i < uv_len; i++) {
 +                      const char *attrib_name = mesh_render_data_uv_auto_layer_uuid_get(rdata, i);
 +                      auto_names_len += strlen(attrib_name) + 2; /* include null terminator and b prefix. */
 +                      cache->auto_layer_len++;
 +              }
 +              for (uint i = 0; i < vcol_len; i++) {
 +                      if (rdata->cd.layers.auto_vcol[i]) {
 +                              const char *attrib_name = mesh_render_data_vcol_auto_layer_uuid_get(rdata, i);
 +                              auto_names_len += strlen(attrib_name) + 2; /* include null terminator and b prefix. */
 +                              cache->auto_layer_len++;
 +                      }
 +              }
 +              auto_names_len += 1; /* add an ultimate '\0' terminator */
 +              cache->auto_layer_names = MEM_callocN(auto_names_len * sizeof(char), "Auto layer name buf");
 +              cache->auto_layer_is_srgb = MEM_mallocN(cache->auto_layer_len * sizeof(int), "Auto layer value buf");
 +
 +#define USE_COMP_MESH_DATA
 +
 +              for (uint i = 0; i < uv_len; i++) {
 +                      /* UV */
 +                      const char *attrib_name = mesh_render_data_uv_layer_uuid_get(rdata, i);
 +#if defined(USE_COMP_MESH_DATA) && 0 /* these are clamped. Maybe use them as an option in the future */
 +                      uv_id[i] = GPU_vertformat_attr_add(format, attrib_name, GPU_COMP_I16, 2, GPU_FETCH_INT_TO_FLOAT_UNIT);
 +#else
 +                      uv_id[i] = GPU_vertformat_attr_add(format, attrib_name, GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
 +#endif
 +
 +                      /* Auto Name */
 +                      attrib_name = mesh_render_data_uv_auto_layer_uuid_get(rdata, i);
 +                      GPU_vertformat_alias_add(format, attrib_name);
 +
 +                      /* +1 include null terminator. */
 +                      auto_ofs += 1 + BLI_snprintf_rlen(
 +                              cache->auto_layer_names + auto_ofs, auto_names_len - auto_ofs, "b%s", attrib_name);
 +                      cache->auto_layer_is_srgb[auto_id++] = 0; /* tag as not srgb */
 +
 +                      if (i == rdata->cd.layers.uv_active) {
 +                              GPU_vertformat_alias_add(format, "u");
 +                      }
 +              }
 +
 +              for (uint i = 0; i < tangent_len; i++) {
 +                      const char *attrib_name = mesh_render_data_tangent_layer_uuid_get(rdata, i);
 +#ifdef USE_COMP_MESH_DATA
 +                      /* Tangents need more precision than 10_10_10 */
 +                      tangent_id[i] = GPU_vertformat_attr_add(format, attrib_name, GPU_COMP_I16, 4, GPU_FETCH_INT_TO_FLOAT_UNIT);
 +#else
 +                      tangent_id[i] = GPU_vertformat_attr_add(format, attrib_name, GPU_COMP_F32, 4, GPU_FETCH_FLOAT);
 +#endif
 +
 +                      if (i == rdata->cd.layers.tangent_active) {
 +                              GPU_vertformat_alias_add(format, "t");
 +                      }
 +              }
 +
 +              for (uint i = 0; i < vcol_len; i++) {
 +                      const char *attrib_name = mesh_render_data_vcol_layer_uuid_get(rdata, i);
 +                      vcol_id[i] = GPU_vertformat_attr_add(format, attrib_name, GPU_COMP_U8, 3, GPU_FETCH_INT_TO_FLOAT_UNIT);
 +
 +                      /* Auto layer */
 +                      if (rdata->cd.layers.auto_vcol[i]) {
 +                              attrib_name = mesh_render_data_vcol_auto_layer_uuid_get(rdata, i);
 +
 +                              GPU_vertformat_alias_add(format, attrib_name);
 +
 +                              /* +1 include null terminator. */
 +                              auto_ofs += 1 + BLI_snprintf_rlen(
 +                                      cache->auto_layer_names + auto_ofs, auto_names_len - auto_ofs, "b%s", attrib_name);
 +                              cache->auto_layer_is_srgb[auto_id++] = 1; /* tag as srgb */
 +                      }
 +
 +                      if (i == rdata->cd.layers.vcol_