/*
 * 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) 2006 Blender Foundation.
 * All rights reserved.
 * Implementation of CDDerivedMesh.
 *
 * BKE_cdderivedmesh.h contains the function prototypes for this file.
 */

/** \file
 * \ingroup bke
 */

#include "atomic_ops.h"

#include "BLI_math.h"
#include "BLI_utildefines.h"

#include "BKE_pbvh.h"
#include "BKE_cdderivedmesh.h"
#include "BKE_mesh.h"
#include "BKE_mesh_mapping.h"
#include "BKE_object.h"
#include "BKE_paint.h"
#include "BKE_editmesh.h"
#include "BKE_curve.h"

#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
#include "DNA_object_types.h"
#include "DNA_curve_types.h" /* for Curve */

#include "MEM_guardedalloc.h"

#include <string.h>
#include <limits.h>
#include <math.h>

typedef struct {
  DerivedMesh dm;

  /* these point to data in the DerivedMesh custom data layers,
   * they are only here for efficiency and convenience */
  MVert *mvert;
  MEdge *medge;
  MFace *mface;
  MLoop *mloop;
  MPoly *mpoly;

  /* Cached */
  struct PBVH *pbvh;
  bool pbvh_draw;

  /* Mesh connectivity */
  MeshElemMap *pmap;
  int *pmap_mem;
} CDDerivedMesh;

/**************** DerivedMesh interface functions ****************/
static int cdDM_getNumVerts(DerivedMesh *dm)
{
  return dm->numVertData;
}

static int cdDM_getNumEdges(DerivedMesh *dm)
{
  return dm->numEdgeData;
}

static int cdDM_getNumTessFaces(DerivedMesh *dm)
{
  /* uncomment and add a breakpoint on the printf()
   * to help debug tessfaces issues since BMESH merge. */
#if 0
  if (dm->numTessFaceData == 0 && dm->numPolyData != 0) {
    printf("%s: has no faces!\n");
  }
#endif
  return dm->numTessFaceData;
}

static int cdDM_getNumLoops(DerivedMesh *dm)
{
  return dm->numLoopData;
}

static int cdDM_getNumPolys(DerivedMesh *dm)
{
  return dm->numPolyData;
}

static void cdDM_getVert(DerivedMesh *dm, int index, MVert *r_vert)
{
  CDDerivedMesh *cddm = (CDDerivedMesh *)dm;
  *r_vert = cddm->mvert[index];
}

static void cdDM_getEdge(DerivedMesh *dm, int index, MEdge *r_edge)
{
  CDDerivedMesh *cddm = (CDDerivedMesh *)dm;
  *r_edge = cddm->medge[index];
}

static void cdDM_getTessFace(DerivedMesh *dm, int index, MFace *r_face)
{
  CDDerivedMesh *cddm = (CDDerivedMesh *)dm;
  *r_face = cddm->mface[index];
}

static void cdDM_copyVertArray(DerivedMesh *dm, MVert *r_vert)
{
  CDDerivedMesh *cddm = (CDDerivedMesh *)dm;
  memcpy(r_vert, cddm->mvert, sizeof(*r_vert) * dm->numVertData);
}

static void cdDM_copyEdgeArray(DerivedMesh *dm, MEdge *r_edge)
{
  CDDerivedMesh *cddm = (CDDerivedMesh *)dm;
  memcpy(r_edge, cddm->medge, sizeof(*r_edge) * dm->numEdgeData);
}

static void cdDM_copyTessFaceArray(DerivedMesh *dm, MFace *r_face)
{
  CDDerivedMesh *cddm = (CDDerivedMesh *)dm;
  memcpy(r_face, cddm->mface, sizeof(*r_face) * dm->numTessFaceData);
}

static void cdDM_copyLoopArray(DerivedMesh *dm, MLoop *r_loop)
{
  CDDerivedMesh *cddm = (CDDerivedMesh *)dm;
  memcpy(r_loop, cddm->mloop, sizeof(*r_loop) * dm->numLoopData);
}

static void cdDM_copyPolyArray(DerivedMesh *dm, MPoly *r_poly)
{
  CDDerivedMesh *cddm = (CDDerivedMesh *)dm;
  memcpy(r_poly, cddm->mpoly, sizeof(*r_poly) * dm->numPolyData);
}

static void cdDM_getMinMax(DerivedMesh *dm, float r_min[3], float r_max[3])
{
  CDDerivedMesh *cddm = (CDDerivedMesh *)dm;
  int i;

  if (dm->numVertData) {
    for (i = 0; i < dm->numVertData; i++) {
      minmax_v3v3_v3(r_min, r_max, cddm->mvert[i].co);
    }
  }
  else {
    zero_v3(r_min);
    zero_v3(r_max);
  }
}

static void cdDM_getVertCo(DerivedMesh *dm, int index, float r_co[3])
{
  CDDerivedMesh *cddm = (CDDerivedMesh *)dm;

  copy_v3_v3(r_co, cddm->mvert[index].co);
}

static void cdDM_getVertCos(DerivedMesh *dm, float (*r_cos)[3])
{
  MVert *mv = CDDM_get_verts(dm);
  int i;

  for (i = 0; i < dm->numVertData; i++, mv++) {
    copy_v3_v3(r_cos[i], mv->co);
  }
}

static void cdDM_getVertNo(DerivedMesh *dm, int index, float r_no[3])
{
  CDDerivedMesh *cddm = (CDDerivedMesh *)dm;
  normal_short_to_float_v3(r_no, cddm->mvert[index].no);
}

static const MeshElemMap *cdDM_getPolyMap(Object *ob, DerivedMesh *dm)
{
  CDDerivedMesh *cddm = (CDDerivedMesh *)dm;

  if (!cddm->pmap && ob->type == OB_MESH) {
    Mesh *me = ob->data;

    BKE_mesh_vert_poly_map_create(
        &cddm->pmap, &cddm->pmap_mem, me->mpoly, me->mloop, me->totvert, me->totpoly, me->totloop);
  }

  return cddm->pmap;
}

static bool check_sculpt_object_deformed(Object *object, bool for_construction)
{
  bool deformed = false;

  /* Active modifiers means extra deformation, which can't be handled correct
   * on birth of PBVH and sculpt "layer" levels, so use PBVH only for internal brush
   * stuff and show final DerivedMesh so user would see actual object shape.
   */
  deformed |= object->sculpt->modifiers_active;

  if (for_construction) {
    deformed |= object->sculpt->kb != NULL;
  }
  else {
    /* As in case with modifiers, we can't synchronize deformation made against
     * PBVH and non-locked keyblock, so also use PBVH only for brushes and
     * final DM to give final result to user.
     */
    deformed |= object->sculpt->kb && (object->shapeflag & OB_SHAPE_LOCK) == 0;
  }

  return deformed;
}

static bool can_pbvh_draw(Object *ob, DerivedMesh *dm)
{
  CDDerivedMesh *cddm = (CDDerivedMesh *)dm;
  Mesh *me = ob->data;
  bool deformed = check_sculpt_object_deformed(ob, false);

  if (deformed) {
    return false;
  }

  return cddm->mvert == me->mvert || ob->sculpt->kb;
}

