#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 /* 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 beauty, 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 beauty, float perc) { float vec1[3], fac; if(beauty & B_SMOOTH) { /* we calculate an offset vector vec1[], to be added to *co */ float len, fac, nor[3], nor1[3], nor2[3]; VecSubf(nor, edge->v1->co, edge->v2->co); len= 0.5f*Normalize(nor); VECCOPY(nor1, edge->v1->no); VECCOPY(nor2, edge->v2->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(edge->v1->co, edge->v2->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 beauty 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 beauty, float percent, BMEdge **out) { BMVert *ev; // float co[3]; ev = BM_Split_Edge(bm, edge->v1, edge, out, percent, 1); /* offset for smooth or sphere or fractal */ alter_co(ev->co, edge, rad, beauty, percent); #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 beauty, BMEdge **newe) { BMVert *ev; float percent; if (beauty & (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, beauty, percent, newe); /* VECCOPY(ev->co, co2); } */ return ev; } static void bm_subdivide_multicut(BMesh *bm, BMEdge *edge, float rad, int beauty, int numcuts) { 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; 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*/ if (face->len > MAX_FACE) continue; i = 0; for (nl=BMIter_New(&liter, bmesh, BM_LOOPS_OF_FACE, face); nl; nl=BMIter_Step(&liter)) { 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); //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*/ if (face->len > MAX_FACE) continue; 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++; } 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); } /*editmesh-emulating function*/ void BM_esubdivideflag(Object *obedit, BMesh *bm, int flag, float rad, int beauty, 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, beauty); 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, flag, BM_EDGE); BMO_Exec_Op(bm, &op); BMO_Finish_Op(bm, &op); }