diff options
Diffstat (limited to 'source/blender/bmesh/operators/bmo_subdivide.c')
-rw-r--r-- | source/blender/bmesh/operators/bmo_subdivide.c | 1104 |
1 files changed, 1104 insertions, 0 deletions
diff --git a/source/blender/bmesh/operators/bmo_subdivide.c b/source/blender/bmesh/operators/bmo_subdivide.c new file mode 100644 index 00000000000..310762e0e37 --- /dev/null +++ b/source/blender/bmesh/operators/bmo_subdivide.c @@ -0,0 +1,1104 @@ +/* + * ***** 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. + * + * Contributor(s): Joseph Eagar. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#include "MEM_guardedalloc.h" + +#include "BLI_math.h" +#include "BLI_rand.h" +#include "BLI_array.h" +#include "BLI_noise.h" + +#include "BKE_customdata.h" + +#include "DNA_object_types.h" + +#include "ED_mesh.h" + +#include "bmesh.h" +#include "bmesh_private.h" + +#include "bmesh_operators_private.h" /* own include */ + +#include "bmo_subdivide.h" /* own include */ + +/* flags for all elements share a common bitfield space */ +#define SUBD_SPLIT 1 + +#define EDGE_PERCENT 2 + +/* I don't think new faces are flagged, currently, but + * better safe than sorry. */ +#define FACE_CUSTOMFILL 4 +#define ELE_INNER 8 +#define ELE_SPLIT 16 + +/* + * NOTE: beauty has been renamed to flag! + */ + +/* 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? + */ + +/* connects face with smallest len, which I think should always be correct for + * edge subdivision */ +static BMEdge *connect_smallest_face(BMesh *bm, BMVert *v1, BMVert *v2, BMFace **r_nf) +{ + BMIter iter, iter2; + BMVert *v; + BMLoop *nl; + BMFace *face, *curf = NULL; + + /* this isn't the best thing in the world. it doesn't handle cases where there's + * multiple faces yet. that might require a convexity test to figure out which + * face is "best," and who knows what for non-manifold conditions. */ + for (face = BM_iter_new(&iter, bm, BM_FACES_OF_VERT, v1); face; face = BM_iter_step(&iter)) { + for (v = BM_iter_new(&iter2, bm, BM_VERTS_OF_FACE, face); v; v = BM_iter_step(&iter2)) { + if (v == v2) { + if (!curf || face->len < curf->len) curf = face; + } + } + } + + if (curf) { + face = BM_face_split(bm, curf, v1, v2, &nl, NULL); + + if (r_nf) *r_nf = face; + return nl ? nl->e : NULL; + } + + return NULL; +} +/* calculates offset for co, based on fractal, sphere or smooth settings */ +static void alter_co(BMesh *bm, BMVert *v, BMEdge *UNUSED(origed), const subdparams *params, float perc, + BMVert *vsta, BMVert *vend) +{ + float tvec[3], prev_co[3], fac; + float *co = NULL; + int i, totlayer = CustomData_number_of_layers(&bm->vdata, CD_SHAPEKEY); + + BM_vert_normal_update_all(bm, v); + + co = CustomData_bmesh_get_n(&bm->vdata, v->head.data, CD_SHAPEKEY, params->origkey); + copy_v3_v3(prev_co, co); + + if (params->beauty & B_SMOOTH) { + /* we calculate an offset vector vec1[], to be added to *co */ + float len, nor[3], nor1[3], nor2[3], smooth = params->smooth; + + sub_v3_v3v3(nor, vsta->co, vend->co); + len = 0.5f * normalize_v3(nor); + + copy_v3_v3(nor1, vsta->no); + copy_v3_v3(nor2, vend->no); + + /* cosine angle */ + fac= dot_v3v3(nor, nor1); + mul_v3_v3fl(tvec, nor1, fac); + + /* cosine angle */ + fac = -dot_v3v3(nor, nor2); + madd_v3_v3fl(tvec, nor2, fac); + + /* falloff for multi subdivide */ + smooth *= sqrtf(fabsf(1.0f - 2.0f * fabsf(0.5f-perc))); + + mul_v3_fl(tvec, smooth * len); + + add_v3_v3(co, tvec); + } + else if (params->beauty & B_SPHERE) { /* subdivide sphere */ + normalize_v3(co); + mul_v3_fl(co, params->smooth); + } + + if (params->beauty & B_FRACTAL) { + float len = len_v3v3(vsta->co, vend->co); + float vec2[3] = {0.0f, 0.0f, 0.0f}, co2[3]; + + fac = params->fractal * len; + + add_v3_v3(vec2, vsta->no); + add_v3_v3(vec2, vend->no); + mul_v3_fl(vec2, 0.5f); + + add_v3_v3v3(co2, v->co, params->off); + tvec[0] = fac * (BLI_gTurbulence(1.0, co2[0], co2[1], co2[2], 15, 0, 1) - 0.5f); + tvec[1] = fac * (BLI_gTurbulence(1.0, co2[0], co2[1], co2[2], 15, 0, 1) - 0.5f); + tvec[2] = fac * (BLI_gTurbulence(1.0, co2[0], co2[1], co2[2], 15, 0, 1) - 0.5f); + + mul_v3_v3(vec2, tvec); + + /* add displacemen */ + add_v3_v3v3(co, co, vec2); + } + + /* apply the new difference to the rest of the shape keys, + * note that this doent take rotations into account, we _could_ support + * this by getting the normals and coords for each shape key and + * re-calculate the smooth value for each but this is quite involved. + * for now its ok to simply apply the difference IMHO - campbell */ + sub_v3_v3v3(tvec, prev_co, co); + + for (i = 0; i < totlayer; i++) { + if (params->origkey != i) { + co = CustomData_bmesh_get_n(&bm->vdata, v->head.data, CD_SHAPEKEY, i); + sub_v3_v3(co, tvec); + } + } + +} + +/* 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, BMEdge *oedge, + const subdparams *params, float percent, + float percent2, + BMEdge **out, BMVert *vsta, BMVert *vend) +{ + BMVert *ev; + + ev = BM_edge_split(bm, edge->v1, edge, out, percent); + + BMO_elem_flag_enable(bm, ev, ELE_INNER); + + /* offset for smooth or sphere or fractal */ + alter_co(bm, ev, oedge, params, percent2, vsta, vend); + +#if 0 //BMESH_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, BMEdge *oedge, + int curpoint, int totpoint, const subdparams *params, + BMEdge **newe, BMVert *vsta, BMVert *vend) +{ + BMVert *ev; + float percent, percent2 = 0.0f; + + if (BMO_elem_flag_test(bm, edge, EDGE_PERCENT) && totpoint == 1) + percent = BMO_slot_map_float_get(bm, params->op, "edgepercents", edge); + else { + percent = 1.0f / (float)(totpoint + 1-curpoint); + percent2 = (float)(curpoint + 1) / (float)(totpoint + 1); + + } + + ev = bm_subdivide_edge_addvert(bm, edge, oedge, params, percent, + percent2, newe, vsta, vend); + return ev; +} + +static void bm_subdivide_multicut(BMesh *bm, BMEdge *edge, const subdparams *params, + BMVert *vsta, BMVert *vend) +{ + BMEdge *eed = edge, *newe, temp = *edge; + BMVert *v, ov1 = *edge->v1, ov2 = *edge->v2, *v1 = edge->v1, *v2 = edge->v2; + int i, numcuts = params->numcuts; + + temp.v1 = &ov1; + temp.v2 = &ov2; + + for (i = 0; i < numcuts; i++) { + v = subdivideedgenum(bm, eed, &temp, i, params->numcuts, params, &newe, vsta, vend); + + BMO_elem_flag_enable(bm, v, SUBD_SPLIT); + BMO_elem_flag_enable(bm, eed, SUBD_SPLIT); + BMO_elem_flag_enable(bm, newe, SUBD_SPLIT); + + BMO_elem_flag_enable(bm, v, ELE_SPLIT); + BMO_elem_flag_enable(bm, eed, ELE_SPLIT); + BMO_elem_flag_enable(bm, newe, SUBD_SPLIT); + + BM_CHECK_ELEMENT(bm, v); + if (v->e) BM_CHECK_ELEMENT(bm, v->e); + if (v->e && v->e->l) BM_CHECK_ELEMENT(bm, v->e->l->f); + } + + alter_co(bm, v1, &temp, params, 0, &ov1, &ov2); + alter_co(bm, v2, &temp, params, 1.0, &ov1, &ov2); +} + +/* note: the patterns are rotated as necassary to + * match the input geometry. they're based on the + * pre-split state of the face */ + +/* + * v3---------v2 + * | | + * | | + * | | + * | | + * v4---v0---v1 + */ +static void quad_1edge_split(BMesh *bm, BMFace *UNUSED(face), + BMVert **verts, const subdparams *params) +{ + BMFace *nf; + int i, add, numcuts = params->numcuts; + + /* if it's odd, the middle face is a quad, otherwise it's a triangl */ + if ((numcuts % 2) == 0) { + add = 2; + for (i = 0; i < numcuts; i++) { + if (i == numcuts / 2) { + add -= 1; + } + connect_smallest_face(bm, verts[i], verts[numcuts + add], &nf); + } + } + else { + add = 2; + for (i = 0; i < numcuts; i++) { + connect_smallest_face(bm, verts[i], verts[numcuts + add], &nf); + if (i == numcuts/2) { + add -= 1; + connect_smallest_face(bm, verts[i], verts[numcuts + add], &nf); + } + } + + } +} + +static SubDPattern quad_1edge = { + {1, 0, 0, 0}, + quad_1edge_split, + 4, +}; + + +/* + * v6--------v5 + * | | + * | |v4s + * | |v3s + * | s s | + * v7-v0--v1-v2 + */ +static void quad_2edge_split_path(BMesh *bm, BMFace *UNUSED(face), BMVert **verts, + const subdparams *params) +{ + BMFace *nf; + int i, numcuts = params->numcuts; + + for (i = 0; i < numcuts; i++) { + connect_smallest_face(bm, verts[i], verts[numcuts + (numcuts - i)], &nf); + } + connect_smallest_face(bm, verts[numcuts * 2 + 3], verts[numcuts * 2 + 1], &nf); +} + +static SubDPattern quad_2edge_path = { + {1, 1, 0, 0}, + quad_2edge_split_path, + 4, +}; + +/* + * v6--------v5 + * | | + * | |v4s + * | |v3s + * | s s | + * v7-v0--v1-v2 + */ +static void quad_2edge_split_innervert(BMesh *bm, BMFace *UNUSED(face), BMVert **verts, + const subdparams *params) +{ + BMFace *nf; + BMVert *v, *lastv; + BMEdge *e, *ne, olde; + int i, numcuts = params->numcuts; + + lastv = verts[numcuts]; + + for (i = numcuts - 1; i >= 0; i--) { + e = connect_smallest_face(bm, verts[i], verts[numcuts + (numcuts - i)], &nf); + + olde = *e; + v = bm_subdivide_edge_addvert(bm, e, &olde, params, 0.5f, 0.5f, &ne, e->v1, e->v2); + + if (i != numcuts - 1) { + connect_smallest_face(bm, lastv, v, &nf); + } + + lastv = v; + } + + connect_smallest_face(bm, lastv, verts[numcuts * 2 + 2], &nf); +} + +static SubDPattern quad_2edge_innervert = { + {1, 1, 0, 0}, + quad_2edge_split_innervert, + 4, +}; + +/* + * v6--------v5 + * | | + * | |v4s + * | |v3s + * | s s | + * v7-v0--v1-v2 + * + */ +static void quad_2edge_split_fan(BMesh *bm, BMFace *UNUSED(face), BMVert **verts, + const subdparams *params) +{ + BMFace *nf; + /* BMVert *v; */ /* UNUSED */ + /* BMVert *lastv = verts[2]; */ /* UNUSED */ + /* BMEdge *e, *ne; */ /* UNUSED */ + int i, numcuts = params->numcuts; + + for (i = 0; i < numcuts; i++) { + connect_smallest_face(bm, verts[i], verts[numcuts * 2 + 2], &nf); + connect_smallest_face(bm, verts[numcuts + (numcuts - i)], verts[numcuts * 2 + 2], &nf); + } +} + +static SubDPattern quad_2edge_fan = { + {1, 1, 0, 0}, + quad_2edge_split_fan, + 4, +}; + +/* + * s s + * v8--v7--v6-v5 + * | | + * | v4 s + * | | + * | v3 s + * | s s | + * v9-v0--v1-v2 + */ +static void quad_3edge_split(BMesh *bm, BMFace *UNUSED(face), BMVert **verts, + const subdparams *params) +{ + BMFace *nf; + int i, add = 0, numcuts = params->numcuts; + + for (i = 0; i < numcuts; i++) { + if (i == numcuts / 2) { + if (numcuts % 2 != 0) { + connect_smallest_face(bm, verts[numcuts - i - 1 + add], verts[i + numcuts + 1], &nf); + } + add = numcuts * 2 + 2; + } + connect_smallest_face(bm, verts[numcuts - i - 1 + add], verts[i + numcuts + 1], &nf); + } + + for (i = 0; i < numcuts / 2 + 1; i++) { + connect_smallest_face(bm, verts[i], verts[(numcuts - i) + numcuts * 2 + 1], &nf); + } +} + +static SubDPattern quad_3edge = { + {1, 1, 1, 0}, + quad_3edge_split, + 4, +}; + +/* + * v8--v7-v6--v5 + * | s | + * |v9 s s|v4 + * first line | | last line + * |v10s s s|v3 + * v11-v0--v1-v2 + * + * it goes from bottom up + */ +static void quad_4edge_subdivide(BMesh *bm, BMFace *UNUSED(face), BMVert **verts, + const subdparams *params) +{ + BMFace *nf; + BMVert *v, *v1, *v2; + BMEdge *e, *ne, temp; + BMVert **lines; + int numcuts = params->numcuts; + int i, j, a, b, s = numcuts + 2 /* , totv = numcuts * 4 + 4 */; + + lines = MEM_callocN(sizeof(BMVert *)*(numcuts + 2)*(numcuts + 2), "q_4edge_split"); + /* build a 2-dimensional array of verts, + * containing every vert (and all new ones) + * in the face */ + + /* first line */ + for (i = 0; i < numcuts + 2; i++) { + lines[i] = verts[numcuts * 3 + 2 + (numcuts - i + 1)]; + } + + /* last line */ + for (i = 0; i < numcuts + 2; i++) { + lines[(s - 1) * s + i] = verts[numcuts + i]; + } + + /* first and last members of middle lines */ + for (i = 0; i < numcuts; i++) { + a = i; + b = numcuts + 1 + numcuts + 1 + (numcuts - i - 1); + + e = connect_smallest_face(bm, verts[a], verts[b], &nf); + if (!e) + continue; + + BMO_elem_flag_enable(bm, e, ELE_INNER); + BMO_elem_flag_enable(bm, nf, ELE_INNER); + + + v1 = lines[(i + 1)*s] = verts[a]; + v2 = lines[(i + 1)*s + s - 1] = verts[b]; + + temp = *e; + for (a = 0; a < numcuts; a++) { + v = subdivideedgenum(bm, e, &temp, a, numcuts, params, &ne, + v1, v2); + if (!v) + bmesh_error(); + + BMO_elem_flag_enable(bm, ne, ELE_INNER); + lines[(i + 1) * s + a + 1] = v; + } + } + + for (i = 1; i < numcuts + 2; i++) { + for (j = 1; j < numcuts + 1; j++) { + a = i * s + j; + b = (i - 1) * s + j; + e = connect_smallest_face(bm, lines[a], lines[b], &nf); + if (!e) + continue; + + BMO_elem_flag_enable(bm, e, ELE_INNER); + BMO_elem_flag_enable(bm, nf, ELE_INNER); + } + } + + MEM_freeN(lines); +} + +/* + * v3 + * / \ + * / \ + * / \ + * / \ + * / \ + * v4--v0--v1--v2 + * s s + */ +static void tri_1edge_split(BMesh *bm, BMFace *UNUSED(face), BMVert **verts, + const subdparams *params) +{ + BMFace *nf; + int i, numcuts = params->numcuts; + + for (i = 0; i < numcuts; i++) { + connect_smallest_face(bm, verts[i], verts[numcuts + 1], &nf); + } +} + +static SubDPattern tri_1edge = { + {1, 0, 0}, + tri_1edge_split, + 3, +}; + +/* v5 + * / \ + * s v6/---\ v4 s + * / \ / \ + * sv7/---v---\ v3 s + * / \/ \/ \ + * v8--v0--v1--v2 + * s s + */ +static void tri_3edge_subdivide(BMesh *bm, BMFace *UNUSED(face), BMVert **verts, + const subdparams *params) +{ + BMFace *nf; + BMEdge *e, *ne, temp; + BMVert ***lines, *v, ov1, ov2; + void *stackarr[1]; + int i, j, a, b, numcuts = params->numcuts; + + /* number of verts in each lin */ + lines = MEM_callocN(sizeof(void *)*(numcuts + 2), "triangle vert table"); + + lines[0] = (BMVert **) stackarr; + lines[0][0] = verts[numcuts * 2 + 1]; + + lines[numcuts + 1] = MEM_callocN(sizeof(void *) * (numcuts + 2), "triangle vert table 2"); + for (i = 0; i < numcuts; i++) { + lines[numcuts + 1][i + 1] = verts[i]; + } + lines[numcuts + 1][0] = verts[numcuts * 3 + 2]; + lines[numcuts + 1][numcuts + 1] = verts[numcuts]; + + for (i = 0; i < numcuts; i++) { + lines[i + 1] = MEM_callocN(sizeof(void *)*(2 + i), "triangle vert table row"); + a = numcuts * 2 + 2 + i; + b = numcuts + numcuts - i; + e = connect_smallest_face(bm, verts[a], verts[b], &nf); + if (!e) goto cleanup; + + BMO_elem_flag_enable(bm, e, ELE_INNER); + BMO_elem_flag_enable(bm, nf, ELE_INNER); + + lines[i + 1][0] = verts[a]; + lines[i + 1][i + 1] = verts[b]; + + temp = *e; + ov1 = *verts[a]; + ov2 = *verts[b]; + temp.v1 = &ov1; + temp.v2 = &ov2; + for (j = 0; j < i; j++) { + v = subdivideedgenum(bm, e, &temp, j, i, params, &ne, + verts[a], verts[b]); + lines[i + 1][j + 1] = v; + + BMO_elem_flag_enable(bm, ne, ELE_INNER); + } + } + + /* + * v5 + * / \ + * s v6/---\ v4 s + * / \ / \ + * sv7/---v---\ v3 s + * / \/ \/ \ + * v8--v0--v1--v2 + * s s + */ + for (i = 1; i < numcuts + 1; i++) { + for (j = 0; j < i; j++) { + e = connect_smallest_face(bm, lines[i][j], lines[i + 1][j + 1], &nf); + + BMO_elem_flag_enable(bm, e, ELE_INNER); + BMO_elem_flag_enable(bm, nf, ELE_INNER); + + e = connect_smallest_face(bm, lines[i][j + 1], lines[i + 1][j + 1], &nf); + + BMO_elem_flag_enable(bm, e, ELE_INNER); + BMO_elem_flag_enable(bm, nf, ELE_INNER); + } + } + +cleanup: + for (i = 1; i < numcuts + 2; i++) { + if (lines[i]) MEM_freeN(lines[i]); + } + + MEM_freeN(lines); +} + +static SubDPattern tri_3edge = { + {1, 1, 1}, + tri_3edge_subdivide, + 3, +}; + + +static SubDPattern quad_4edge = { + {1, 1, 1, 1}, + quad_4edge_subdivide, + 4, +}; + +static SubDPattern *patterns[] = { + NULL, //quad single edge pattern is inserted here + NULL, //quad corner vert pattern is inserted here + NULL, //tri single edge pattern is inserted here + NULL, + &quad_3edge, + NULL, +}; + +#define PLEN (sizeof(patterns) / sizeof(void *)) + +typedef struct subd_facedata { + BMVert *start; SubDPattern *pat; + int totedgesel; //only used if pat was NULL, e.g. no pattern was found + BMFace *face; +} subd_facedata; + +void esubdivide_exec(BMesh *bmesh, BMOperator *op) +{ + BMOpSlot *einput; + SubDPattern *pat; + subdparams params; + subd_facedata *facedata = NULL; + BMIter viter, fiter, liter; + BMVert *v, **verts = NULL; + BMEdge *edge, **edges = NULL; + BMLoop *nl, *l, **splits = NULL, **loops = NULL; + BMFace *face; + BLI_array_declare(splits); + BLI_array_declare(loops); + BLI_array_declare(facedata); + BLI_array_declare(edges); + BLI_array_declare(verts); + float smooth, fractal; + int beauty, cornertype, singleedge, gridfill; + int skey, seed, i, j, matched, a, b, numcuts, totesel; + + BMO_slot_buffer_flag_enable(bmesh, op, "edges", SUBD_SPLIT, BM_EDGE); + + numcuts = BMO_slot_int_get(op, "numcuts"); + seed = BMO_slot_int_get(op, "seed"); + smooth = BMO_slot_float_get(op, "smooth"); + fractal = BMO_slot_float_get(op, "fractal"); + beauty = BMO_slot_int_get(op, "beauty"); + cornertype = BMO_slot_int_get(op, "quadcornertype"); + singleedge = BMO_slot_int_get(op, "singleedge"); + gridfill = BMO_slot_int_get(op, "gridfill"); + + BLI_srandom(seed); + + patterns[1] = NULL; + //straight cut is patterns[1] == NULL + switch (cornertype) { + case SUBD_PATH: + patterns[1] = &quad_2edge_path; + break; + case SUBD_INNERVERT: + patterns[1] = &quad_2edge_innervert; + break; + case SUBD_FAN: + patterns[1] = &quad_2edge_fan; + break; + } + + if (singleedge) { + patterns[0] = &quad_1edge; + patterns[2] = &tri_1edge; + } + else { + patterns[0] = NULL; + patterns[2] = NULL; + } + + if (gridfill) { + patterns[3] = &quad_4edge; + patterns[5] = &tri_3edge; + } + else { + patterns[3] = NULL; + patterns[5] = NULL; + } + + /* add a temporary shapekey layer to store displacements on current geometr */ + BM_data_layer_add(bmesh, &bmesh->vdata, CD_SHAPEKEY); + skey = CustomData_number_of_layers(&bmesh->vdata, CD_SHAPEKEY) - 1; + + BM_ITER(v, &viter, bmesh, BM_VERTS_OF_MESH, NULL) { + float *co = CustomData_bmesh_get_n(&bmesh->vdata, v->head.data, CD_SHAPEKEY, skey); + copy_v3_v3(co, v->co); + } + + /* first go through and tag edge */ + BMO_slot_from_flag(bmesh, op, "edges", + SUBD_SPLIT, BM_EDGE); + + params.numcuts = numcuts; + params.op = op; + params.smooth = smooth; + params.seed = seed; + params.fractal = fractal; + params.beauty = beauty; + params.origkey = skey; + params.off[0] = (float)BLI_drand() * 200.0f; + params.off[1] = (float)BLI_drand() * 200.0f; + params.off[2] = (float)BLI_drand() * 200.0f; + + BMO_slot_map_to_flag(bmesh, op, "custompatterns", + FACE_CUSTOMFILL); + + BMO_slot_map_to_flag(bmesh, op, "edgepercents", + EDGE_PERCENT); + + for (face = BM_iter_new(&fiter, bmesh, BM_FACES_OF_MESH, NULL); + face; + face = BM_iter_step(&fiter)) + { + BMEdge *e1 = NULL, *e2 = NULL; + float vec1[3], vec2[3]; + + /* figure out which pattern to us */ + + BLI_array_empty(edges); + BLI_array_empty(verts); + matched = 0; + + i = 0; + totesel = 0; + for (nl = BM_iter_new(&liter, bmesh, BM_LOOPS_OF_FACE, face); nl; nl = BM_iter_step(&liter)) { + BLI_array_growone(edges); + BLI_array_growone(verts); + edges[i] = nl->e; + verts[i] = nl->v; + + if (BMO_elem_flag_test(bmesh, edges[i], SUBD_SPLIT)) { + if (!e1) e1 = edges[i]; + else e2 = edges[i]; + + totesel++; + } + + i++; + } + + /* make sure the two edges have a valid angle to each othe */ + if (totesel == 2 && BM_edge_share_vert(e1, e2)) { + float angle; + + sub_v3_v3v3(vec1, e1->v2->co, e1->v1->co); + sub_v3_v3v3(vec2, e2->v2->co, e2->v1->co); + normalize_v3(vec1); + normalize_v3(vec2); + + angle = dot_v3v3(vec1, vec2); + angle = fabsf(angle); + if (fabsf(angle - 1.0f) < 0.01f) { + totesel = 0; + } + } + + if (BMO_elem_flag_test(bmesh, face, FACE_CUSTOMFILL)) { + pat = BMO_slot_map_data_get(bmesh, op, + "custompatterns", face); + for (i = 0; i < pat->len; i++) { + matched = 1; + for (j = 0; j < pat->len; j++) { + a = (j + i) % pat->len; + if ((!!BMO_elem_flag_test(bmesh, edges[a], SUBD_SPLIT)) != (!!pat->seledges[j])) { + matched = 0; + break; + } + } + if (matched) { + BLI_array_growone(facedata); + b = BLI_array_count(facedata) - 1; + facedata[b].pat = pat; + facedata[b].start = verts[i]; + facedata[b].face = face; + facedata[b].totedgesel = totesel; + BMO_elem_flag_enable(bmesh, face, SUBD_SPLIT); + break; + } + } + + /* obvously don't test for other patterns matchin */ + continue; + } + + for (i = 0; i < PLEN; i++) { + pat = patterns[i]; + if (!pat) continue; + + if (pat->len == face->len) { + for (a = 0; a < pat->len; a++) { + matched = 1; + for (b = 0; b < pat->len; b++) { + j = (b + a) % pat->len; + if ((!!BMO_elem_flag_test(bmesh, edges[j], SUBD_SPLIT)) != (!!pat->seledges[b])) { + matched = 0; + break; + } + } + if (matched) { + break; + } + } + if (matched) { + BLI_array_growone(facedata); + j = BLI_array_count(facedata) - 1; + + BMO_elem_flag_enable(bmesh, face, SUBD_SPLIT); + + facedata[j].pat = pat; + facedata[j].start = verts[a]; + facedata[j].face = face; + facedata[j].totedgesel = totesel; + break; + } + } + + } + + if (!matched && totesel) { + BLI_array_growone(facedata); + j = BLI_array_count(facedata) - 1; + + BMO_elem_flag_enable(bmesh, face, SUBD_SPLIT); + facedata[j].totedgesel = totesel; + facedata[j].face = face; + } + } + + einput = BMO_slot_get(op, "edges"); + + /* go through and split edge */ + for (i = 0; i < einput->len; i++) { + edge = ((BMEdge **)einput->data.p)[i]; + bm_subdivide_multicut(bmesh, edge, ¶ms, edge->v1, edge->v2); + } + + i = 0; + for (i = 0; i < BLI_array_count(facedata); i++) { + face = facedata[i].face; + + /* figure out which pattern to us */ + BLI_array_empty(verts); + + pat = facedata[i].pat; + + if (!pat && facedata[i].totedgesel == 2) { + int vlen; + + /* ok, no pattern. we still may be able to do something */ + BLI_array_empty(loops); + BLI_array_empty(splits); + + /* for case of two edges, connecting them shouldn't be too har */ + BM_ITER(l, &liter, bmesh, BM_LOOPS_OF_FACE, face) { + BLI_array_growone(loops); + loops[BLI_array_count(loops) - 1] = l; + } + + vlen = BLI_array_count(loops); + + /* find the boundary of one of the split edge */ + for (a = 1; a < vlen; a++) { + if (!BMO_elem_flag_test(bmesh, loops[a - 1]->v, ELE_INNER) && + BMO_elem_flag_test(bmesh, loops[a]->v, ELE_INNER)) + { + break; + } + } + + if (BMO_elem_flag_test(bmesh, loops[(a + numcuts + 1) % vlen]->v, ELE_INNER)) { + b = (a + numcuts + 1) % vlen; + } + else { + /* find the boundary of the other edge. */ + for (j = 0; j < vlen; j++) { + b = (j + a + numcuts + 1) % vlen; + if (!BMO_elem_flag_test(bmesh, loops[b == 0 ? vlen - 1 : b - 1]->v, ELE_INNER) && + BMO_elem_flag_test(bmesh, loops[b]->v, ELE_INNER)) + { + break; + } + } + } + + b += numcuts - 1; + + for (j = 0; j < numcuts; j++) { + BLI_array_growone(splits); + splits[BLI_array_count(splits) - 1] = loops[a]; + + BLI_array_growone(splits); + splits[BLI_array_count(splits) - 1] = loops[b]; + + b = (b - 1) % vlen; + a = (a + 1) % vlen; + } + + //BM_face_legal_splits(bmesh, face, splits, BLI_array_count(splits)/2); + + for (j = 0; j < BLI_array_count(splits) / 2; j++) { + if (splits[j * 2]) { + /* BMFace *nf = */ /* UNUSED */ + BM_face_split(bmesh, face, splits[j * 2]->v, splits[j * 2 + 1]->v, &nl, NULL); + } + } + + continue; + } + else if (!pat) { + continue; + } + + j = a = 0; + for (nl = BM_iter_new(&liter, bmesh, BM_LOOPS_OF_FACE, face); + nl; + nl = BM_iter_step(&liter)) + { + if (nl->v == facedata[i].start) { + a = j + 1; + break; + } + j++; + } + + for (j = 0; j < face->len; j++) { + BLI_array_growone(verts); + } + + j = 0; + for (nl = BM_iter_new(&liter, bmesh, BM_LOOPS_OF_FACE, face); nl; nl = BM_iter_step(&liter)) { + b = (j - a + face->len) % face->len; + verts[b] = nl->v; + j += 1; + } + + BM_CHECK_ELEMENT(bmesh, face); + pat->connectexec(bmesh, face, verts, ¶ms); + } + + /* copy original-geometry displacements to current coordinate */ + BM_ITER(v, &viter, bmesh, BM_VERTS_OF_MESH, NULL) { + float *co = CustomData_bmesh_get_n(&bmesh->vdata, v->head.data, CD_SHAPEKEY, skey); + copy_v3_v3(v->co, co); + } + + BM_data_layer_free_n(bmesh, &bmesh->vdata, CD_SHAPEKEY, skey); + + if (facedata) BLI_array_free(facedata); + if (edges) BLI_array_free(edges); + if (verts) BLI_array_free(verts); + BLI_array_free(splits); + BLI_array_free(loops); + + BMO_slot_from_flag(bmesh, op, "outinner", + ELE_INNER, BM_ALL); + BMO_slot_from_flag(bmesh, op, "outsplit", + ELE_SPLIT, BM_ALL); + + BMO_slot_from_flag(bmesh, op, "geomout", + ELE_INNER|ELE_SPLIT|SUBD_SPLIT, BM_ALL); +} + +/* editmesh-emulating functio */ +void BM_mesh_esubdivideflag(Object *UNUSED(obedit), BMesh *bm, int flag, float smooth, + float fractal, int beauty, int numcuts, + int seltype, int cornertype, int singleedge, + int gridfill, int seed) +{ + BMOperator op; + + BMO_op_initf(bm, &op, "esubd edges=%he smooth=%f fractal=%f " + "beauty=%d numcuts=%d quadcornertype=%d singleedge=%d " + "gridfill=%d seed=%d", + flag, smooth, fractal, beauty, numcuts, + cornertype, singleedge, gridfill, seed); + + BMO_op_exec(bm, &op); + + if (seltype == SUBDIV_SELECT_INNER) { + BMOIter iter; + BMHeader *ele; + // int i; + + ele = BMO_iter_new(&iter, bm, &op, "outinner", BM_EDGE|BM_VERT); + for ( ; ele; ele = BMO_iter_step(&iter)) { + BM_elem_select_set(bm, ele, TRUE); + } + } + else if (seltype == SUBDIV_SELECT_LOOPCUT) { + BMOIter iter; + BMHeader *ele; + // int i; + + /* deselect input */ + BM_mesh_elem_flag_disable_all(bm, BM_VERT | BM_EDGE | BM_FACE, BM_ELEM_SELECT); + + ele = BMO_iter_new(&iter, bm, &op, "outinner", BM_EDGE|BM_VERT); + for ( ; ele; ele = BMO_iter_step(&iter)) { + BM_elem_select_set(bm, ele, TRUE); + + if (ele->htype == BM_VERT) { + BMEdge *e; + BMIter eiter; + + BM_ITER(e, &eiter, bm, BM_EDGES_OF_VERT, ele) { + if (!BM_elem_flag_test(e, BM_ELEM_SELECT) && + BM_elem_flag_test(e->v1, BM_ELEM_SELECT) && + BM_elem_flag_test(e->v2, BM_ELEM_SELECT)) + { + BM_elem_select_set(bm, e, TRUE); + bm->totedgesel += 1; + } + else if (BM_elem_flag_test(e, BM_ELEM_SELECT) && + (!BM_elem_flag_test(e->v1, BM_ELEM_SELECT) || + !BM_elem_flag_test(e->v2, BM_ELEM_SELECT))) + { + BM_elem_select_set(bm, e, FALSE); + bm->totedgesel -= 1; + } + } + } + } + } + + BMO_op_finish(bm, &op); +} + +void esplit_exec(BMesh *bm, BMOperator *op) +{ + BMOIter siter; + BMEdge *e; + subdparams params; + int skey; + + params.numcuts = BMO_slot_get(op, "numcuts")->data.i; + params.op = op; + + BM_data_layer_add(bm, &bm->vdata, CD_SHAPEKEY); + skey = CustomData_number_of_layers(&bm->vdata, CD_SHAPEKEY) - 1; + + params.origkey = skey; + + /* go through and split edge */ + BMO_ITER(e, &siter, bm, op, "edges", BM_EDGE) { + bm_subdivide_multicut(bm, e, ¶ms, e->v1, e->v2); + } + + BMO_slot_from_flag(bm, op, "outsplit", ELE_SPLIT, BM_ALL); + + BM_data_layer_free_n(bm, &bm->vdata, CD_SHAPEKEY, skey); +} |