static PBVH *cdDM_getPBVH(Object *ob, DerivedMesh *dm)
{
  CDDerivedMesh *cddm = (CDDerivedMesh *)dm;

  if (!ob) {
    cddm->pbvh = NULL;
    return NULL;
  }

  if (!ob->sculpt) {
    return NULL;
  }

  if (ob->sculpt->pbvh) {
    cddm->pbvh = ob->sculpt->pbvh;
    cddm->pbvh_draw = can_pbvh_draw(ob, dm);
  }

  /* Sculpting on a BMesh (dynamic-topology) gets a special PBVH */
  if (!cddm->pbvh && ob->sculpt->bm) {
    cddm->pbvh = BKE_pbvh_new();
    cddm->pbvh_draw = true;

    BKE_pbvh_build_bmesh(cddm->pbvh,
                         ob->sculpt->bm,
                         ob->sculpt->bm_smooth_shading,
                         ob->sculpt->bm_log,
                         ob->sculpt->cd_vert_node_offset,
                         ob->sculpt->cd_face_node_offset);

    pbvh_show_diffuse_color_set(cddm->pbvh, ob->sculpt->show_diffuse_color);
    pbvh_show_mask_set(cddm->pbvh, ob->sculpt->show_mask);
  }

  /* always build pbvh from original mesh, and only use it for drawing if
   * this derivedmesh is just original mesh. it's the multires subsurf dm
   * that this is actually for, to support a pbvh on a modified mesh */
  if (!cddm->pbvh && ob->type == OB_MESH) {
    Mesh *me = BKE_object_get_original_mesh(ob);
    const int looptris_num = poly_to_tri_count(me->totpoly, me->totloop);
    MLoopTri *looptri;
    bool deformed;

    cddm->pbvh = BKE_pbvh_new();
    cddm->pbvh_draw = can_pbvh_draw(ob, dm);

    looptri = MEM_malloc_arrayN(looptris_num, sizeof(*looptri), __func__);

    BKE_mesh_recalc_looptri(me->mloop, me->mpoly, me->mvert, me->totloop, me->totpoly, looptri);

    BKE_pbvh_build_mesh(cddm->pbvh,
                        me->mpoly,
                        me->mloop,
                        me->mvert,
                        me->totvert,
                        &me->vdata,
                        &me->ldata,
                        looptri,
                        looptris_num);

    pbvh_show_diffuse_color_set(cddm->pbvh, ob->sculpt->show_diffuse_color);
    pbvh_show_mask_set(cddm->pbvh, ob->sculpt->show_mask);

    deformed = check_sculpt_object_deformed(ob, true);

    if (deformed && ob->derivedDeform) {
      DerivedMesh *deformdm = ob->derivedDeform;
      float(*vertCos)[3];
      int totvert;

      totvert = deformdm->getNumVerts(deformdm);
      vertCos = MEM_malloc_arrayN(totvert, sizeof(float[3]), "cdDM_getPBVH vertCos");
      deformdm->getVertCos(deformdm, vertCos);
      BKE_pbvh_apply_vertCos(cddm->pbvh, vertCos, totvert);
      MEM_freeN(vertCos);
    }
  }

  return cddm->pbvh;
}

static void cdDM_foreachMappedVert(DerivedMesh *dm,
                                   void (*func)(void *userData,
                                                int index,
                                                const float co[3],
                                                const float no_f[3],
                                                const short no_s[3]),
                                   void *userData,
                                   DMForeachFlag flag)
{
  MVert *mv = CDDM_get_verts(dm);
  const int *index = DM_get_vert_data_layer(dm, CD_ORIGINDEX);
  int i;

  if (index) {
    for (i = 0; i < dm->numVertData; i++, mv++) {
      const short *no = (flag & DM_FOREACH_USE_NORMAL) ? mv->no : NULL;
      const int orig = *index++;
      if (orig == ORIGINDEX_NONE) {
        continue;
      }
      func(userData, orig, mv->co, NULL, no);
    }
  }
  else {
    for (i = 0; i < dm->numVertData; i++, mv++) {
      const short *no = (flag & DM_FOREACH_USE_NORMAL) ? mv->no : NULL;
      func(userData, i, mv->co, NULL, no);
    }
  }
}

static void cdDM_foreachMappedEdge(
    DerivedMesh *dm,
    void (*func)(void *userData, int index, const float v0co[3], const float v1co[3]),
    void *userData)
{
  CDDerivedMesh *cddm = (CDDerivedMesh *)dm;
  MVert *mv = cddm->mvert;
  MEdge *med = cddm->medge;
  int i, orig, *index = DM_get_edge_data_layer(dm, CD_ORIGINDEX);

  for (i = 0; i < dm->numEdgeData; i++, med++) {
    if (index) {
      orig = *index++;
      if (orig == ORIGINDEX_NONE) {
        continue;
      }
      func(userData, orig, mv[med->v1].co, mv[med->v2].co);
    }
    else {
      func(userData, i, mv[med->v1].co, mv[med->v2].co);
    }
  }
}

static void cdDM_foreachMappedLoop(DerivedMesh *dm,
                                   void (*func)(void *userData,
                                                int vertex_index,
                                                int face_index,
                                                const float co[3],
                                                const float no[3]),
                                   void *userData,
                                   DMForeachFlag flag)
{
  /* We can't use dm->getLoopDataLayout(dm) here,
   * we want to always access dm->loopData, EditDerivedBMesh would
   * return loop data from bmesh itself. */
  const float(*lnors)[3] = (flag & DM_FOREACH_USE_NORMAL) ? DM_get_loop_data_layer(dm, CD_NORMAL) :
                                                            NULL;

  const MVert *mv = CDDM_get_verts(dm);
  const MLoop *ml = CDDM_get_loops(dm);
  const MPoly *mp = CDDM_get_polys(dm);
  const int *v_index = DM_get_vert_data_layer(dm, CD_ORIGINDEX);
  const int *f_index = DM_get_poly_data_layer(dm, CD_ORIGINDEX);
  int p_idx, i;

  for (p_idx = 0; p_idx < dm->numPolyData; ++p_idx, ++mp) {
    for (i = 0; i < mp->totloop; ++i, ++ml) {
      const int v_idx = v_index ? v_index[ml->v] : ml->v;
      const int f_idx = f_index ? f_index[p_idx] : p_idx;
      const float *no = lnors ? *lnors++ : NULL;
      if (!ELEM(ORIGINDEX_NONE, v_idx, f_idx)) {
        func(userData, v_idx, f_idx, mv[ml->v].co, no);
      }
    }
  }
}

