/* * ***** 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/mesh_convert.c * \ingroup bke */ #include "MEM_guardedalloc.h" #include "DNA_scene_types.h" #include "DNA_key_types.h" #include "DNA_material_types.h" #include "DNA_meta_types.h" #include "DNA_object_types.h" #include "DNA_mesh_types.h" #include "DNA_curve_types.h" #include "BLI_utildefines.h" #include "BLI_math.h" #include "BLI_listbase.h" #include "BLI_edgehash.h" #include "BKE_main.h" #include "BKE_DerivedMesh.h" #include "BKE_global.h" #include "BKE_key.h" #include "BKE_mesh.h" #include "BKE_mesh_runtime.h" #include "BKE_modifier.h" #include "BKE_displist.h" #include "BKE_library.h" #include "BKE_material.h" #include "BKE_mball.h" /* these 2 are only used by conversion functions */ #include "BKE_curve.h" /* -- */ #include "BKE_object.h" #include "DEG_depsgraph.h" #include "DEG_depsgraph_query.h" /* Define for cases when you want extra validation of mesh * after certain modifications. */ // #undef VALIDATE_MESH #ifdef VALIDATE_MESH # define ASSERT_IS_VALID_MESH(mesh) (BLI_assert((mesh == NULL) || (BKE_mesh_is_valid(mesh) == true))) #else # define ASSERT_IS_VALID_MESH(mesh) #endif void BKE_mesh_from_metaball(ListBase *lb, Mesh *me) { DispList *dl; MVert *mvert; MLoop *mloop, *allloop; MPoly *mpoly; const float *nors, *verts; int a, *index; dl = lb->first; if (dl == NULL) return; if (dl->type == DL_INDEX4) { mvert = CustomData_add_layer(&me->vdata, CD_MVERT, CD_CALLOC, NULL, dl->nr); allloop = mloop = CustomData_add_layer(&me->ldata, CD_MLOOP, CD_CALLOC, NULL, dl->parts * 4); mpoly = CustomData_add_layer(&me->pdata, CD_MPOLY, CD_CALLOC, NULL, dl->parts); me->mvert = mvert; me->mloop = mloop; me->mpoly = mpoly; me->totvert = dl->nr; me->totpoly = dl->parts; a = dl->nr; nors = dl->nors; verts = dl->verts; while (a--) { copy_v3_v3(mvert->co, verts); normal_float_to_short_v3(mvert->no, nors); mvert++; nors += 3; verts += 3; } a = dl->parts; index = dl->index; while (a--) { int count = index[2] != index[3] ? 4 : 3; mloop[0].v = index[0]; mloop[1].v = index[1]; mloop[2].v = index[2]; if (count == 4) mloop[3].v = index[3]; mpoly->totloop = count; mpoly->loopstart = (int)(mloop - allloop); mpoly->flag = ME_SMOOTH; mpoly++; mloop += count; me->totloop += count; index += 4; } BKE_mesh_update_customdata_pointers(me, true); BKE_mesh_calc_normals(me); BKE_mesh_calc_edges(me, true, false); } } /** * Specialized function to use when we _know_ existing edges don't overlap with poly edges. */ static void make_edges_mdata_extend( MEdge **r_alledge, int *r_totedge, const MPoly *mpoly, MLoop *mloop, const int totpoly) { int totedge = *r_totedge; int totedge_new; EdgeHash *eh; unsigned int eh_reserve; const MPoly *mp; int i; eh_reserve = max_ii(totedge, BLI_EDGEHASH_SIZE_GUESS_FROM_POLYS(totpoly)); eh = BLI_edgehash_new_ex(__func__, eh_reserve); for (i = 0, mp = mpoly; i < totpoly; i++, mp++) { BKE_mesh_poly_edgehash_insert(eh, mp, mloop + mp->loopstart); } totedge_new = BLI_edgehash_len(eh); #ifdef DEBUG /* ensure that theres no overlap! */ if (totedge_new) { MEdge *medge = *r_alledge; for (i = 0; i < totedge; i++, medge++) { BLI_assert(BLI_edgehash_haskey(eh, medge->v1, medge->v2) == false); } } #endif if (totedge_new) { EdgeHashIterator *ehi; MEdge *medge; unsigned int e_index = totedge; *r_alledge = medge = (*r_alledge ? MEM_reallocN(*r_alledge, sizeof(MEdge) * (totedge + totedge_new)) : MEM_calloc_arrayN(totedge_new, sizeof(MEdge), __func__)); medge += totedge; totedge += totedge_new; /* --- */ for (ehi = BLI_edgehashIterator_new(eh); BLI_edgehashIterator_isDone(ehi) == false; BLI_edgehashIterator_step(ehi), ++medge, e_index++) { BLI_edgehashIterator_getKey(ehi, &medge->v1, &medge->v2); BLI_edgehashIterator_setValue(ehi, SET_UINT_IN_POINTER(e_index)); medge->crease = medge->bweight = 0; medge->flag = ME_EDGEDRAW | ME_EDGERENDER; } BLI_edgehashIterator_free(ehi); *r_totedge = totedge; for (i = 0, mp = mpoly; i < totpoly; i++, mp++) { MLoop *l = &mloop[mp->loopstart]; MLoop *l_prev = (l + (mp->totloop - 1)); int j; for (j = 0; j < mp->totloop; j++, l++) { /* lookup hashed edge index */ l_prev->e = GET_UINT_FROM_POINTER(BLI_edgehash_lookup(eh, l_prev->v, l->v)); l_prev = l; } } } BLI_edgehash_free(eh, NULL); } /* Initialize mverts, medges and, faces for converting nurbs to mesh and derived mesh */ /* return non-zero on error */ int BKE_mesh_nurbs_to_mdata( Object *ob, MVert **r_allvert, int *r_totvert, MEdge **r_alledge, int *r_totedge, MLoop **r_allloop, MPoly **r_allpoly, int *r_totloop, int *r_totpoly) { ListBase disp = {NULL, NULL}; if (ob->runtime.curve_cache) { disp = ob->runtime.curve_cache->disp; } return BKE_mesh_nurbs_displist_to_mdata( ob, &disp, r_allvert, r_totvert, r_alledge, r_totedge, r_allloop, r_allpoly, NULL, r_totloop, r_totpoly); } /* BMESH: this doesn't calculate all edges from polygons, * only free standing edges are calculated */ /* Initialize mverts, medges and, faces for converting nurbs to mesh and derived mesh */ /* use specified dispbase */ int BKE_mesh_nurbs_displist_to_mdata( Object *ob, const ListBase *dispbase, MVert **r_allvert, int *r_totvert, MEdge **r_alledge, int *r_totedge, MLoop **r_allloop, MPoly **r_allpoly, MLoopUV **r_alluv, int *r_totloop, int *r_totpoly) { Curve *cu = ob->data; DispList *dl; MVert *mvert; MPoly *mpoly; MLoop *mloop; MLoopUV *mloopuv = NULL; MEdge *medge; const float *data; int a, b, ofs, vertcount, startvert, totvert = 0, totedge = 0, totloop = 0, totpoly = 0; int p1, p2, p3, p4, *index; const bool conv_polys = ((CU_DO_2DFILL(cu) == false) || /* 2d polys are filled with DL_INDEX3 displists */ (ob->type == OB_SURF)); /* surf polys are never filled */ /* count */ dl = dispbase->first; while (dl) { if (dl->type == DL_SEGM) { totvert += dl->parts * dl->nr; totedge += dl->parts * (dl->nr - 1); } else if (dl->type == DL_POLY) { if (conv_polys) { totvert += dl->parts * dl->nr; totedge += dl->parts * dl->nr; } } else if (dl->type == DL_SURF) { int tot; totvert += dl->parts * dl->nr; tot = (dl->parts - 1 + ((dl->flag & DL_CYCL_V) == 2)) * (dl->nr - 1 + (dl->flag & DL_CYCL_U)); totpoly += tot; totloop += tot * 4; } else if (dl->type == DL_INDEX3) { int tot; totvert += dl->nr; tot = dl->parts; totpoly += tot; totloop += tot * 3; } dl = dl->next; } if (totvert == 0) { /* error("can't convert"); */ /* Make Sure you check ob->data is a curve */ return -1; } *r_allvert = mvert = MEM_calloc_arrayN(totvert, sizeof(MVert), "nurbs_init mvert"); *r_alledge = medge = MEM_calloc_arrayN(totedge, sizeof(MEdge), "nurbs_init medge"); *r_allloop = mloop = MEM_calloc_arrayN(totpoly, 4 * sizeof(MLoop), "nurbs_init mloop"); // totloop *r_allpoly = mpoly = MEM_calloc_arrayN(totpoly, sizeof(MPoly), "nurbs_init mloop"); if (r_alluv) *r_alluv = mloopuv = MEM_calloc_arrayN(totpoly, 4 * sizeof(MLoopUV), "nurbs_init mloopuv"); /* verts and faces */ vertcount = 0; dl = dispbase->first; while (dl) { const bool is_smooth = (dl->rt & CU_SMOOTH) != 0; if (dl->type == DL_SEGM) { startvert = vertcount; a = dl->parts * dl->nr; data = dl->verts; while (a--) { copy_v3_v3(mvert->co, data); data += 3; vertcount++; mvert++; } for (a = 0; a < dl->parts; a++) { ofs = a * dl->nr; for (b = 1; b < dl->nr; b++) { medge->v1 = startvert + ofs + b - 1; medge->v2 = startvert + ofs + b; medge->flag = ME_LOOSEEDGE | ME_EDGERENDER | ME_EDGEDRAW; medge++; } } } else if (dl->type == DL_POLY) { if (conv_polys) { startvert = vertcount; a = dl->parts * dl->nr; data = dl->verts; while (a--) { copy_v3_v3(mvert->co, data); data += 3; vertcount++; mvert++; } for (a = 0; a < dl->parts; a++) { ofs = a * dl->nr; for (b = 0; b < dl->nr; b++) { medge->v1 = startvert + ofs + b; if (b == dl->nr - 1) medge->v2 = startvert + ofs; else medge->v2 = startvert + ofs + b + 1; medge->flag = ME_LOOSEEDGE | ME_EDGERENDER | ME_EDGEDRAW; medge++; } } } } else if (dl->type == DL_INDEX3) { startvert = vertcount; a = dl->nr; data = dl->verts; while (a--) { copy_v3_v3(mvert->co, data); data += 3; vertcount++; mvert++; } a = dl->parts; index = dl->index; while (a--) { mloop[0].v = startvert + index[0]; mloop[1].v = startvert + index[2]; mloop[2].v = startvert + index[1]; mpoly->loopstart = (int)(mloop - (*r_allloop)); mpoly->totloop = 3; mpoly->mat_nr = dl->col; if (mloopuv) { int i; for (i = 0; i < 3; i++, mloopuv++) { mloopuv->uv[0] = (mloop[i].v - startvert) / (float)(dl->nr - 1); mloopuv->uv[1] = 0.0f; } } if (is_smooth) mpoly->flag |= ME_SMOOTH; mpoly++; mloop += 3; index += 3; } } else if (dl->type == DL_SURF) { startvert = vertcount; a = dl->parts * dl->nr; data = dl->verts; while (a--) { copy_v3_v3(mvert->co, data); data += 3; vertcount++; mvert++; } for (a = 0; a < dl->parts; a++) { if ( (dl->flag & DL_CYCL_V) == 0 && a == dl->parts - 1) break; if (dl->flag & DL_CYCL_U) { /* p2 -> p1 -> */ p1 = startvert + dl->nr * a; /* p4 -> p3 -> */ p2 = p1 + dl->nr - 1; /* -----> next row */ p3 = p1 + dl->nr; p4 = p2 + dl->nr; b = 0; } else { p2 = startvert + dl->nr * a; p1 = p2 + 1; p4 = p2 + dl->nr; p3 = p1 + dl->nr; b = 1; } if ( (dl->flag & DL_CYCL_V) && a == dl->parts - 1) { p3 -= dl->parts * dl->nr; p4 -= dl->parts * dl->nr; } for (; b < dl->nr; b++) { mloop[0].v = p1; mloop[1].v = p3; mloop[2].v = p4; mloop[3].v = p2; mpoly->loopstart = (int)(mloop - (*r_allloop)); mpoly->totloop = 4; mpoly->mat_nr = dl->col; if (mloopuv) { int orco_sizeu = dl->nr - 1; int orco_sizev = dl->parts - 1; int i; /* exception as handled in convertblender.c too */ if (dl->flag & DL_CYCL_U) { orco_sizeu++; if (dl->flag & DL_CYCL_V) orco_sizev++; } else if (dl->flag & DL_CYCL_V) { orco_sizev++; } for (i = 0; i < 4; i++, mloopuv++) { /* find uv based on vertex index into grid array */ int v = mloop[i].v - startvert; mloopuv->uv[0] = (v / dl->nr) / (float)orco_sizev; mloopuv->uv[1] = (v % dl->nr) / (float)orco_sizeu; /* cyclic correction */ if ((i == 1 || i == 2) && mloopuv->uv[0] == 0.0f) mloopuv->uv[0] = 1.0f; if ((i == 0 || i == 1) && mloopuv->uv[1] == 0.0f) mloopuv->uv[1] = 1.0f; } } if (is_smooth) mpoly->flag |= ME_SMOOTH; mpoly++; mloop += 4; p4 = p3; p3++; p2 = p1; p1++; } } } dl = dl->next; } if (totpoly) { make_edges_mdata_extend( r_alledge, &totedge, *r_allpoly, *r_allloop, totpoly); } *r_totpoly = totpoly; *r_totloop = totloop; *r_totedge = totedge; *r_totvert = totvert; return 0; } Mesh *BKE_mesh_new_nomain_from_curve_displist(Object *ob, ListBase *dispbase) { Curve *cu = ob->data; Mesh *mesh; 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 BKE_mesh_new_nomain(0, 0, 0, 0, 0); } mesh = BKE_mesh_new_nomain(totvert, totedge, 0, totloop, totpoly); mesh->runtime.cd_dirty_vert |= CD_MASK_NORMAL; memcpy(mesh->mvert, allvert, totvert * sizeof(MVert)); memcpy(mesh->medge, alledge, totedge * sizeof(MEdge)); memcpy(mesh->mloop, allloop, totloop * sizeof(MLoop)); memcpy(mesh->mpoly, allpoly, totpoly * sizeof(MPoly)); if (alluv) { const char *uvname = "Orco"; CustomData_add_layer_named(&mesh->ldata, CD_MLOOPUV, CD_ASSIGN, alluv, totloop, uvname); } MEM_freeN(allvert); MEM_freeN(alledge); MEM_freeN(allloop); MEM_freeN(allpoly); return mesh; } Mesh *BKE_mesh_new_nomain_from_curve(Object *ob) { ListBase disp = {NULL, NULL}; if (ob->runtime.curve_cache) { disp = ob->runtime.curve_cache->disp; } return BKE_mesh_new_nomain_from_curve_displist(ob, &disp); } /* this may fail replacing ob->data, be sure to check ob->type */ void BKE_mesh_from_nurbs_displist( Main *bmain, Object *ob, ListBase *dispbase, const bool use_orco_uv, const char *obdata_name, bool temporary) { Object *ob1; DerivedMesh *dm = ob->derivedFinal; Mesh *me; Curve *cu; MVert *allvert = NULL; MEdge *alledge = NULL; MLoop *allloop = NULL; MLoopUV *alluv = NULL; MPoly *allpoly = NULL; int totvert, totedge, totloop, totpoly; cu = ob->data; if (dm == NULL) { 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 */ return; } /* make mesh */ me = BKE_mesh_add(bmain, obdata_name); me->totvert = totvert; me->totedge = totedge; me->totloop = totloop; me->totpoly = totpoly; me->mvert = CustomData_add_layer(&me->vdata, CD_MVERT, CD_ASSIGN, allvert, me->totvert); me->medge = CustomData_add_layer(&me->edata, CD_MEDGE, CD_ASSIGN, alledge, me->totedge); me->mloop = CustomData_add_layer(&me->ldata, CD_MLOOP, CD_ASSIGN, allloop, me->totloop); me->mpoly = CustomData_add_layer(&me->pdata, CD_MPOLY, CD_ASSIGN, allpoly, me->totpoly); if (alluv) { const char *uvname = "Orco"; me->mloopuv = CustomData_add_layer_named(&me->ldata, CD_MLOOPUV, CD_ASSIGN, alluv, me->totloop, uvname); } BKE_mesh_calc_normals(me); } else { me = BKE_mesh_add(bmain, obdata_name); DM_to_mesh(dm, me, ob, CD_MASK_MESH, false); } me->totcol = cu->totcol; me->mat = cu->mat; /* Copy evaluated texture space from curve to mesh. * * Note that we disable auto texture space feature since that will cause * texture space to evaluate differently for curve and mesh, since curve * uses CV to calculate bounding box, and mesh uses what is coming from * tessellated curve. */ me->texflag = cu->texflag & ~CU_AUTOSPACE; copy_v3_v3(me->loc, cu->loc); copy_v3_v3(me->size, cu->size); copy_v3_v3(me->rot, cu->rot); BKE_mesh_texspace_calc(me); cu->mat = NULL; cu->totcol = 0; /* Do not decrement ob->data usercount here, it's done at end of func with BKE_libblock_free_us() call. */ ob->data = me; ob->type = OB_MESH; /* other users */ ob1 = bmain->object.first; while (ob1) { if (ob1->data == cu) { ob1->type = OB_MESH; id_us_min((ID *)ob1->data); ob1->data = ob->data; id_us_plus((ID *)ob1->data); } ob1 = ob1->id.next; } if (temporary) { /* For temporary objects in BKE_mesh_new_from_object don't remap * the entire scene with associated depsgraph updates, which are * problematic for renderers exporting data. */ id_us_min(&cu->id); BKE_libblock_free(bmain, cu); } else { BKE_libblock_free_us(bmain, cu); } } void BKE_mesh_from_nurbs(Main *bmain, Object *ob) { Curve *cu = (Curve *) ob->data; bool use_orco_uv = (cu->flag & CU_UV_ORCO) != 0; ListBase disp = {NULL, NULL}; if (ob->runtime.curve_cache) { disp = ob->runtime.curve_cache->disp; } BKE_mesh_from_nurbs_displist(bmain, ob, &disp, use_orco_uv, cu->id.name, false); } typedef struct EdgeLink { struct EdgeLink *next, *prev; void *edge; } EdgeLink; typedef struct VertLink { Link *next, *prev; unsigned int index; } VertLink; static void prependPolyLineVert(ListBase *lb, unsigned int index) { VertLink *vl = MEM_callocN(sizeof(VertLink), "VertLink"); vl->index = index; BLI_addhead(lb, vl); } static void appendPolyLineVert(ListBase *lb, unsigned int index) { VertLink *vl = MEM_callocN(sizeof(VertLink), "VertLink"); vl->index = index; BLI_addtail(lb, vl); } void BKE_mesh_to_curve_nurblist(const Mesh *me, ListBase *nurblist, const int edge_users_test) { MVert *mvert = me->mvert; MEdge *med, *medge = me->medge; MPoly *mp, *mpoly = me->mpoly; MLoop *mloop = me->mloop; int medge_len = me->totedge; int mpoly_len = me->totpoly; int totedges = 0; int i; /* only to detect edge polylines */ int *edge_users; ListBase edges = {NULL, NULL}; /* get boundary edges */ edge_users = MEM_calloc_arrayN(medge_len, sizeof(int), __func__); for (i = 0, mp = mpoly; i < mpoly_len; i++, mp++) { MLoop *ml = &mloop[mp->loopstart]; int j; for (j = 0; j < mp->totloop; j++, ml++) { edge_users[ml->e]++; } } /* create edges from all faces (so as to find edges not in any faces) */ med = medge; for (i = 0; i < medge_len; i++, med++) { if (edge_users[i] == edge_users_test) { EdgeLink *edl = MEM_callocN(sizeof(EdgeLink), "EdgeLink"); edl->edge = med; BLI_addtail(&edges, edl); totedges++; } } MEM_freeN(edge_users); if (edges.first) { while (edges.first) { /* each iteration find a polyline and add this as a nurbs poly spline */ ListBase polyline = {NULL, NULL}; /* store a list of VertLink's */ bool closed = false; int totpoly = 0; MEdge *med_current = ((EdgeLink *)edges.last)->edge; unsigned int startVert = med_current->v1; unsigned int endVert = med_current->v2; bool ok = true; appendPolyLineVert(&polyline, startVert); totpoly++; appendPolyLineVert(&polyline, endVert); totpoly++; BLI_freelinkN(&edges, edges.last); totedges--; while (ok) { /* while connected edges are found... */ EdgeLink *edl = edges.last; ok = false; while (edl) { EdgeLink *edl_prev = edl->prev; med = edl->edge; if (med->v1 == endVert) { endVert = med->v2; appendPolyLineVert(&polyline, med->v2); totpoly++; BLI_freelinkN(&edges, edl); totedges--; ok = true; } else if (med->v2 == endVert) { endVert = med->v1; appendPolyLineVert(&polyline, endVert); totpoly++; BLI_freelinkN(&edges, edl); totedges--; ok = true; } else if (med->v1 == startVert) { startVert = med->v2; prependPolyLineVert(&polyline, startVert); totpoly++; BLI_freelinkN(&edges, edl); totedges--; ok = true; } else if (med->v2 == startVert) { startVert = med->v1; prependPolyLineVert(&polyline, startVert); totpoly++; BLI_freelinkN(&edges, edl); totedges--; ok = true; } edl = edl_prev; } } /* Now we have a polyline, make into a curve */ if (startVert == endVert) { BLI_freelinkN(&polyline, polyline.last); totpoly--; closed = true; } /* --- nurbs --- */ { Nurb *nu; BPoint *bp; VertLink *vl; /* create new 'nurb' within the curve */ nu = (Nurb *)MEM_callocN(sizeof(Nurb), "MeshNurb"); nu->pntsu = totpoly; nu->pntsv = 1; nu->orderu = 4; nu->flagu = CU_NURB_ENDPOINT | (closed ? CU_NURB_CYCLIC : 0); /* endpoint */ nu->resolu = 12; nu->bp = (BPoint *)MEM_calloc_arrayN(totpoly, sizeof(BPoint), "bpoints"); /* add points */ vl = polyline.first; for (i = 0, bp = nu->bp; i < totpoly; i++, bp++, vl = (VertLink *)vl->next) { copy_v3_v3(bp->vec, mvert[vl->index].co); bp->f1 = SELECT; bp->radius = bp->weight = 1.0; } BLI_freelistN(&polyline); /* add nurb to curve */ BLI_addtail(nurblist, nu); } /* --- done with nurbs --- */ } } } void BKE_mesh_to_curve(Main *bmain, Depsgraph *depsgraph, Scene *scene, Object *ob) { /* make new mesh data from the original copy */ Mesh *me_eval = mesh_get_eval_final(depsgraph, scene, ob, CD_MASK_MESH); ListBase nurblist = {NULL, NULL}; BKE_mesh_to_curve_nurblist(me_eval, &nurblist, 0); BKE_mesh_to_curve_nurblist(me_eval, &nurblist, 1); if (nurblist.first) { Curve *cu = BKE_curve_add(bmain, ob->id.name + 2, OB_CURVE); cu->flag |= CU_3D; cu->nurb = nurblist; id_us_min(&((Mesh *)ob->data)->id); ob->data = cu; ob->type = OB_CURVE; BKE_object_free_derived_caches(ob); } } /* settings: 1 - preview, 2 - render */ Mesh *BKE_mesh_new_from_object( Depsgraph *depsgraph, Main *bmain, Scene *sce, Object *ob, const bool apply_modifiers, const bool calc_tessface, const bool calc_undeformed) { Mesh *tmpmesh; Curve *tmpcu = NULL, *copycu; int i; const bool render = (DEG_get_mode(depsgraph) == DAG_EVAL_RENDER); const bool cage = !apply_modifiers; bool do_mat_id_data_us = true; /* perform the mesh extraction based on type */ switch (ob->type) { case OB_FONT: case OB_CURVE: case OB_SURF: { ListBase dispbase = {NULL, NULL}; DerivedMesh *derivedFinal = NULL; int uv_from_orco; /* copies object and modifiers (but not the data) */ Object *tmpobj; /* TODO: make it temp copy outside bmain! */ BKE_id_copy_ex(bmain, &ob->id, (ID **)&tmpobj, LIB_ID_COPY_CACHES | LIB_ID_CREATE_NO_DEG_TAG, false); tmpcu = (Curve *)tmpobj->data; /* Copy cached display list, it might be needed by the stack evaluation. * Ideally stack should be able to use render-time display list, but doing * so is quite tricky and not safe so close to the release. * * TODO(sergey): Look into more proper solution. */ if (ob->runtime.curve_cache != NULL) { if (tmpobj->runtime.curve_cache == NULL) { tmpobj->runtime.curve_cache = MEM_callocN(sizeof(CurveCache), "CurveCache for curve types"); } BKE_displist_copy(&tmpobj->runtime.curve_cache->disp, &ob->runtime.curve_cache->disp); } /* if getting the original caged mesh, delete object modifiers */ if (cage) BKE_object_free_modifiers(tmpobj, 0); /* copies the data */ BKE_id_copy_ex(bmain, ob->data, (ID **)©cu, LIB_ID_CREATE_NO_DEG_TAG, false); tmpobj->data = copycu; /* make sure texture space is calculated for a copy of curve, * it will be used for the final result. */ BKE_curve_texspace_calc(copycu); /* temporarily set edit so we get updates from edit mode, but * also because for text datablocks copying it while in edit * mode gives invalid data structures */ copycu->editfont = tmpcu->editfont; copycu->editnurb = tmpcu->editnurb; /* get updated display list, and convert to a mesh */ BKE_displist_make_curveTypes_forRender(depsgraph, sce, tmpobj, &dispbase, &derivedFinal, false, render); copycu->editfont = NULL; copycu->editnurb = NULL; tmpobj->derivedFinal = derivedFinal; /* convert object type to mesh */ uv_from_orco = (tmpcu->flag & CU_UV_ORCO) != 0; BKE_mesh_from_nurbs_displist(bmain, tmpobj, &dispbase, uv_from_orco, tmpcu->id.name + 2, true); tmpmesh = tmpobj->data; BKE_displist_free(&dispbase); /* BKE_mesh_from_nurbs changes the type to a mesh, check it worked. * if it didn't the curve did not have any segments or otherwise * would have generated an empty mesh */ if (tmpobj->type != OB_MESH) { BKE_libblock_free(bmain, tmpobj); return NULL; } BKE_libblock_free(bmain, tmpobj); /* XXX The curve to mesh conversion is convoluted... But essentially, BKE_mesh_from_nurbs_displist() * already transfers the ownership of materials from the temp copy of the Curve ID to the new * Mesh ID, so we do not want to increase materials' usercount later. */ do_mat_id_data_us = false; break; } case OB_MBALL: { /* metaballs don't have modifiers, so just convert to mesh */ Object *basis_ob = BKE_mball_basis_find(sce, ob); /* todo, re-generatre for render-res */ /* metaball_polygonize(scene, ob) */ if (ob != basis_ob) return NULL; /* only do basis metaball */ tmpmesh = BKE_mesh_add(bmain, ((ID *)ob->data)->name + 2); /* BKE_mesh_add gives us a user count we don't need */ id_us_min(&tmpmesh->id); if (render) { ListBase disp = {NULL, NULL}; BKE_displist_make_mball_forRender(depsgraph, sce, ob, &disp); BKE_mesh_from_metaball(&disp, tmpmesh); BKE_displist_free(&disp); } else { ListBase disp = {NULL, NULL}; if (ob->runtime.curve_cache) { disp = ob->runtime.curve_cache->disp; } BKE_mesh_from_metaball(&disp, tmpmesh); } BKE_mesh_texspace_copy_from_object(tmpmesh, ob); break; } case OB_MESH: /* copies object and modifiers (but not the data) */ if (cage) { /* copies the data */ tmpmesh = BKE_mesh_copy(bmain, ob->data); /* XXX BKE_mesh_copy() already handles materials usercount. */ do_mat_id_data_us = false; } /* if not getting the original caged mesh, get final derived mesh */ else { /* Make a dummy mesh, saves copying */ DerivedMesh *dm; /* CustomDataMask mask = CD_MASK_BAREMESH|CD_MASK_MTFACE|CD_MASK_MCOL; */ CustomDataMask mask = CD_MASK_MESH; /* this seems more suitable, exporter, * for example, needs CD_MASK_MDEFORMVERT */ if (calc_undeformed) mask |= CD_MASK_ORCO; /* Write the display mesh into the dummy mesh */ if (render) dm = mesh_create_derived_render(depsgraph, sce, ob, mask); else dm = mesh_create_derived_view(depsgraph, sce, ob, mask); tmpmesh = BKE_mesh_add(bmain, ((ID *)ob->data)->name + 2); DM_to_mesh(dm, tmpmesh, ob, mask, true); /* Copy autosmooth settings from original mesh. */ Mesh *me = (Mesh *)ob->data; tmpmesh->flag |= (me->flag & ME_AUTOSMOOTH); tmpmesh->smoothresh = me->smoothresh; } /* BKE_mesh_add/copy gives us a user count we don't need */ id_us_min(&tmpmesh->id); break; default: /* "Object does not have geometry data") */ return NULL; } /* Copy materials to new mesh */ switch (ob->type) { case OB_SURF: case OB_FONT: case OB_CURVE: tmpmesh->totcol = tmpcu->totcol; /* free old material list (if it exists) and adjust user counts */ if (tmpcu->mat) { for (i = tmpcu->totcol; i-- > 0; ) { /* are we an object material or data based? */ tmpmesh->mat[i] = give_current_material(ob, i + 1); if (((ob->matbits && ob->matbits[i]) || do_mat_id_data_us) && tmpmesh->mat[i]) { id_us_plus(&tmpmesh->mat[i]->id); } } } break; case OB_MBALL: { MetaBall *tmpmb = (MetaBall *)ob->data; tmpmesh->mat = MEM_dupallocN(tmpmb->mat); tmpmesh->totcol = tmpmb->totcol; /* free old material list (if it exists) and adjust user counts */ if (tmpmb->mat) { for (i = tmpmb->totcol; i-- > 0; ) { /* are we an object material or data based? */ tmpmesh->mat[i] = give_current_material(ob, i + 1); if (((ob->matbits && ob->matbits[i]) || do_mat_id_data_us) && tmpmesh->mat[i]) { id_us_plus(&tmpmesh->mat[i]->id); } } } break; } case OB_MESH: if (!cage) { Mesh *origmesh = ob->data; tmpmesh->flag = origmesh->flag; tmpmesh->mat = MEM_dupallocN(origmesh->mat); tmpmesh->totcol = origmesh->totcol; tmpmesh->smoothresh = origmesh->smoothresh; if (origmesh->mat) { for (i = origmesh->totcol; i-- > 0; ) { /* are we an object material or data based? */ tmpmesh->mat[i] = give_current_material(ob, i + 1); if (((ob->matbits && ob->matbits[i]) || do_mat_id_data_us) && tmpmesh->mat[i]) { id_us_plus(&tmpmesh->mat[i]->id); } } } } break; } /* end copy materials */ if (calc_tessface) { /* cycles and exporters rely on this still */ BKE_mesh_tessface_ensure(tmpmesh); } return tmpmesh; } static void add_shapekey_layers(Mesh *mesh_dest, Mesh *mesh_src) { KeyBlock *kb; Key *key = mesh_src->key; int i; if (!mesh_src->key) return; /* ensure we can use mesh vertex count for derived mesh custom data */ if (mesh_src->totvert != mesh_dest->totvert) { fprintf(stderr, "%s: vertex size mismatch (mesh/dm) '%s' (%d != %d)\n", __func__, mesh_src->id.name + 2, mesh_src->totvert, mesh_dest->totvert); return; } for (i = 0, kb = key->block.first; kb; kb = kb->next, i++) { int ci; float *array; if (mesh_src->totvert != kb->totelem) { fprintf(stderr, "%s: vertex size mismatch (Mesh '%s':%d != KeyBlock '%s':%d)\n", __func__, mesh_src->id.name + 2, mesh_src->totvert, kb->name, kb->totelem); array = MEM_calloc_arrayN((size_t)mesh_src->totvert, 3 * sizeof(float), __func__); } else { array = MEM_malloc_arrayN((size_t)mesh_src->totvert, 3 * sizeof(float), __func__); memcpy(array, kb->data, (size_t)mesh_src->totvert * 3 * sizeof(float)); } CustomData_add_layer_named(&mesh_dest->vdata, CD_SHAPEKEY, CD_ASSIGN, array, mesh_dest->totvert, kb->name); ci = CustomData_get_layer_index_n(&mesh_dest->vdata, CD_SHAPEKEY, i); mesh_dest->vdata.layers[ci].uid = kb->uid; } } Mesh *BKE_mesh_create_derived_for_modifier( struct Depsgraph *depsgraph, Scene *scene, Object *ob, ModifierData *md, int build_shapekey_layers) { Mesh *me = ob->data; const ModifierTypeInfo *mti = modifierType_getInfo(md->type); Mesh *result; KeyBlock *kb; ModifierEvalContext mectx = {depsgraph, ob, 0}; if (!(md->mode & eModifierMode_Realtime)) { return NULL; } if (mti->isDisabled && mti->isDisabled(scene, md, 0)) { return NULL; } if (build_shapekey_layers && me->key && (kb = BLI_findlink(&me->key->block, ob->shapenr - 1))) { BKE_keyblock_convert_to_mesh(kb, me); } if (mti->type == eModifierTypeType_OnlyDeform) { int numVerts; float (*deformedVerts)[3] = BKE_mesh_vertexCos_get(me, &numVerts); modifier_deformVerts(md, &mectx, NULL, deformedVerts, numVerts); BKE_id_copy_ex( NULL, &me->id, (ID **)&result, LIB_ID_CREATE_NO_MAIN | LIB_ID_CREATE_NO_USER_REFCOUNT | LIB_ID_CREATE_NO_DEG_TAG | LIB_ID_COPY_NO_PREVIEW, false); BKE_mesh_apply_vert_coords(result, deformedVerts); if (build_shapekey_layers) add_shapekey_layers(result, me); MEM_freeN(deformedVerts); } else { Mesh *mesh_temp; BKE_id_copy_ex( NULL, &me->id, (ID **)&mesh_temp, LIB_ID_CREATE_NO_MAIN | LIB_ID_CREATE_NO_USER_REFCOUNT | LIB_ID_CREATE_NO_DEG_TAG | LIB_ID_COPY_NO_PREVIEW, false); if (build_shapekey_layers) add_shapekey_layers(mesh_temp, me); result = modifier_applyModifier(md, &mectx, mesh_temp); ASSERT_IS_VALID_MESH(result); if (mesh_temp != result) { BKE_id_free(NULL, mesh_temp); } } return result; } /* This is a Mesh-based copy of the same function in DerivedMesh.c */ static void shapekey_layers_to_keyblocks(Mesh *mesh_src, Mesh *mesh_dst, int actshape_uid) { KeyBlock *kb; int i, j, tot; if (!mesh_dst->key) return; tot = CustomData_number_of_layers(&mesh_src->vdata, CD_SHAPEKEY); for (i = 0; i < tot; i++) { CustomDataLayer *layer = &mesh_src->vdata.layers[CustomData_get_layer_index_n(&mesh_src->vdata, CD_SHAPEKEY, i)]; float (*cos)[3], (*kbcos)[3]; for (kb = mesh_dst->key->block.first; kb; kb = kb->next) { if (kb->uid == layer->uid) break; } if (!kb) { kb = BKE_keyblock_add(mesh_dst->key, layer->name); kb->uid = layer->uid; } if (kb->data) MEM_freeN(kb->data); cos = CustomData_get_layer_n(&mesh_src->vdata, CD_SHAPEKEY, i); kb->totelem = mesh_src->totvert; kb->data = kbcos = MEM_malloc_arrayN(kb->totelem, 3 * sizeof(float), __func__); if (kb->uid == actshape_uid) { MVert *mvert = mesh_src->mvert; for (j = 0; j < mesh_src->totvert; j++, kbcos++, mvert++) { copy_v3_v3(*kbcos, mvert->co); } } else { for (j = 0; j < kb->totelem; j++, cos++, kbcos++) { copy_v3_v3(*kbcos, *cos); } } } for (kb = mesh_dst->key->block.first; kb; kb = kb->next) { if (kb->totelem != mesh_src->totvert) { if (kb->data) MEM_freeN(kb->data); kb->totelem = mesh_src->totvert; kb->data = MEM_calloc_arrayN(kb->totelem, 3 * sizeof(float), __func__); fprintf(stderr, "%s: lost a shapekey layer: '%s'! (bmesh internal error)\n", __func__, kb->name); } } } /* This is a Mesh-based copy of DM_to_mesh() */ void BKE_mesh_nomain_to_mesh(Mesh *mesh_src, Mesh *mesh_dst, Object *ob, CustomDataMask mask, bool take_ownership) { /* mesh_src might depend on mesh_dst, so we need to do everything with a local copy */ /* TODO(Sybren): the above claim came from DM_to_mesh(); check whether it is still true with Mesh */ Mesh tmp = *mesh_dst; int totvert, totedge /*, totface */ /* UNUSED */, totloop, totpoly; int did_shapekeys = 0; eCDAllocType alloctype = CD_DUPLICATE; if (take_ownership /* && dm->type == DM_TYPE_CDDM && dm->needsFree */) { bool has_any_referenced_layers = CustomData_has_referenced(&mesh_src->vdata) || CustomData_has_referenced(&mesh_src->edata) || CustomData_has_referenced(&mesh_src->ldata) || CustomData_has_referenced(&mesh_src->fdata) || CustomData_has_referenced(&mesh_src->pdata); if (!has_any_referenced_layers) { alloctype = CD_ASSIGN; } } CustomData_reset(&tmp.vdata); CustomData_reset(&tmp.edata); CustomData_reset(&tmp.fdata); CustomData_reset(&tmp.ldata); CustomData_reset(&tmp.pdata); BKE_mesh_ensure_normals(mesh_src); totvert = tmp.totvert = mesh_src->totvert; totedge = tmp.totedge = mesh_src->totedge; totloop = tmp.totloop = mesh_src->totloop; totpoly = tmp.totpoly = mesh_src->totpoly; tmp.totface = 0; CustomData_copy(&mesh_src->vdata, &tmp.vdata, mask, alloctype, totvert); CustomData_copy(&mesh_src->edata, &tmp.edata, mask, alloctype, totedge); CustomData_copy(&mesh_src->ldata, &tmp.ldata, mask, alloctype, totloop); CustomData_copy(&mesh_src->pdata, &tmp.pdata, mask, alloctype, totpoly); tmp.cd_flag = mesh_src->cd_flag; tmp.runtime.deformed_only = mesh_src->runtime.deformed_only; if (CustomData_has_layer(&mesh_src->vdata, CD_SHAPEKEY)) { KeyBlock *kb; int uid; if (ob) { kb = BLI_findlink(&mesh_dst->key->block, ob->shapenr - 1); if (kb) { uid = kb->uid; } else { printf("%s: error - could not find active shapekey %d!\n", __func__, ob->shapenr - 1); uid = INT_MAX; } } else { /* if no object, set to INT_MAX so we don't mess up any shapekey layers */ uid = INT_MAX; } shapekey_layers_to_keyblocks(mesh_src, mesh_dst, uid); did_shapekeys = 1; } /* copy texture space */ if (ob) { BKE_mesh_texspace_copy_from_object(&tmp, ob); } /* not all DerivedMeshes store their verts/edges/faces in CustomData, so * we set them here in case they are missing */ /* TODO(Sybren): we could probably replace CD_ASSIGN with alloctype and always directly pass mesh_src->mxxx, * instead of using a ternary operator. */ if (!CustomData_has_layer(&tmp.vdata, CD_MVERT)) { CustomData_add_layer( &tmp.vdata, CD_MVERT, CD_ASSIGN, (alloctype == CD_ASSIGN) ? mesh_src->mvert : MEM_dupallocN(mesh_src->mvert), totvert); } if (!CustomData_has_layer(&tmp.edata, CD_MEDGE)) { CustomData_add_layer( &tmp.edata, CD_MEDGE, CD_ASSIGN, (alloctype == CD_ASSIGN) ? mesh_src->medge : MEM_dupallocN(mesh_src->medge), totedge); } if (!CustomData_has_layer(&tmp.pdata, CD_MPOLY)) { /* TODO(Sybren): assigment to tmp.mxxx is probably not necessary due to the * BKE_mesh_update_customdata_pointers() call below. */ tmp.mloop = (alloctype == CD_ASSIGN) ? mesh_src->mloop : MEM_dupallocN(mesh_src->mloop); tmp.mpoly = (alloctype == CD_ASSIGN) ? mesh_src->mpoly : MEM_dupallocN(mesh_src->mpoly); CustomData_add_layer(&tmp.ldata, CD_MLOOP, CD_ASSIGN, tmp.mloop, tmp.totloop); CustomData_add_layer(&tmp.pdata, CD_MPOLY, CD_ASSIGN, tmp.mpoly, tmp.totpoly); } /* object had got displacement layer, should copy this layer to save sculpted data */ /* NOTE: maybe some other layers should be copied? nazgul */ if (CustomData_has_layer(&mesh_dst->ldata, CD_MDISPS)) { if (totloop == mesh_dst->totloop) { MDisps *mdisps = CustomData_get_layer(&mesh_dst->ldata, CD_MDISPS); CustomData_add_layer(&tmp.ldata, CD_MDISPS, alloctype, mdisps, totloop); } } /* yes, must be before _and_ after tessellate */ BKE_mesh_update_customdata_pointers(&tmp, false); /* since 2.65 caller must do! */ // BKE_mesh_tessface_calc(&tmp); CustomData_free(&mesh_dst->vdata, mesh_dst->totvert); CustomData_free(&mesh_dst->edata, mesh_dst->totedge); CustomData_free(&mesh_dst->fdata, mesh_dst->totface); CustomData_free(&mesh_dst->ldata, mesh_dst->totloop); CustomData_free(&mesh_dst->pdata, mesh_dst->totpoly); /* ok, this should now use new CD shapekey data, * which should be fed through the modifier * stack */ if (tmp.totvert != mesh_dst->totvert && !did_shapekeys && mesh_dst->key) { printf("%s: YEEK! this should be recoded! Shape key loss!: ID '%s'\n", __func__, tmp.id.name); if (tmp.key && !(tmp.id.tag & LIB_TAG_NO_MAIN)) { id_us_min(&tmp.key->id); } tmp.key = NULL; } /* Clear selection history */ MEM_SAFE_FREE(tmp.mselect); tmp.totselect = 0; BLI_assert(ELEM(tmp.bb, NULL, mesh_dst->bb)); if (mesh_dst->bb) { MEM_freeN(mesh_dst->bb); tmp.bb = NULL; } /* skip the listbase */ MEMCPY_STRUCT_OFS(mesh_dst, &tmp, id.prev); if (take_ownership) { if (alloctype == CD_ASSIGN) { CustomData_free_typemask(&mesh_src->vdata, mesh_src->totvert, ~mask); CustomData_free_typemask(&mesh_src->edata, mesh_src->totedge, ~mask); CustomData_free_typemask(&mesh_src->ldata, mesh_src->totloop, ~mask); CustomData_free_typemask(&mesh_src->pdata, mesh_src->totpoly, ~mask); } BKE_id_free(NULL, mesh_src); } } /* This is a Mesh-based copy of DM_to_meshkey() */ void BKE_mesh_nomain_to_meshkey(Mesh *mesh_src, Mesh *mesh_dst, KeyBlock *kb) { int a, totvert = mesh_src->totvert; float *fp; MVert *mvert; if (totvert == 0 || mesh_dst->totvert == 0 || mesh_dst->totvert != totvert) { return; } if (kb->data) MEM_freeN(kb->data); kb->data = MEM_malloc_arrayN(mesh_dst->key->elemsize, mesh_dst->totvert, "kb->data"); kb->totelem = totvert; fp = kb->data; mvert = mesh_src->mvert; for (a = 0; a < kb->totelem; a++, fp += 3, mvert++) { copy_v3_v3(fp, mvert->co); } }