/* * ***** 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. * about this. * * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * The Original Code is Copyright (C) 2007 Blender Foundation. * All rights reserved. * * The Original Code is: all of this file. * * Contributor(s): Geoffrey Bantle. * * ***** END GPL LICENSE BLOCK ***** */ /** \file blender/bmesh/intern/bmesh_construct.c * \ingroup bmesh * * BM construction functions. */ #include "MEM_guardedalloc.h" #include "BKE_customdata.h" #include "BKE_utildefines.h" #include "BLI_array.h" #include "BLI_utildefines.h" #include "DNA_meshdata_types.h" #include "DNA_mesh_types.h" #include "BLI_math.h" #include "BLI_utildefines.h" #include "bmesh.h" #include "bmesh_private.h" #include #include #include #define SELECT 1 /* prototypes */ static void bm_copy_loop_attributes(BMesh *source_mesh, BMesh *target_mesh, const BMLoop *source_loop, BMLoop *target_loop); #if 0 /* * BM_CONSTRUCT.C * * This file contains functions for making and destroying * individual elements like verts, edges and faces. * */ /* * BMESH MAKE VERT * * Creates a new vertex and returns a pointer * to it. If a pointer to an example vertex is * passed in, it's custom data and properties * will be copied to the new vertex. * */ BMVert *BM_Make_Vert(BMesh *bm, float co[3], BMVert *example) { BMVert *v = NULL; v = bmesh_mv(bm, co); if (example) CustomData_bmesh_copy_data(&bm->vdata, &bm->vdata, example->head.data, &v->head.data); return v; } /* * BMESH MAKE EDGE * * Creates a new edge betweeen two vertices and returns a * pointer to it. If 'nodouble' equals 1, then a check is * is done to make sure that an edge between those two vertices * does not already exist. If it does, that edge is returned instead * of creating a new one. * * If a new edge is created, and a pointer to an example edge is * provided, it's custom data and properties will be copied to the * new edge. * */ BMEdge *BM_Make_Edge(BMesh *bm, BMVert *v1, BMVert *v2, BMEdge *example, int nodouble) { BMEdge *e = NULL; if (nodouble) /* test if edge already exists. */ e = BM_Edge_Exist(v1, v2); if (!e) { e = bmesh_me(bm, v1, v2); if (example) CustomData_bmesh_copy_data(&bm->edata, &bm->edata, example->head.data, &e->head.data); } return e; } #endif /* * BMESH MAKE QUADTRIANGLE * * Creates a new quad or triangle from * a list of 3 or 4 vertices. If nodouble * equals 1, then a check is done to see * if a face with these vertices already * exists and returns it instead. If a pointer * to an example face is provided, it's custom * data and properties will be copied to the new * face. * * Note that the winding of the face is determined * by the order of the vertices in the vertex array */ BMFace *BM_Make_Face_QuadTri(BMesh *bm, BMVert *v1, BMVert *v2, BMVert *v3, BMVert *v4, const BMFace *example, const int nodouble) { BMVert *vtar[4] = {v1, v2, v3, v4}; return BM_Make_Face_QuadTri_v(bm, vtar, v4 ? 4 : 3, example, nodouble); } /* remove the edge array bits from this. Its not really needed? */ BMFace *BM_Make_Face_QuadTri_v(BMesh *bm, BMVert **verts, int len, const BMFace *example, const int nodouble) { BMEdge *edar[4] = {NULL}; BMFace *f = NULL; int overlap = 0; edar[0] = BM_Edge_Exist(verts[0], verts[1]); edar[1] = BM_Edge_Exist(verts[1], verts[2]); if (len == 4) { edar[2] = BM_Edge_Exist(verts[2], verts[3]); edar[3] = BM_Edge_Exist(verts[3], verts[0]); } else { edar[2] = BM_Edge_Exist(verts[2], verts[0]); } if (nodouble) { /* check if face exists or overlaps */ if (len == 4) { overlap = BM_Exist_Face_Overlaps(bm, verts, len, &f); } else { overlap = BM_Exist_Face_Overlaps(bm, verts, len, &f); } } /* make new face */ if ((!f) && (!overlap)) { if (!edar[0]) edar[0] = BM_Make_Edge(bm, verts[0], verts[1], NULL, 0); if (!edar[1]) edar[1] = BM_Make_Edge(bm, verts[1], verts[2], NULL, 0); if (len == 4) { if (!edar[2]) edar[2] = BM_Make_Edge(bm, verts[2], verts[3], NULL, 0); if (!edar[3]) edar[3] = BM_Make_Edge(bm, verts[3], verts[0], NULL, 0); } else { if (!edar[2]) edar[2] = BM_Make_Edge(bm, verts[2], verts[0], NULL, 0); } f = BM_Make_Face(bm, verts, edar, len, 0); if (example && f) { BM_Copy_Attributes(bm, bm, example, f); } } return f; } /* copies face data from shared adjacent faces */ void BM_Face_CopyShared(BMesh *bm, BMFace *f) { BMIter iter; BMLoop *l, *l2; if (!f) return; l = BMIter_New(&iter, bm, BM_LOOPS_OF_FACE, f); for ( ; l; l = BMIter_Step(&iter)) { l2 = l->radial_next; if (l2 && l2 != l) { if (l2->v == l->v) { bm_copy_loop_attributes(bm, bm, l2, l); } else { l2 = l2->next; bm_copy_loop_attributes(bm, bm, l2, l); } } } } /* * BMESH MAKE NGON * * Attempts to make a new Ngon from a list of edges. * If nodouble equals one, a check for overlaps or existing * * The edges are not required to be ordered, simply to to form * a single closed loop as a whole * * Note that while this function will work fine when the edges * are already sorted, if the edges are always going to be sorted, * BM_Make_Face should be considered over this function as it * avoids some unnecessary work. */ BMFace *BM_Make_Ngon(BMesh *bm, BMVert *v1, BMVert *v2, BMEdge **edges, int len, int nodouble) { BMEdge **edges2 = NULL; BLI_array_staticdeclare(edges2, BM_NGON_STACK_SIZE); BMVert **verts = NULL, *v; BLI_array_staticdeclare(verts, BM_NGON_STACK_SIZE); BMFace *f = NULL; BMEdge *e; BMVert *ev1, *ev2; int i, /* j, */ v1found, reverse; /* this code is hideous, yeek. I'll have to think about ways of * cleaning it up. basically, it now combines the old BM_Make_Ngon * _and_ the old bmesh_mf functions, so its kindof smashed together * - joeedh */ if (!len || !v1 || !v2 || !edges || !bm) return NULL; /* put edges in correct order */ for (i = 0; i < len; i++) { bmesh_api_setflag(edges[i], _FLAG_MF); } ev1 = edges[0]->v1; ev2 = edges[0]->v2; if (v1 == ev2) { /* Swapping here improves performance and consistency of face * structure in the special case that the edges are already in * the correct order and winding */ SWAP(BMVert *, ev1, ev2); } BLI_array_append(verts, ev1); v = ev2; e = edges[0]; do { BMEdge *e2 = e; BLI_array_append(verts, v); BLI_array_append(edges2, e); do { e2 = bmesh_disk_nextedge(e2, v); if (e2 != e && bmesh_api_getflag(e2, _FLAG_MF)) { v = BM_OtherEdgeVert(e2, v); break; } } while (e2 != e); if (e2 == e) goto err; /* the edges do not form a closed loop */ e = e2; } while (e != edges[0]); if (BLI_array_count(edges2) != len) { goto err; /* we didn't use all edges in forming the boundary loop */ } /* ok, edges are in correct order, now ensure they are going * in the correct direction */ v1found = reverse = 0; for (i = 0; i < len; i++) { if (BM_Vert_In_Edge(edges2[i], v1)) { /* see if v1 and v2 are in the same edge */ if (BM_Vert_In_Edge(edges2[i], v2)) { /* if v1 is shared by the *next* edge, then the winding * is incorrect */ if (BM_Vert_In_Edge(edges2[(i + 1) % len], v1)) { reverse = 1; break; } } v1found = 1; } if (!v1found && BM_Vert_In_Edge(edges2[i], v2)) { reverse = 1; break; } } if (reverse) { for (i = 0; i < len / 2; i++) { v = verts[i]; verts[i] = verts[len - i - 1]; verts[len - i - 1] = v; } } for (i = 0; i < len; i++) { edges2[i] = BM_Edge_Exist(verts[i], verts[(i + 1) % len]); if (!edges2[i]) { goto err; } } f = BM_Make_Face(bm, verts, edges2, len, nodouble); /* clean up flags */ for (i = 0; i < len; i++) { bmesh_api_clearflag(edges2[i], _FLAG_MF); } BLI_array_free(verts); BLI_array_free(edges2); return f; err: for (i = 0; i < len; i++) { bmesh_api_clearflag(edges[i], _FLAG_MF); } BLI_array_free(verts); BLI_array_free(edges2); return NULL; } /* bmesh_make_face_from_face(BMesh *bm, BMFace *source, BMFace *target) */ /* * REMOVE TAGGED XXX * * Called by operators to remove elements that they have marked for * removal. * */ void BM_remove_tagged_faces(BMesh *bm, int flag) { BMFace *f; BMIter iter; BM_ITER(f, &iter, bm, BM_FACES_OF_MESH, NULL) { if (BMO_TestFlag(bm, f, flag)) BM_Kill_Face(bm, f); } } void BM_remove_tagged_edges(BMesh *bm, int flag) { BMEdge *e; BMIter iter; BM_ITER(e, &iter, bm, BM_EDGES_OF_MESH, NULL) { if (BMO_TestFlag(bm, e, flag)) BM_Kill_Edge(bm, e); } } void BM_remove_tagged_verts(BMesh *bm, int flag) { BMVert *v; BMIter iter; BM_ITER(v, &iter, bm, BM_VERTS_OF_MESH, NULL) { if (BMO_TestFlag(bm, v, flag)) BM_Kill_Vert(bm, v); } } static void bm_copy_vert_attributes(BMesh *source_mesh, BMesh *target_mesh, const BMVert *source_vertex, BMVert *target_vertex) { if ((source_mesh == target_mesh) && (source_vertex == target_vertex)) { return; } copy_v3_v3(target_vertex->no, source_vertex->no); CustomData_bmesh_free_block(&target_mesh->vdata, &target_vertex->head.data); CustomData_bmesh_copy_data(&source_mesh->vdata, &target_mesh->vdata, source_vertex->head.data, &target_vertex->head.data); } static void bm_copy_edge_attributes(BMesh *source_mesh, BMesh *target_mesh, const BMEdge *source_edge, BMEdge *target_edge) { if ((source_mesh == target_mesh) && (source_edge == target_edge)) { return; } CustomData_bmesh_free_block(&target_mesh->edata, &target_edge->head.data); CustomData_bmesh_copy_data(&source_mesh->edata, &target_mesh->edata, source_edge->head.data, &target_edge->head.data); } static void bm_copy_loop_attributes(BMesh *source_mesh, BMesh *target_mesh, const BMLoop *source_loop, BMLoop *target_loop) { if ((source_mesh == target_mesh) && (source_loop == target_loop)) { return; } CustomData_bmesh_free_block(&target_mesh->ldata, &target_loop->head.data); CustomData_bmesh_copy_data(&source_mesh->ldata, &target_mesh->ldata, source_loop->head.data, &target_loop->head.data); } static void bm_copy_face_attributes(BMesh *source_mesh, BMesh *target_mesh, const BMFace *source_face, BMFace *target_face) { if ((source_mesh == target_mesh) && (source_face == target_face)) { return; } copy_v3_v3(target_face->no, source_face->no); CustomData_bmesh_free_block(&target_mesh->pdata, &target_face->head.data); CustomData_bmesh_copy_data(&source_mesh->pdata, &target_mesh->pdata, source_face->head.data, &target_face->head.data); target_face->mat_nr = source_face->mat_nr; } /* BMESH_TODO: Special handling for hide flags? */ void BM_Copy_Attributes(BMesh *source_mesh, BMesh *target_mesh, const void *source, void *target) { const BMHeader *sheader = source; BMHeader *theader = target; if (sheader->htype != theader->htype) return; /* First we copy select */ if (BM_Selected(source_mesh, source)) BM_Select(target_mesh, target, TRUE); /* Now we copy flags */ theader->hflag = sheader->hflag; /* Copy specific attributes */ if (theader->htype == BM_VERT) bm_copy_vert_attributes(source_mesh, target_mesh, (const BMVert *)source, (BMVert *)target); else if (theader->htype == BM_EDGE) bm_copy_edge_attributes(source_mesh, target_mesh, (const BMEdge *)source, (BMEdge *)target); else if (theader->htype == BM_LOOP) bm_copy_loop_attributes(source_mesh, target_mesh, (const BMLoop *)source, (BMLoop *)target); else if (theader->htype == BM_FACE) bm_copy_face_attributes(source_mesh, target_mesh, (const BMFace *)source, (BMFace *)target); } BMesh *BM_Copy_Mesh(BMesh *bmold) { BMesh *bm; BMVert *v, *v2, **vtable = NULL; BMEdge *e, *e2, **edges = NULL, **etable = NULL; BLI_array_declare(edges); BMLoop *l, /* *l2, */ **loops = NULL; BLI_array_declare(loops); BMFace *f, *f2, **ftable = NULL; BMEditSelection *ese; BMIter iter, liter; int allocsize[4] = {512, 512, 2048, 512}; int i, j; /* allocate a bmesh */ bm = BM_Make_Mesh(bmold->ob, allocsize); CustomData_copy(&bmold->vdata, &bm->vdata, CD_MASK_BMESH, CD_CALLOC, 0); CustomData_copy(&bmold->edata, &bm->edata, CD_MASK_BMESH, CD_CALLOC, 0); CustomData_copy(&bmold->ldata, &bm->ldata, CD_MASK_BMESH, CD_CALLOC, 0); CustomData_copy(&bmold->pdata, &bm->pdata, CD_MASK_BMESH, CD_CALLOC, 0); CustomData_bmesh_init_pool(&bm->vdata, allocsize[0]); CustomData_bmesh_init_pool(&bm->edata, allocsize[1]); CustomData_bmesh_init_pool(&bm->ldata, allocsize[2]); CustomData_bmesh_init_pool(&bm->pdata, allocsize[3]); vtable = MEM_mallocN(sizeof(BMVert *) * bmold->totvert, "BM_Copy_Mesh vtable"); etable = MEM_mallocN(sizeof(BMEdge *) * bmold->totedge, "BM_Copy_Mesh etable"); ftable = MEM_mallocN(sizeof(BMFace *) * bmold->totface, "BM_Copy_Mesh ftable"); v = BMIter_New(&iter, bmold, BM_VERTS_OF_MESH, NULL); for (i = 0; v; v = BMIter_Step(&iter), i++) { v2 = BM_Make_Vert(bm, v->co, NULL); /* copy between meshes so cant use 'example' argument */ BM_Copy_Attributes(bmold, bm, v, v2); vtable[i] = v2; BM_SetIndex(v, i); /* set_inline */ BM_SetIndex(v2, i); /* set_inline */ } bmold->elem_index_dirty &= ~BM_VERT; bm->elem_index_dirty &= ~BM_VERT; /* safety check */ BLI_assert(i == bmold->totvert); e = BMIter_New(&iter, bmold, BM_EDGES_OF_MESH, NULL); for (i = 0; e; e = BMIter_Step(&iter), i++) { e2 = BM_Make_Edge(bm, vtable[BM_GetIndex(e->v1)], vtable[BM_GetIndex(e->v2)], e, 0); BM_Copy_Attributes(bmold, bm, e, e2); etable[i] = e2; BM_SetIndex(e, i); /* set_inline */ BM_SetIndex(e2, i); /* set_inline */ } bmold->elem_index_dirty &= ~BM_EDGE; bm->elem_index_dirty &= ~BM_EDGE; /* safety check */ BLI_assert(i == bmold->totedge); f = BMIter_New(&iter, bmold, BM_FACES_OF_MESH, NULL); for (i = 0; f; f = BMIter_Step(&iter), i++) { BM_SetIndex(f, i); /* set_inline */ BLI_array_empty(loops); BLI_array_empty(edges); BLI_array_growitems(loops, f->len); BLI_array_growitems(edges, f->len); l = BMIter_New(&liter, bmold, BM_LOOPS_OF_FACE, f); for (j = 0; j < f->len; j++, l = BMIter_Step(&liter)) { loops[j] = l; edges[j] = etable[BM_GetIndex(l->e)]; } v = vtable[BM_GetIndex(loops[0]->v)]; v2 = vtable[BM_GetIndex(loops[1]->v)]; if (!bmesh_verts_in_edge(v, v2, edges[0])) { v = vtable[BM_GetIndex(loops[BLI_array_count(loops) - 1]->v)]; v2 = vtable[BM_GetIndex(loops[0]->v)]; } f2 = BM_Make_Ngon(bm, v, v2, edges, f->len, 0); if (!f2) continue; /* use totface incase adding some faces fails */ BM_SetIndex(f2, (bm->totface - 1)); /* set_inline */ ftable[i] = f2; BM_Copy_Attributes(bmold, bm, f, f2); copy_v3_v3(f2->no, f->no); l = BMIter_New(&liter, bm, BM_LOOPS_OF_FACE, f2); for (j = 0; j < f->len; j++, l = BMIter_Step(&liter)) { BM_Copy_Attributes(bmold, bm, loops[j], l); } if (f == bmold->act_face) bm->act_face = f2; } bmold->elem_index_dirty &= ~BM_FACE; bm->elem_index_dirty &= ~BM_FACE; /* safety check */ BLI_assert(i == bmold->totface); /* copy over edit selection history */ for (ese = bmold->selected.first; ese; ese = ese->next) { void *ele = NULL; if (ese->htype == BM_VERT) ele = vtable[BM_GetIndex(ese->data)]; else if (ese->htype == BM_EDGE) ele = etable[BM_GetIndex(ese->data)]; else if (ese->htype == BM_FACE) { ele = ftable[BM_GetIndex(ese->data)]; } else { BLI_assert(0); } if (ele) BM_store_selection(bm, ele); } MEM_freeN(etable); MEM_freeN(vtable); MEM_freeN(ftable); BLI_array_free(loops); BLI_array_free(edges); return bm; } /* ME -> BM */ char BM_Vert_Flag_From_MEFlag(const char meflag) { return ( ((meflag & SELECT) ? BM_SELECT : 0) | ((meflag & ME_HIDE) ? BM_HIDDEN : 0) ); } char BM_Edge_Flag_From_MEFlag(const short meflag) { return ( ((meflag & SELECT) ? BM_SELECT : 0) | ((meflag & ME_SEAM) ? BM_SEAM : 0) | ((meflag & ME_SHARP) ? BM_SHARP : 0) | ((meflag & ME_HIDE) ? BM_HIDDEN : 0) ); } char BM_Face_Flag_From_MEFlag(const char meflag) { return ( ((meflag & ME_FACE_SEL) ? BM_SELECT : 0) | ((meflag & ME_SMOOTH) ? BM_SMOOTH : 0) | ((meflag & ME_HIDE) ? BM_HIDDEN : 0) ); } /* BM -> ME */ char BM_Vert_Flag_To_MEFlag(BMVert *eve) { const char hflag = eve->head.hflag; return ( ((hflag & BM_SELECT) ? SELECT : 0) | ((hflag & BM_HIDDEN) ? ME_HIDE : 0) ); } short BM_Edge_Flag_To_MEFlag(BMEdge *eed) { const char hflag = eed->head.hflag; return ( ((hflag & BM_SELECT) ? SELECT : 0) | ((hflag & BM_SEAM) ? ME_SEAM : 0) | ((hflag & BM_SHARP) ? ME_SHARP : 0) | ((hflag & BM_HIDDEN) ? ME_HIDE : 0) | ((BM_Wire_Edge(NULL, eed)) ? ME_LOOSEEDGE : 0) | /* not typical */ (ME_EDGEDRAW | ME_EDGERENDER) ); } char BM_Face_Flag_To_MEFlag(BMFace *efa) { const char hflag = efa->head.hflag; return ( ((hflag & BM_SELECT) ? ME_FACE_SEL : 0) | ((hflag & BM_SMOOTH) ? ME_SMOOTH : 0) | ((hflag & BM_HIDDEN) ? ME_HIDE : 0) ); } /* unused, type spesific functions below */ #if 0 /* BM FLAGS TO ME FLAGS Returns the flags stored in element, which much be either a BMVert, BMEdge, or BMFace, converted to mesh flags. */ short BMFlags_To_MEFlags(void *element) { const char src_htype = ((BMHeader *)element)->htype; if (src_htype == BM_FACE) { return BM_Face_Flag_To_MEFlag(element); } else if (src_htype == BM_EDGE) { return BM_Edge_Flag_To_MEFlag(element); } else if (src_htype == BM_VERT) { return BM_Vert_Flag_To_MEFlag(element); } else { return 0; } } /* BM FLAGS TO ME FLAGS Returns the flags stored in element, which much be either a MVert, MEdge, or MPoly, converted to mesh flags. type must be either BM_VERT, BM_EDGE, or BM_FACE. */ char MEFlags_To_BMFlags(const short hflag, const char htype) { if (htype == BM_FACE) { return BM_Face_Flag_From_MEFlag(hflag); } else if (htype == BM_EDGE) { return BM_Edge_Flag_From_MEFlag(hflag); } else if (htype == BM_VERT) { return BM_Vert_Flag_From_MEFlag(hflag); } else { return 0; } } #endif