static void cdDM_foreachMappedFaceCenter(
    DerivedMesh *dm,
    void (*func)(void *userData, int index, const float cent[3], const float no[3]),
    void *userData,
    DMForeachFlag flag)
{
  CDDerivedMesh *cddm = (CDDerivedMesh *)dm;
  MVert *mvert = cddm->mvert;
  MPoly *mp;
  MLoop *ml;
  int i, orig, *index;

  index = CustomData_get_layer(&dm->polyData, CD_ORIGINDEX);
  mp = cddm->mpoly;
  for (i = 0; i < dm->numPolyData; i++, mp++) {
    float cent[3];
    float *no, _no[3];

    if (index) {
      orig = *index++;
      if (orig == ORIGINDEX_NONE) {
        continue;
      }
    }
    else {
      orig = i;
    }

    ml = &cddm->mloop[mp->loopstart];
    BKE_mesh_calc_poly_center(mp, ml, mvert, cent);

    if (flag & DM_FOREACH_USE_NORMAL) {
      BKE_mesh_calc_poly_normal(mp, ml, mvert, (no = _no));
    }
    else {
      no = NULL;
    }

    func(userData, orig, cent, no);
  }
}

void CDDM_recalc_tessellation_ex(DerivedMesh *dm, const bool do_face_nor_cpy)
{
  CDDerivedMesh *cddm = (CDDerivedMesh *)dm;

  dm->numTessFaceData = BKE_mesh_recalc_tessellation(&dm->faceData,
                                                     &dm->loopData,
                                                     &dm->polyData,
                                                     cddm->mvert,
                                                     dm->numTessFaceData,
                                                     dm->numLoopData,
                                                     dm->numPolyData,
                                                     do_face_nor_cpy);

  cddm->mface = CustomData_get_layer(&dm->faceData, CD_MFACE);

  /* Tessellation recreated faceData, and the active layer indices need to get re-propagated
   * from loops and polys to faces */
  CustomData_bmesh_update_active_layers(&dm->faceData, &dm->loopData);
}

void CDDM_recalc_tessellation(DerivedMesh *dm)
{
  CDDM_recalc_tessellation_ex(dm, true);
}

void CDDM_recalc_looptri(DerivedMesh *dm)
{
  CDDerivedMesh *cddm = (CDDerivedMesh *)dm;
  const unsigned int totpoly = dm->numPolyData;
  const unsigned int totloop = dm->numLoopData;

  DM_ensure_looptri_data(dm);
  BLI_assert(totpoly == 0 || cddm->dm.looptris.array_wip != NULL);

  BKE_mesh_recalc_looptri(
      cddm->mloop, cddm->mpoly, cddm->mvert, totloop, totpoly, cddm->dm.looptris.array_wip);

  BLI_assert(cddm->dm.looptris.array == NULL);
  atomic_cas_ptr(
      (void **)&cddm->dm.looptris.array, cddm->dm.looptris.array, cddm->dm.looptris.array_wip);
  cddm->dm.looptris.array_wip = NULL;
}

static void cdDM_free_internal(CDDerivedMesh *cddm)
{
  if (cddm->pmap) {
    MEM_freeN(cddm->pmap);
  }
  if (cddm->pmap_mem) {
    MEM_freeN(cddm->pmap_mem);
  }
}

static void cdDM_release(DerivedMesh *dm)
{
  CDDerivedMesh *cddm = (CDDerivedMesh *)dm;

  if (DM_release(dm)) {
    cdDM_free_internal(cddm);
    MEM_freeN(cddm);
  }
}

/**************** CDDM interface functions ****************/
static CDDerivedMesh *cdDM_create(const char *desc)
{
  CDDerivedMesh *cddm;
  DerivedMesh *dm;

  cddm = MEM_callocN(sizeof(*cddm), desc);
  dm = &cddm->dm;

  dm->getMinMax = cdDM_getMinMax;

  dm->getNumVerts = cdDM_getNumVerts;
  dm->getNumEdges = cdDM_getNumEdges;
  dm->getNumTessFaces = cdDM_getNumTessFaces;
  dm->getNumLoops = cdDM_getNumLoops;
  dm->getNumPolys = cdDM_getNumPolys;

  dm->getVert = cdDM_getVert;
  dm->getEdge = cdDM_getEdge;
  dm->getTessFace = cdDM_getTessFace;

  dm->copyVertArray = cdDM_copyVertArray;
  dm->copyEdgeArray = cdDM_copyEdgeArray;
  dm->copyTessFaceArray = cdDM_copyTessFaceArray;
  dm->copyLoopArray = cdDM_copyLoopArray;
  dm->copyPolyArray = cdDM_copyPolyArray;

  dm->getVertData = DM_get_vert_data;
  dm->getEdgeData = DM_get_edge_data;
  dm->getTessFaceData = DM_get_tessface_data;
  dm->getVertDataArray = DM_get_vert_data_layer;
  dm->getEdgeDataArray = DM_get_edge_data_layer;
  dm->getTessFaceDataArray = DM_get_tessface_data_layer;

  dm->calcNormals = CDDM_calc_normals;
  dm->calcLoopNormals = CDDM_calc_loop_normals;
  dm->calcLoopNormalsSpaceArray = CDDM_calc_loop_normals_spacearr;
  dm->calcLoopTangents = DM_calc_loop_tangents;
  dm->recalcTessellation = CDDM_recalc_tessellation;
  dm->recalcLoopTri = CDDM_recalc_looptri;

  dm->getVertCos = cdDM_getVertCos;
  dm->getVertCo = cdDM_getVertCo;
  dm->getVertNo = cdDM_getVertNo;

  dm->getPBVH = cdDM_getPBVH;
  dm->getPolyMap = cdDM_getPolyMap;

  dm->foreachMappedVert = cdDM_foreachMappedVert;
  dm->foreachMappedEdge = cdDM_foreachMappedEdge;
  dm->foreachMappedLoop = cdDM_foreachMappedLoop;
  dm->foreachMappedFaceCenter = cdDM_foreachMappedFaceCenter;

  dm->release = cdDM_release;

  return cddm;
}

DerivedMesh *CDDM_new(int numVerts, int numEdges, int numTessFaces, int numLoops, int numPolys)
{
  CDDerivedMesh *cddm = cdDM_create("CDDM_new dm");
  DerivedMesh *dm = &cddm->dm;

  DM_init(dm, DM_TYPE_CDDM, numVerts, numEdges, numTessFaces, numLoops, numPolys);

  CustomData_add_layer(&dm->vertData, CD_ORIGINDEX, CD_CALLOC, NULL, numVerts);
  CustomData_add_layer(&dm->edgeData, CD_ORIGINDEX, CD_CALLOC, NULL, numEdges);
  CustomData_add_layer(&dm->faceData, CD_ORIGINDEX, CD_CALLOC, NULL, numTessFaces);
  CustomData_add_layer(&dm->polyData, CD_ORIGINDEX, CD_CALLOC, NULL, numPolys);

  CustomData_add_layer(&dm->vertData, CD_MVERT, CD_CALLOC, NULL, numVerts);
  CustomData_add_layer(&dm->edgeData, CD_MEDGE, CD_CALLOC, NULL, numEdges);
  CustomData_add_layer(&dm->faceData, CD_MFACE, CD_CALLOC, NULL, numTessFaces);
  CustomData_add_layer(&dm->loopData, CD_MLOOP, CD_CALLOC, NULL, numLoops);
  CustomData_add_layer(&dm->polyData, CD_MPOLY, CD_CALLOC, NULL, numPolys);

  cddm->mvert = CustomData_get_layer(&dm->vertData, CD_MVERT);
  cddm->medge = CustomData_get_layer(&dm->edgeData, CD_MEDGE);
  cddm->mface = CustomData_get_layer(&dm->faceData, CD_MFACE);
  cddm->mloop = CustomData_get_layer(&dm->loopData, CD_MLOOP);
  cddm->mpoly = CustomData_get_layer(&dm->polyData, CD_MPOLY);

  return dm;
}

