/* * 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 #include #include 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, 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; }