#include "MEM_guardedalloc.h" #include "BKE_utildefines.h" #include "BLI_arithb.h" #include "BLI_rand.h" #include "DNA_object_types.h" #include "ED_mesh.h" #include "bmesh.h" #include "mesh_intern.h" #include #include #include #include #define SUBD_SPLIT 1 #define FACE_NEW 1 #define MAX_FACE 800 /*stuff for the flag paramter. note that what used to live in "beauty" and in "seltype" live here. still have to convert the beauty flags over, which is why it starts at 128 (to avoid collision).*/ #define SELTYPE_INNER 128 /* NOTE: beauty has been renamed to flag! */ /* note: this is a pattern-based edge subdivider. it tries to match a pattern to edge selections on faces, then executes functions to cut them. */ typedef struct subdpattern { int seledges[20]; //selected edges mask, for splitting /*verts starts at the first new vert cut, not the first vert in the face*/ void (*connectexec)(BMesh *bm, BMFace *face, BMVert **verts, int numcuts, int flag, float rad); int len; /*total number of verts*/ } subdpattern; /*generic subdivision rules: * two selected edges in a face should make a link between them. * one edge should do, what? make pretty topology, or just split the edge only? */ /* calculates offset for co, based on fractal, sphere or smooth settings */ static void alter_co(float *co, BMEdge *edge, float rad, int flag, float perc, BMVert *vsta, BMVert *vend) { float vec1[3], fac; if(flag & B_SMOOTH) { /* we calculate an offset vector vec1[], to be added to *co */ float len, fac, nor[3], nor1[3], nor2[3]; VecSubf(nor, vsta->co, vend->co); len= 0.5f*Normalize(nor); VECCOPY(nor1, vsta->no); VECCOPY(nor2, vend->no); /* cosine angle */ fac= nor[0]*nor1[0] + nor[1]*nor1[1] + nor[2]*nor1[2] ; vec1[0]= fac*nor1[0]; vec1[1]= fac*nor1[1]; vec1[2]= fac*nor1[2]; /* cosine angle */ fac= -nor[0]*nor2[0] - nor[1]*nor2[1] - nor[2]*nor2[2] ; vec1[0]+= fac*nor2[0]; vec1[1]+= fac*nor2[1]; vec1[2]+= fac*nor2[2]; vec1[0]*= rad*len; vec1[1]*= rad*len; vec1[2]*= rad*len; co[0] += vec1[0]; co[1] += vec1[1]; co[2] += vec1[2]; } else { if(rad > 0.0) { /* subdivide sphere */ Normalize(co); co[0]*= rad; co[1]*= rad; co[2]*= rad; } else if(rad< 0.0) { /* fractal subdivide */ fac= rad* VecLenf(vsta->co, vend->co); vec1[0]= fac*(float)(0.5-BLI_drand()); vec1[1]= fac*(float)(0.5-BLI_drand()); vec1[2]= fac*(float)(0.5-BLI_drand()); VecAddf(co, co, vec1); } } } /* assumes in the edge is the correct interpolated vertices already */ /* percent defines the interpolation, rad and flag are for special options */ /* results in new vertex with correct coordinate, vertex normal and weight group info */ static BMVert *bm_subdivide_edge_addvert(BMesh *bm, BMEdge *edge, float rad, int flag, float percent, BMEdge **out, BMVert *vsta, BMVert *vend) { BMVert *ev; // float co[3]; ev = BM_Split_Edge(bm, edge->v1, edge, out, percent, 1); if (flag & SELTYPE_INNER) BM_Select_Vert(bm, ev, 1); /* offset for smooth or sphere or fractal */ alter_co(ev->co, edge, rad, flag, percent, vsta, vend); #if 0 //TODO /* clip if needed by mirror modifier */ if (edge->v1->f2) { if ( edge->v1->f2 & edge->v2->f2 & 1) { co[0]= 0.0f; } if ( edge->v1->f2 & edge->v2->f2 & 2) { co[1]= 0.0f; } if ( edge->v1->f2 & edge->v2->f2 & 4) { co[2]= 0.0f; } } #endif return ev; } static BMVert *subdivideedgenum(BMesh *bm, BMEdge *edge, int curpoint, int totpoint, float rad, int flag, BMEdge **newe, BMVert *vsta, BMVert *vend) { BMVert *ev; float percent; if (flag & (B_PERCENTSUBD) && totpoint == 1) /*I guess the idea is vertices store what percent to use?*/ //percent=(float)(edge->tmp.l)/32768.0f; percent= 1.0; //edge->tmp.fp; else { percent= 1.0f/(float)(totpoint+1-curpoint); } /*{ float co[3], co2[3]; VecSubf(co, edge->v2->co, edge->v1->co); VecMulf(co, 1.0f/(float)(totpoint+1-curpoint)); VecAddf(co2, edge->v1->co, co); */ ev= bm_subdivide_edge_addvert(bm, edge, rad, flag, percent, newe, vsta, vend); /* VECCOPY(ev->co, co2); } */ return ev; } static void bm_subdivide_multicut(BMesh *bm, BMEdge *edge, float rad, int flag, int numcuts, BMVert *vsta, BMVert *vend) { BMEdge *eed = edge, *newe; BMVert *v; int i; for(i=0;idata.i; flag = BMO_GetSlot(op, BMOP_ESUBDIVIDE_FLAG)->data.i; rad = BMO_GetSlot(op, BMOP_ESUBDIVIDE_RADIUS)->data.f; selaction = BMO_GetSlot(op, BMOP_ESUBDIVIDE_SELACTION)->data.i; if (selaction == SUBDIV_SELECT_INNER) flag |= SELTYPE_INNER; einput = BMO_GetSlot(op, BMOP_ESUBDIVIDE_EDGES); /*first go through and split edges*/ for (i=0; ilen; i++) { edge = ((BMEdge**)einput->data.p)[i]; BMO_SetFlag(bmesh, edge, SUBD_SPLIT); } for (face=BMIter_New(&fiter, bmesh, BM_FACES, NULL); face; face=BMIter_Step(&fiter)) { /*figure out which pattern to use*/ i = 0; V_RESET(edges); V_RESET(verts); for (nl=BMIter_New(&liter, bmesh, BM_LOOPS_OF_FACE, face); nl; nl=BMIter_Step(&liter)) { V_GROW(edges); V_GROW(verts); edges[i] = nl->e; verts[i] = nl->v; i++; } for (i=0; ilen == face->len) { for (a=0; alen; a++) { matched = 1; for (b=0; blen; b++) { j = (b + a) % pat->len; if ((!!BMO_TestFlag(bmesh, edges[j], SUBD_SPLIT)) != (!!pat->seledges[b])) { matched = 0; break; } } if (matched) break; } if (matched) { V_GROW(facedata); BMO_SetFlag(bmesh, face, SUBD_SPLIT); j = V_COUNT(facedata) - 1; facedata[j].pat = pat; facedata[j].start = verts[a]; break; } } } } /*go through and split edges*/ for (i=0; ilen; i++) { edge = ((BMEdge**)einput->data.p)[i]; bm_subdivide_multicut(bmesh, edge, rad, flag, numcuts, edge->v1, edge->v2); //BM_Split_Edge_Multi(bmesh, edge, numcuts); } //if (facedata) V_FREE(facedata); //return; i = 0; for (face=BMIter_New(&fiter, bmesh, BM_FACES, NULL); face; face=BMIter_Step(&fiter)) { /*figure out which pattern to use*/ V_RESET(verts); if (BMO_TestFlag(bmesh, face, SUBD_SPLIT) == 0) continue; pat = facedata[i].pat; if (!pat) continue; j = a = 0; for (nl=BMIter_New(&liter, bmesh, BM_LOOPS_OF_FACE, face); nl; nl=BMIter_Step(&liter)) { if (nl->v == facedata[i].start) { a = j+1; break; } j++; } for (j=0; jlen; j++) { V_GROW(verts); } j = 0; for (nl=BMIter_New(&liter, bmesh, BM_LOOPS_OF_FACE, face); nl; nl=BMIter_Step(&liter)) { b = (j-a+face->len) % face->len; verts[b] = nl->v; j += 1; } pat->connectexec(bmesh, face, verts, numcuts, flag, rad); i++; } if (facedata) V_FREE(facedata); if (edges) V_FREE(edges); if (verts) V_FREE(verts); } /*editmesh-emulating function*/ void BM_esubdivideflag(Object *obedit, BMesh *bm, int selflag, float rad, int flag, int numcuts, int seltype) { BMOperator op; BMO_Init_Op(&op, BMOP_ESUBDIVIDE); BMO_Set_Int(&op, BMOP_ESUBDIVIDE_NUMCUTS, numcuts); BMO_Set_Int(&op, BMOP_ESUBDIVIDE_FLAG, flag); BMO_Set_Float(&op, BMOP_ESUBDIVIDE_RADIUS, rad); BMO_Set_Int(&op, BMOP_ESUBDIVIDE_SELACTION, seltype); BMO_HeaderFlag_To_Slot(bm, &op, BMOP_ESUBDIVIDE_EDGES, selflag, BM_EDGE); BMO_Exec_Op(bm, &op); BMO_Finish_Op(bm, &op); } void BM_esubdivideflag_conv(Object *obedit,EditMesh *em,int selflag, float rad, int flag, int numcuts, int seltype) { BMesh *bm = editmesh_to_bmesh(em); EditMesh *em2; BM_esubdivideflag(obedit, bm, selflag, rad, flag, numcuts, seltype); em2 = bmesh_to_editmesh(bm); free_editMesh(em); *em = *em2; MEM_freeN(em2); BM_Free_Mesh(bm); }