DerivedMesh *CDDM_from_mesh(Mesh *mesh)
{
  return CDDM_from_mesh_ex(mesh, CD_REFERENCE, &CD_MASK_MESH);
}

DerivedMesh *CDDM_from_mesh_ex(Mesh *mesh,
                               eCDAllocType alloctype,
                               const CustomData_MeshMasks *mask)
{
  CDDerivedMesh *cddm = cdDM_create(__func__);
  DerivedMesh *dm = &cddm->dm;
  CustomData_MeshMasks cddata_masks = *mask;

  cddata_masks.lmask &= ~CD_MASK_MDISPS;

  /* this does a referenced copy, with an exception for fluidsim */

  DM_init(dm,
          DM_TYPE_CDDM,
          mesh->totvert,
          mesh->totedge,
          0 /* mesh->totface */,
          mesh->totloop,
          mesh->totpoly);

  /* This should actually be dm->deformedOnly = mesh->runtime.deformed_only,
   * but only if the original mesh had its deformed_only flag correctly set
   * (which isn't generally the case). */
  dm->deformedOnly = 1;
  dm->cd_flag = mesh->cd_flag;

  if (mesh->runtime.cd_dirty_vert & CD_MASK_NORMAL) {
    dm->dirty |= DM_DIRTY_NORMALS;
  }
  /* TODO DM_DIRTY_TESS_CDLAYERS ? Maybe not though,
   * since we probably want to switch to looptris? */

  CustomData_merge(&mesh->vdata, &dm->vertData, cddata_masks.vmask, alloctype, mesh->totvert);
  CustomData_merge(&mesh->edata, &dm->edgeData, cddata_masks.emask, alloctype, mesh->totedge);
  CustomData_merge(&mesh->fdata,
                   &dm->faceData,
                   cddata_masks.fmask | CD_MASK_ORIGINDEX,
                   alloctype,
                   0 /* mesh->totface */);
  CustomData_merge(&mesh->ldata, &dm->loopData, cddata_masks.lmask, alloctype, mesh->totloop);
  CustomData_merge(&mesh->pdata, &dm->polyData, cddata_masks.pmask, alloctype, mesh->totpoly);

  cddm->mvert = CustomData_get_layer(&dm->vertData, CD_MVERT);
  cddm->medge = CustomData_get_layer(&dm->edgeData, CD_MEDGE);
  cddm->mloop = CustomData_get_layer(&dm->loopData, CD_MLOOP);
  cddm->mpoly = CustomData_get_layer(&dm->polyData, CD_MPOLY);
#if 0
  cddm->mface = CustomData_get_layer(&dm->faceData, CD_MFACE);
#else
  cddm->mface = NULL;
#endif

  /* commented since even when CD_ORIGINDEX was first added this line fails
   * on the default cube, (after editmode toggle too) - campbell */
#if 0
  BLI_assert(CustomData_has_layer(&cddm->dm.faceData, CD_ORIGINDEX));
#endif

  return dm;
}

DerivedMesh *CDDM_from_curve(Object *ob)
{
  ListBase disp = {NULL, NULL};

  if (ob->runtime.curve_cache) {
    disp = ob->runtime.curve_cache->disp;
  }

  return CDDM_from_curve_displist(ob, &disp);
}

DerivedMesh *CDDM_from_curve_displist(Object *ob, ListBase *dispbase)
{
  Curve *cu = (Curve *)ob->data;
  DerivedMesh *dm;
  CDDerivedMesh *cddm;
  MVert *allvert;
  MEdge *alledge;
  MLoop *allloop;
  MPoly *allpoly;
  MLoopUV *alluv = NULL;
  int totvert, totedge, totloop, totpoly;
  bool use_orco_uv = (cu->flag & CU_UV_ORCO) != 0;

  if (BKE_mesh_nurbs_displist_to_mdata(ob,
                                       dispbase,
                                       &allvert,
                                       &totvert,
                                       &alledge,
                                       &totedge,
                                       &allloop,
                                       &allpoly,
                                       (use_orco_uv) ? &alluv : NULL,
                                       &totloop,
                                       &totpoly) != 0) {
    /* Error initializing mdata. This often happens when curve is empty */
    return CDDM_new(0, 0, 0, 0, 0);
  }

  dm = CDDM_new(totvert, totedge, 0, totloop, totpoly);
  dm->deformedOnly = 1;
  dm->dirty |= DM_DIRTY_NORMALS;

  cddm = (CDDerivedMesh *)dm;

  memcpy(cddm->mvert, allvert, totvert * sizeof(MVert));
  memcpy(cddm->medge, alledge, totedge * sizeof(MEdge));
  memcpy(cddm->mloop, allloop, totloop * sizeof(MLoop));
  memcpy(cddm->mpoly, allpoly, totpoly * sizeof(MPoly));

  if (alluv) {
    const char *uvname = "Orco";
    CustomData_add_layer_named(&cddm->dm.loopData, CD_MLOOPUV, CD_ASSIGN, alluv, totloop, uvname);
  }

  MEM_freeN(allvert);
  MEM_freeN(alledge);
  MEM_freeN(allloop);
  MEM_freeN(allpoly);

  return dm;
}

static void loops_to_customdata_corners(
    BMesh *bm, CustomData *facedata, int cdindex, const BMLoop *l3[3], int numCol, int numUV)
{
  const BMLoop *l;
  //  BMFace *f = l3[0]->f;
  MTFace *texface;
  MCol *mcol;
  MLoopCol *mloopcol;
  MLoopUV *mloopuv;
  int i, j, hasPCol = CustomData_has_layer(&bm->ldata, CD_PREVIEW_MLOOPCOL);

  for (i = 0; i < numUV; i++) {
    texface = CustomData_get_n(facedata, CD_MTFACE, cdindex, i);

    for (j = 0; j < 3; j++) {
      l = l3[j];
      mloopuv = CustomData_bmesh_get_n(&bm->ldata, l->head.data, CD_MLOOPUV, i);
      copy_v2_v2(texface->uv[j], mloopuv->uv);
    }
  }

  for (i = 0; i < numCol; i++) {
    mcol = CustomData_get_n(facedata, CD_MCOL, cdindex, i);

    for (j = 0; j < 3; j++) {
      l = l3[j];
      mloopcol = CustomData_bmesh_get_n(&bm->ldata, l->head.data, CD_MLOOPCOL, i);
      MESH_MLOOPCOL_TO_MCOL(mloopcol, &mcol[j]);
    }
  }

  if (hasPCol) {
    mcol = CustomData_get(facedata, cdindex, CD_PREVIEW_MCOL);

    for (j = 0; j < 3; j++) {
      l = l3[j];
      mloopcol = CustomData_bmesh_get(&bm->ldata, l->head.data, CD_PREVIEW_MLOOPCOL);
      MESH_MLOOPCOL_TO_MCOL(mloopcol, &mcol[j]);
    }
  }
}

/* TODO(campbell): remove, use BKE_mesh_from_bmesh_for_eval_nomain instead. */

/* used for both editbmesh and bmesh */
static DerivedMesh *cddm_from_bmesh_ex(struct BMesh *bm,
                                       const bool use_mdisps,
                                       /* EditBMesh vars for use_tessface */
                                       const bool use_tessface,
                                       const int em_tottri,
                                       const BMLoop *(*em_looptris)[3])
{
  DerivedMesh *dm = CDDM_new(
      bm->totvert, bm->totedge, use_tessface ? em_tottri : 0, bm->totloop, bm->totface);

  CDDerivedMesh *cddm = (CDDerivedMesh *)dm;
  BMIter iter;
  BMVert *eve;
  BMEdge *eed;
  BMFace *efa;
  MVert *mvert = cddm->mvert;
  MEdge *medge = cddm->medge;
  MFace *mface = cddm->mface;
  MLoop *mloop = cddm->mloop;
  MPoly *mpoly = cddm->mpoly;
  int numCol = CustomData_number_of_layers(&bm->ldata, CD_MLOOPCOL);
  int numUV = CustomData_number_of_layers(&bm->ldata, CD_MLOOPUV);
  int *index, add_orig;
  CustomData_MeshMasks mask = {0};
  unsigned int i, j;

  const int cd_vert_bweight_offset = CustomData_get_offset(&bm->vdata, CD_BWEIGHT);
  const int cd_edge_bweight_offset = CustomData_get_offset(&bm->edata, CD_BWEIGHT);
  const int cd_edge_crease_offset = CustomData_get_offset(&bm->edata, CD_CREASE);

  dm->deformedOnly = 1;

  /* don't add origindex layer if one already exists */
  add_orig = !CustomData_has_layer(&bm->pdata, CD_ORIGINDEX);

  mask = CD_MASK_DERIVEDMESH;
  if (use_mdisps) {
    mask.lmask |= CD_MASK_MDISPS;
  }

  /* don't process shapekeys, we only feed them through the modifier stack as needed,
   * e.g. for applying modifiers or the like*/
  mask.vmask &= ~CD_MASK_SHAPEKEY;
  CustomData_merge(&bm->vdata, &dm->vertData, mask.vmask, CD_CALLOC, dm->numVertData);
  CustomData_merge(&bm->edata, &dm->edgeData, mask.emask, CD_CALLOC, dm->numEdgeData);
  CustomData_merge(&bm->ldata, &dm->loopData, mask.lmask, CD_CALLOC, dm->numLoopData);
  CustomData_merge(&bm->pdata, &dm->polyData, mask.pmask, CD_CALLOC, dm->numPolyData);

  /* add tessellation mface layers */
  if (use_tessface) {
    CustomData_from_bmeshpoly(&dm->faceData, &dm->loopData, em_tottri);
  }

  index = dm->getVertDataArray(dm, CD_ORIGINDEX);

  BM_ITER_MESH_INDEX (eve, &iter, bm, BM_VERTS_OF_MESH, i) {
    MVert *mv = &mvert[i];

    copy_v3_v3(mv->co, eve->co);

    BM_elem_index_set(eve, i); /* set_inline */

    normal_float_to_short_v3(mv->no, eve->no);

    mv->flag = BM_vert_flag_to_mflag(eve);

    if (cd_vert_bweight_offset != -1) {
      mv->bweight = BM_ELEM_CD_GET_FLOAT_AS_UCHAR(eve, cd_vert_bweight_offset);
    }

    if (add_orig) {
      *index++ = i;
    }

    CustomData_from_bmesh_block(&bm->vdata, &dm->vertData, eve->head.data, i);
  }
  bm->elem_index_dirty &= ~BM_VERT;

  index = dm->getEdgeDataArray(dm, CD_ORIGINDEX);
  BM_ITER_MESH_INDEX (eed, &iter, bm, BM_EDGES_OF_MESH, i) {
    MEdge *med = &medge[i];

    BM_elem_index_set(eed, i); /* set_inline */

    med->v1 = BM_elem_index_get(eed->v1);
    med->v2 = BM_elem_index_get(eed->v2);

    med->flag = BM_edge_flag_to_mflag(eed);

    /* handle this differently to editmode switching,
     * only enable draw for single user edges rather then calculating angle */
    if ((med->flag & ME_EDGEDRAW) == 0) {
      if (eed->l && eed->l == eed->l->radial_next) {
        med->flag |= ME_EDGEDRAW;
      }
    }

    if (cd_edge_crease_offset != -1) {
      med->crease = BM_ELEM_CD_GET_FLOAT_AS_UCHAR(eed, cd_edge_crease_offset);
    }
    if (cd_edge_bweight_offset != -1) {
      med->bweight = BM_ELEM_CD_GET_FLOAT_AS_UCHAR(eed, cd_edge_bweight_offset);
    }

    CustomData_from_bmesh_block(&bm->edata, &dm->edgeData, eed->head.data, i);
    if (add_orig) {
      *index++ = i;
    }
  }
  bm->elem_index_dirty &= ~BM_EDGE;

  /* avoid this where possiblem, takes extra memory */
  if (use_tessface) {

    BM_mesh_elem_index_ensure(bm, BM_FACE);

    index = dm->getTessFaceDataArray(dm, CD_ORIGINDEX);
    for (i = 0; i < dm->numTessFaceData; i++) {
      MFace *mf = &mface[i];
      const BMLoop **l = em_looptris[i];
      efa = l[0]->f;

      mf->v1 = BM_elem_index_get(l[0]->v);
      mf->v2 = BM_elem_index_get(l[1]->v);
      mf->v3 = BM_elem_index_get(l[2]->v);
      mf->v4 = 0;
      mf->mat_nr = efa->mat_nr;
      mf->flag = BM_face_flag_to_mflag(efa);

      /* map mfaces to polygons in the same cddm intentionally */
      *index++ = BM_elem_index_get(efa);

      loops_to_customdata_corners(bm, &dm->faceData, i, l, numCol, numUV);
      test_index_face(mf, &dm->faceData, i, 3);
    }
  }

  index = CustomData_get_layer(&dm->polyData, CD_ORIGINDEX);
  j = 0;
  BM_ITER_MESH_INDEX (efa, &iter, bm, BM_FACES_OF_MESH, i) {
    BMLoop *l_iter;
    BMLoop *l_first;
    MPoly *mp = &mpoly[i];

    BM_elem_index_set(efa, i); /* set_inline */

    mp->totloop = efa->len;
    mp->flag = BM_face_flag_to_mflag(efa);
    mp->loopstart = j;
    mp->mat_nr = efa->mat_nr;

    l_iter = l_first = BM_FACE_FIRST_LOOP(efa);
    do {
      mloop->v = BM_elem_index_get(l_iter->v);
      mloop->e = BM_elem_index_get(l_iter->e);
      CustomData_from_bmesh_block(&bm->ldata, &dm->loopData, l_iter->head.data, j);

      BM_elem_index_set(l_iter, j); /* set_inline */

      j++;
      mloop++;
    } while ((l_iter = l_iter->next) != l_first);

    CustomData_from_bmesh_block(&bm->pdata, &dm->polyData, efa->head.data, i);

    if (add_orig) {
      *index++ = i;
    }
  }
  bm->elem_index_dirty &= ~(BM_FACE | BM_LOOP);

  dm->cd_flag = BM_mesh_cd_flag_from_bmesh(bm);

  return dm;
}

struct DerivedMesh *CDDM_from_bmesh(struct BMesh *bm, const bool use_mdisps)
{
  return cddm_from_bmesh_ex(bm,
                            use_mdisps,
                            false,
                            /* these vars are for editmesh only */
                            0,
                            NULL);
}

DerivedMesh *CDDM_from_editbmesh(BMEditMesh *em, const bool use_mdisps, const bool use_tessface)
{
  return cddm_from_bmesh_ex(em->bm,
                            use_mdisps,
                            /* editmesh */
                            use_tessface,
                            em->tottri,
                            (const BMLoop *(*)[3])em->looptris);
}

DerivedMesh *CDDM_copy(DerivedMesh *source)
{
  CDDerivedMesh *cddm = cdDM_create("CDDM_copy cddm");
  DerivedMesh *dm = &cddm->dm;
  int numVerts = source->numVertData;
  int numEdges = source->numEdgeData;
  int numTessFaces = 0;
  int numLoops = source->numLoopData;
  int numPolys = source->numPolyData;

  /* NOTE: Don't copy tessellation faces if not requested explicitly. */

  /* ensure these are created if they are made on demand */
  source->getVertDataArray(source, CD_ORIGINDEX);
  source->getEdgeDataArray(source, CD_ORIGINDEX);
  source->getPolyDataArray(source, CD_ORIGINDEX);

  /* this initializes dm, and copies all non mvert/medge/mface layers */
  DM_from_template(dm, source, DM_TYPE_CDDM, numVerts, numEdges, numTessFaces, numLoops, numPolys);
  dm->deformedOnly = source->deformedOnly;
  dm->cd_flag = source->cd_flag;
  dm->dirty = source->dirty;

  /* Tessellation data is never copied, so tag it here.
   * Only tag dirty layers if we really ignored tessellation faces.
   */
  dm->dirty |= DM_DIRTY_TESS_CDLAYERS;

  CustomData_copy_data(&source->vertData, &dm->vertData, 0, 0, numVerts);
  CustomData_copy_data(&source->edgeData, &dm->edgeData, 0, 0, numEdges);

  /* now add mvert/medge/mface layers */
  cddm->mvert = source->dupVertArray(source);
  cddm->medge = source->dupEdgeArray(source);

  CustomData_add_layer(&dm->vertData, CD_MVERT, CD_ASSIGN, cddm->mvert, numVerts);
  CustomData_add_layer(&dm->edgeData, CD_MEDGE, CD_ASSIGN, cddm->medge, numEdges);

  DM_DupPolys(source, dm);

  cddm->mloop = CustomData_get_layer(&dm->loopData, CD_MLOOP);
  cddm->mpoly = CustomData_get_layer(&dm->polyData, CD_MPOLY);

  return dm;
}

/* note, the CD_ORIGINDEX layers are all 0, so if there is a direct
 * relationship between mesh data this needs to be set by the caller. */
DerivedMesh *CDDM_from_template_ex(DerivedMesh *source,
                                   int numVerts,
                                   int numEdges,
                                   int numTessFaces,
                                   int numLoops,
                                   int numPolys,
                                   const CustomData_MeshMasks *mask)
{
  CDDerivedMesh *cddm = cdDM_create("CDDM_from_template dest");
  DerivedMesh *dm = &cddm->dm;

  /* ensure these are created if they are made on demand */
  source->getVertDataArray(source, CD_ORIGINDEX);
  source->getEdgeDataArray(source, CD_ORIGINDEX);
  source->getTessFaceDataArray(source, CD_ORIGINDEX);
  source->getPolyDataArray(source, CD_ORIGINDEX);

  /* this does a copy of all non mvert/medge/mface layers */
  DM_from_template_ex(
      dm, source, DM_TYPE_CDDM, numVerts, numEdges, numTessFaces, numLoops, numPolys, mask);

  /* now add mvert/medge/mface layers */
  CustomData_add_layer(&dm->vertData, CD_MVERT, CD_CALLOC, NULL, numVerts);
  CustomData_add_layer(&dm->edgeData, CD_MEDGE, CD_CALLOC, NULL, numEdges);
  CustomData_add_layer(&dm->faceData, CD_MFACE, CD_CALLOC, NULL, numTessFaces);
  CustomData_add_layer(&dm->loopData, CD_MLOOP, CD_CALLOC, NULL, numLoops);
  CustomData_add_layer(&dm->polyData, CD_MPOLY, CD_CALLOC, NULL, numPolys);

  if (!CustomData_get_layer(&dm->vertData, CD_ORIGINDEX)) {
    CustomData_add_layer(&dm->vertData, CD_ORIGINDEX, CD_CALLOC, NULL, numVerts);
  }
  if (!CustomData_get_layer(&dm->edgeData, CD_ORIGINDEX)) {
    CustomData_add_layer(&dm->edgeData, CD_ORIGINDEX, CD_CALLOC, NULL, numEdges);
  }
  if (!CustomData_get_layer(&dm->faceData, CD_ORIGINDEX)) {
    CustomData_add_layer(&dm->faceData, CD_ORIGINDEX, CD_CALLOC, NULL, numTessFaces);
  }

  cddm->mvert = CustomData_get_layer(&dm->vertData, CD_MVERT);
  cddm->medge = CustomData_get_layer(&dm->edgeData, CD_MEDGE);
  cddm->mface = CustomData_get_layer(&dm->faceData, CD_MFACE);
  cddm->mloop = CustomData_get_layer(&dm->loopData, CD_MLOOP);
  cddm->mpoly = CustomData_get_layer(&dm->polyData, CD_MPOLY);

  return dm;
}
DerivedMesh *CDDM_from_template(
    DerivedMesh *source, int numVerts, int numEdges, int numTessFaces, int numLoops, int numPolys)
{
  return CDDM_from_template_ex(
      source, numVerts, numEdges, numTessFaces, numLoops, numPolys, &CD_MASK_DERIVEDMESH);
}

void CDDM_apply_vert_coords(DerivedMesh *dm, float (*vertCoords)[3])
{
  CDDerivedMesh *cddm = (CDDerivedMesh *)dm;
  MVert *vert;
  int i;

  /* this will just return the pointer if it wasn't a referenced layer */
  vert = CustomData_duplicate_referenced_layer(&dm->vertData, CD_MVERT, dm->numVertData);
  cddm->mvert = vert;

  for (i = 0; i < dm->numVertData; ++i, ++vert) {
    copy_v3_v3(vert->co, vertCoords[i]);
  }

  cddm->dm.dirty |= DM_DIRTY_NORMALS;
}

void CDDM_apply_vert_normals(DerivedMesh *dm, short (*vertNormals)[3])
{
  CDDerivedMesh *cddm = (CDDerivedMesh *)dm;
  MVert *vert;
  int i;

  /* this will just return the pointer if it wasn't a referenced layer */
  vert = CustomData_duplicate_referenced_layer(&dm->vertData, CD_MVERT, dm->numVertData);
  cddm->mvert = vert;

  for (i = 0; i < dm->numVertData; ++i, ++vert) {
    copy_v3_v3_short(vert->no, vertNormals[i]);
  }

  cddm->dm.dirty &= ~DM_DIRTY_NORMALS;
}

void CDDM_calc_normals_mapping_ex(DerivedMesh *dm, const bool only_face_normals)
{
  CDDerivedMesh *cddm = (CDDerivedMesh *)dm;
  float(*face_nors)[3] = NULL;

  if (dm->numVertData == 0) {
    cddm->dm.dirty &= ~DM_DIRTY_NORMALS;
    return;
  }

  /* now we skip calculating vertex normals for referenced layer,
   * no need to duplicate verts.
   * WATCH THIS, bmesh only change!,
   * need to take care of the side effects here - campbell */
#if 0
  /* we don't want to overwrite any referenced layers */
  cddm->mvert = CustomData_duplicate_referenced_layer(&dm->vertData, CD_MVERT, dm->numVertData);
#endif

#if 0
  if (dm->numTessFaceData == 0) {
    /* No tessellation on this mesh yet, need to calculate one.
     *
     * Important not to update face normals from polys since it
     * interferes with assigning the new normal layer in the following code.
     */
    CDDM_recalc_tessellation_ex(dm, false);
  }
  else {
    /* A tessellation already exists, it should always have a CD_ORIGINDEX */
    BLI_assert(CustomData_has_layer(&dm->faceData, CD_ORIGINDEX));
    CustomData_free_layers(&dm->faceData, CD_NORMAL, dm->numTessFaceData);
  }
#endif

  face_nors = MEM_malloc_arrayN(dm->numPolyData, sizeof(*face_nors), "face_nors");

  /* calculate face normals */
  BKE_mesh_calc_normals_poly(cddm->mvert,
                             NULL,
                             dm->numVertData,
                             CDDM_get_loops(dm),
                             CDDM_get_polys(dm),
                             dm->numLoopData,
                             dm->numPolyData,
                             face_nors,
                             only_face_normals);

  CustomData_add_layer(&dm->polyData, CD_NORMAL, CD_ASSIGN, face_nors, dm->numPolyData);

  cddm->dm.dirty &= ~DM_DIRTY_NORMALS;
}

void CDDM_calc_normals_mapping(DerivedMesh *dm)
{
  /* use this to skip calculating normals on original vert's, this may need to be changed */
  const bool only_face_normals = CustomData_is_referenced_layer(&dm->vertData, CD_MVERT);

  CDDM_calc_normals_mapping_ex(dm, only_face_normals);
}

#if 0
/* bmesh note: this matches what we have in trunk */
void CDDM_calc_normals(DerivedMesh *dm)
{
  CDDerivedMesh *cddm = (CDDerivedMesh *)dm;
  float(*poly_nors)[3];

  if (dm->numVertData == 0) {
    return;
  }

  /* we don't want to overwrite any referenced layers */
  cddm->mvert = CustomData_duplicate_referenced_layer(&dm->vertData, CD_MVERT, dm->numVertData);

  /* fill in if it exists */
  poly_nors = CustomData_get_layer(&dm->polyData, CD_NORMAL);
  if (!poly_nors) {
    poly_nors = CustomData_add_layer(&dm->polyData, CD_NORMAL, CD_CALLOC, NULL, dm->numPolyData);
  }

  BKE_mesh_calc_normals_poly(cddm->mvert,
                             dm->numVertData,
                             CDDM_get_loops(dm),
                             CDDM_get_polys(dm),
                             dm->numLoopData,
                             dm->numPolyData,
                             poly_nors,
                             false);

  cddm->dm.dirty &= ~DM_DIRTY_NORMALS;
}
#else

/* poly normal layer is now only for final display */
void CDDM_calc_normals(DerivedMesh *dm)
{
  CDDerivedMesh *cddm = (CDDerivedMesh *)dm;

  /* we don't want to overwrite any referenced layers */
  cddm->mvert = CustomData_duplicate_referenced_layer(&dm->vertData, CD_MVERT, dm->numVertData);

  BKE_mesh_calc_normals_poly(cddm->mvert,
                             NULL,
                             dm->numVertData,
                             CDDM_get_loops(dm),
                             CDDM_get_polys(dm),
                             dm->numLoopData,
                             dm->numPolyData,
                             NULL,
                             false);

  cddm->dm.dirty &= ~DM_DIRTY_NORMALS;
}

#endif

void CDDM_calc_loop_normals(DerivedMesh *dm, const bool use_split_normals, const float split_angle)
{
  CDDM_calc_loop_normals_spacearr(dm, use_split_normals, split_angle, NULL);
}

/* #define DEBUG_CLNORS */
#ifdef DEBUG_CLNORS
#  include "BLI_linklist.h"
#endif

void CDDM_calc_loop_normals_spacearr(DerivedMesh *dm,
                                     const bool use_split_normals,
                                     const float split_angle,
                                     MLoopNorSpaceArray *r_lnors_spacearr)
{
  MVert *mverts = dm->getVertArray(dm);
  MEdge *medges = dm->getEdgeArray(dm);
  MLoop *mloops = dm->getLoopArray(dm);
  MPoly *mpolys = dm->getPolyArray(dm);

  CustomData *ldata, *pdata;

  float(*lnors)[3];
  short(*clnor_data)[2];
  float(*pnors)[3];

  const int numVerts = dm->getNumVerts(dm);
  const int numEdges = dm->getNumEdges(dm);
  const int numLoops = dm->getNumLoops(dm);
  const int numPolys = dm->getNumPolys(dm);

  ldata = dm->getLoopDataLayout(dm);
  if (CustomData_has_layer(ldata, CD_NORMAL)) {
    lnors = CustomData_get_layer(ldata, CD_NORMAL);
  }
  else {
    lnors = CustomData_add_layer(ldata, CD_NORMAL, CD_CALLOC, NULL, numLoops);
  }

  /* Compute poly (always needed) and vert normals. */
  /* Note we can't use DM_ensure_normals, since it won't keep computed poly nors... */
  pdata = dm->getPolyDataLayout(dm);
  pnors = CustomData_get_layer(pdata, CD_NORMAL);
  if (!pnors) {
    pnors = CustomData_add_layer(pdata, CD_NORMAL, CD_CALLOC, NULL, numPolys);
  }
  BKE_mesh_calc_normals_poly(mverts,
                             NULL,
                             numVerts,
                             mloops,
                             mpolys,
                             numLoops,
                             numPolys,
                             pnors,
                             (dm->dirty & DM_DIRTY_NORMALS) ? false : true);

  dm->dirty &= ~DM_DIRTY_NORMALS;

  clnor_data = CustomData_get_layer(ldata, CD_CUSTOMLOOPNORMAL);

  BKE_mesh_normals_loop_split(mverts,
                              numVerts,
                              medges,
                              numEdges,
                              mloops,
                              lnors,
                              numLoops,
                              mpolys,
                              (const float(*)[3])pnors,
                              numPolys,
                              use_split_normals,
                              split_angle,
                              r_lnors_spacearr,
                              clnor_data,
                              NULL);
#ifdef DEBUG_CLNORS
  if (r_lnors_spacearr) {
    int i;
    for (i = 0; i < numLoops; i++) {
      if (r_lnors_spacearr->lspacearr[i]->ref_alpha != 0.0f) {
        LinkNode *loops = r_lnors_spacearr->lspacearr[i]->loops;
        printf("Loop %d uses lnor space %p:\n", i, r_lnors_spacearr->lspacearr[i]);
        print_v3("\tfinal lnor", lnors[i]);
        print_v3("\tauto lnor", r_lnors_spacearr->lspacearr[i]->vec_lnor);
        print_v3("\tref_vec", r_lnors_spacearr->lspacearr[i]->vec_ref);
        printf("\talpha: %f\n\tbeta: %f\n\tloops: %p\n",
               r_lnors_spacearr->lspacearr[i]->ref_alpha,
               r_lnors_spacearr->lspacearr[i]->ref_beta,
               r_lnors_spacearr->lspacearr[i]->loops);
        printf("\t\t(shared with loops");
        while (loops) {
          printf(" %d", POINTER_AS_INT(loops->link));
          loops = loops->next;
        }
        printf(")\n");
      }
      else {
        printf("Loop %d has no lnor space\n", i);
      }
    }
  }
#endif
}

void CDDM_lower_num_verts(DerivedMesh *dm, int numVerts)
{
  BLI_assert(numVerts >= 0);
  if (numVerts < dm->numVertData) {
    CustomData_free_elem(&dm->vertData, numVerts, dm->numVertData - numVerts);
  }

  dm->numVertData = numVerts;
}

void CDDM_lower_num_edges(DerivedMesh *dm, int numEdges)
{
  BLI_assert(numEdges >= 0);
  if (numEdges < dm->numEdgeData) {
    CustomData_free_elem(&dm->edgeData, numEdges, dm->numEdgeData - numEdges);
  }

  dm->numEdgeData = numEdges;
}

void CDDM_lower_num_tessfaces(DerivedMesh *dm, int numTessFaces)
{
  BLI_assert(numTessFaces >= 0);
  if (numTessFaces < dm->numTessFaceData) {
    CustomData_free_elem(&dm->faceData, numTessFaces, dm->numTessFaceData - numTessFaces);
  }

  dm->numTessFaceData = numTessFaces;
}

void CDDM_lower_num_loops(DerivedMesh *dm, int numLoops)
{
  BLI_assert(numLoops >= 0);
  if (numLoops < dm->numLoopData) {
    CustomData_free_elem(&dm->loopData, numLoops, dm->numLoopData - numLoops);
  }

  dm->numLoopData = numLoops;
}

void CDDM_lower_num_polys(DerivedMesh *dm, int numPolys)
{
  BLI_assert(numPolys >= 0);
  if (numPolys < dm->numPolyData) {
    CustomData_free_elem(&dm->polyData, numPolys, dm->numPolyData - numPolys);
  }

  dm->numPolyData = numPolys;
}

/* mesh element access functions */

MVert *CDDM_get_vert(DerivedMesh *dm, int index)
{
  return &((CDDerivedMesh *)dm)->mvert[index];
}

MEdge *CDDM_get_edge(DerivedMesh *dm, int index)
{
  return &((CDDerivedMesh *)dm)->medge[index];
}

MFace *CDDM_get_tessface(DerivedMesh *dm, int index)
{
  return &((CDDerivedMesh *)dm)->mface[index];
}

MLoop *CDDM_get_loop(DerivedMesh *dm, int index)
{
  return &((CDDerivedMesh *)dm)->mloop[index];
}

MPoly *CDDM_get_poly(DerivedMesh *dm, int index)
{
  return &((CDDerivedMesh *)dm)->mpoly[index];
}

/* array access functions */

MVert *CDDM_get_verts(DerivedMesh *dm)
{
  return ((CDDerivedMesh *)dm)->mvert;
}

MEdge *CDDM_get_edges(DerivedMesh *dm)
{
  return ((CDDerivedMesh *)dm)->medge;
}

MFace *CDDM_get_tessfaces(DerivedMesh *dm)
{
  return ((CDDerivedMesh *)dm)->mface;
}

MLoop *CDDM_get_loops(DerivedMesh *dm)
{
  return ((CDDerivedMesh *)dm)->mloop;
}

MPoly *CDDM_get_polys(DerivedMesh *dm)
{
  return ((CDDerivedMesh *)dm)->mpoly;
}

void CDDM_set_mvert(DerivedMesh *dm, MVert *mvert)
{
  CDDerivedMesh *cddm = (CDDerivedMesh *)dm;

  if (!CustomData_has_layer(&dm->vertData, CD_MVERT)) {
    CustomData_add_layer(&dm->vertData, CD_MVERT, CD_ASSIGN, mvert, dm->numVertData);
  }

  cddm->mvert = mvert;
}

void CDDM_set_medge(DerivedMesh *dm, MEdge *medge)
{
  CDDerivedMesh *cddm = (CDDerivedMesh *)dm;

  if (!CustomData_has_layer(&dm->edgeData, CD_MEDGE)) {
    CustomData_add_layer(&dm->edgeData, CD_MEDGE, CD_ASSIGN, medge, dm->numEdgeData);
  }

  cddm->medge = medge;
}

void CDDM_set_mface(DerivedMesh *dm, MFace *mface)
{
  CDDerivedMesh *cddm = (CDDerivedMesh *)dm;

  if (!CustomData_has_layer(&dm->faceData, CD_MFACE)) {
    CustomData_add_layer(&dm->faceData, CD_MFACE, CD_ASSIGN, mface, dm->numTessFaceData);
  }

  cddm->mface = mface;
}

void CDDM_set_mloop(DerivedMesh *dm, MLoop *mloop)
{
  CDDerivedMesh *cddm = (CDDerivedMesh *)dm;

  if (!CustomData_has_layer(&dm->loopData, CD_MLOOP)) {
    CustomData_add_layer(&dm->loopData, CD_MLOOP, CD_ASSIGN, mloop, dm->numLoopData);
  }

  cddm->mloop = mloop;
}

void CDDM_set_mpoly(DerivedMesh *dm, MPoly *mpoly)
{
  CDDerivedMesh *cddm = (CDDerivedMesh *)dm;

  if (!CustomData_has_layer(&dm->polyData, CD_MPOLY)) {
    CustomData_add_layer(&dm->polyData, CD_MPOLY, CD_ASSIGN, mpoly, dm->numPolyData);
  }

  cddm->mpoly = mpoly;
}
