diff options
author | Campbell Barton <ideasman42@gmail.com> | 2012-02-19 22:31:04 +0400 |
---|---|---|
committer | Campbell Barton <ideasman42@gmail.com> | 2012-02-19 22:31:04 +0400 |
commit | afc56a0b10b52d2c8bdfd44257624d776db72f79 (patch) | |
tree | 25d194e29b2708ac1143b3dde6dfbeec7ef1d876 /source/blender/bmesh/operators | |
parent | d6deca4e9d6bc7faff98644286571c7934324a9d (diff) |
copying bmesh dir on its own from bmesh branch
Diffstat (limited to 'source/blender/bmesh/operators')
-rw-r--r-- | source/blender/bmesh/operators/bmo_bevel.c | 881 | ||||
-rw-r--r-- | source/blender/bmesh/operators/bmo_connect.c | 414 | ||||
-rw-r--r-- | source/blender/bmesh/operators/bmo_create.c | 1412 | ||||
-rw-r--r-- | source/blender/bmesh/operators/bmo_dissolve.c | 559 | ||||
-rw-r--r-- | source/blender/bmesh/operators/bmo_dupe.c | 512 | ||||
-rw-r--r-- | source/blender/bmesh/operators/bmo_edgesplit.c | 426 | ||||
-rw-r--r-- | source/blender/bmesh/operators/bmo_extrude.c | 591 | ||||
-rw-r--r-- | source/blender/bmesh/operators/bmo_join_triangles.c | 373 | ||||
-rw-r--r-- | source/blender/bmesh/operators/bmo_mesh_conv.c | 906 | ||||
-rw-r--r-- | source/blender/bmesh/operators/bmo_mirror.c | 126 | ||||
-rw-r--r-- | source/blender/bmesh/operators/bmo_primitive.c | 734 | ||||
-rw-r--r-- | source/blender/bmesh/operators/bmo_removedoubles.c | 590 | ||||
-rw-r--r-- | source/blender/bmesh/operators/bmo_subdivide.c | 1104 | ||||
-rw-r--r-- | source/blender/bmesh/operators/bmo_subdivide.h | 66 | ||||
-rw-r--r-- | source/blender/bmesh/operators/bmo_triangulate.c | 219 | ||||
-rw-r--r-- | source/blender/bmesh/operators/bmo_utils.c | 1297 |
16 files changed, 10210 insertions, 0 deletions
diff --git a/source/blender/bmesh/operators/bmo_bevel.c b/source/blender/bmesh/operators/bmo_bevel.c new file mode 100644 index 00000000000..98e9a510126 --- /dev/null +++ b/source/blender/bmesh/operators/bmo_bevel.c @@ -0,0 +1,881 @@ +/* + * ***** 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_array.h" +#include "BLI_math.h" +#include "BLI_smallhash.h" + +#include "BKE_customdata.h" + +#include "bmesh.h" + +#define BEVEL_FLAG 1 +#define BEVEL_DEL 2 +#define FACE_NEW 4 +#define EDGE_OLD 8 +#define FACE_OLD 16 +#define VERT_OLD 32 +#define FACE_SPAN 64 +#define FACE_HOLE 128 + +typedef struct LoopTag { + BMVert *newv; +} LoopTag; + +typedef struct EdgeTag { + BMVert *newv1, *newv2; +} EdgeTag; + +static void calc_corner_co(BMesh *bm, BMLoop *l, const float fac, float r_co[3], + const short do_dist, const short do_even) +{ + float no[3], l_vec_prev[3], l_vec_next[3], l_co_prev[3], l_co[3], l_co_next[3], co_ofs[3]; + int is_concave; + + /* first get the prev/next verts */ + if (l->f->len > 2) { + copy_v3_v3(l_co_prev, l->prev->v->co); + copy_v3_v3(l_co, l->v->co); + copy_v3_v3(l_co_next, l->next->v->co); + + /* calculate norma */ + sub_v3_v3v3(l_vec_prev, l_co_prev, l_co); + sub_v3_v3v3(l_vec_next, l_co_next, l_co); + + cross_v3_v3v3(no, l_vec_prev, l_vec_next); + is_concave = dot_v3v3(no, l->f->no) > 0.0f; + } + else { + BMIter iter; + BMLoop *l2; + float up[3] = {0.0f, 0.0f, 1.0f}; + + copy_v3_v3(l_co_prev, l->prev->v->co); + copy_v3_v3(l_co, l->v->co); + + BM_ITER(l2, &iter, bm, BM_LOOPS_OF_VERT, l->v) { + if (l2->f != l->f) { + copy_v3_v3(l_co_next, BM_edge_other_vert(l2->e, l2->next->v)->co); + break; + } + } + + sub_v3_v3v3(l_vec_prev, l_co_prev, l_co); + sub_v3_v3v3(l_vec_next, l_co_next, l_co); + + cross_v3_v3v3(no, l_vec_prev, l_vec_next); + if (dot_v3v3(no, no) == 0.0f) { + no[0] = no[1] = 0.0f; no[2] = -1.0f; + } + + is_concave = dot_v3v3(no, up) < 0.0f; + } + + + /* now calculate the new location */ + if (do_dist) { /* treat 'fac' as distance */ + + normalize_v3(l_vec_prev); + normalize_v3(l_vec_next); + + add_v3_v3v3(co_ofs, l_vec_prev, l_vec_next); + if (UNLIKELY(normalize_v3(co_ofs) == 0.0f)) { /* edges form a straignt line */ + cross_v3_v3v3(co_ofs, l_vec_prev, l->f->no); + } + + if (do_even) { + negate_v3(l_vec_next); + mul_v3_fl(co_ofs, fac * shell_angle_to_dist(0.5f * angle_normalized_v3v3(l_vec_prev, l_vec_next))); + /* negate_v3(l_vec_next); */ /* no need unless we use again */ + } + else { + mul_v3_fl(co_ofs, fac); + } + } + else { /* treat as 'fac' as a factor (0 - 1) */ + + /* not strictly necessary, balance vectors + * so the longer edge doesn't skew the result, + * gives nicer, move even output. + * + * Use the minimum rather then the middle value so skinny faces don't flip along the short axis */ + float min_fac = minf(normalize_v3(l_vec_prev), normalize_v3(l_vec_next)); + float angle; + + if (do_even) { + negate_v3(l_vec_next); + angle = angle_normalized_v3v3(l_vec_prev, l_vec_next); + negate_v3(l_vec_next); /* no need unless we use again */ + } + else { + angle = 0.0f; + } + + mul_v3_fl(l_vec_prev, min_fac); + mul_v3_fl(l_vec_next, min_fac); + + add_v3_v3v3(co_ofs, l_vec_prev, l_vec_next); + + if (UNLIKELY(is_zero_v3(co_ofs))) { + cross_v3_v3v3(co_ofs, l_vec_prev, l->f->no); + normalize_v3(co_ofs); + mul_v3_fl(co_ofs, min_fac); + } + + /* done */ + if (do_even) { + mul_v3_fl(co_ofs, (fac * 0.5) * shell_angle_to_dist(0.5f * angle)); + } + else { + mul_v3_fl(co_ofs, fac * 0.5); + } + } + + /* apply delta vec */ + if (is_concave) + negate_v3(co_ofs); + + add_v3_v3v3(r_co, co_ofs, l->v->co); +} + + +#define ETAG_SET(e, v, nv) ( \ + (v) == (e)->v1 ? \ + (etags[BM_elem_index_get((e))].newv1 = (nv)) : \ + (etags[BM_elem_index_get((e))].newv2 = (nv)) \ + ) + +#define ETAG_GET(e, v) ( \ + (v) == (e)->v1 ? \ + (etags[BM_elem_index_get((e))].newv1) : \ + (etags[BM_elem_index_get((e))].newv2) \ + ) + +void bmesh_bevel_exec(BMesh *bm, BMOperator *op) +{ + BMOIter siter; + BMIter iter; + BMEdge *e; + BMVert *v; + BMFace **faces = NULL, *f; + LoopTag *tags = NULL, *tag; + EdgeTag *etags = NULL; + BMVert **verts = NULL; + BMEdge **edges = NULL; + BLI_array_declare(faces); + BLI_array_declare(tags); + BLI_array_declare(etags); + BLI_array_declare(verts); + BLI_array_declare(edges); + SmallHash hash; + float fac = BMO_slot_float_get(op, "percent"); + const short do_even = BMO_slot_int_get(op, "use_even"); + const short do_dist = BMO_slot_int_get(op, "use_dist"); + int i, li, has_elens, HasMDisps = CustomData_has_layer(&bm->ldata, CD_MDISPS); + + has_elens = CustomData_has_layer(&bm->edata, CD_PROP_FLT) && BMO_slot_int_get(op, "use_lengths"); + if (has_elens) { + li = BMO_slot_int_get(op, "lengthlayer"); + } + + BLI_smallhash_init(&hash); + + BMO_ITER(e, &siter, bm, op, "geom", BM_EDGE) { + BMO_elem_flag_enable(bm, e, BEVEL_FLAG|BEVEL_DEL); + BMO_elem_flag_enable(bm, e->v1, BEVEL_FLAG|BEVEL_DEL); + BMO_elem_flag_enable(bm, e->v2, BEVEL_FLAG|BEVEL_DEL); + + if (BM_edge_face_count(e) < 2) { + BMO_elem_flag_disable(bm, e, BEVEL_DEL); + BMO_elem_flag_disable(bm, e->v1, BEVEL_DEL); + BMO_elem_flag_disable(bm, e->v2, BEVEL_DEL); + } +#if 0 + if (BM_edge_face_count(e) == 0) { + BMVert *verts[2] = {e->v1, e->v2}; + BMEdge *edges[2] = {e, BM_edge_create(bm, e->v1, e->v2, e, 0)}; + + BMO_elem_flag_enable(bm, edges[1], BEVEL_FLAG); + BM_face_create(bm, verts, edges, 2, FALSE); + } +#endif + } + + BM_ITER(v, &iter, bm, BM_VERTS_OF_MESH, NULL) { + BMO_elem_flag_enable(bm, v, VERT_OLD); + } + +#if 0 + //a bit of cleaner code that, alas, doens't work. + /* build edge tag */ + BM_ITER(e, &iter, bm, BM_EDGES_OF_MESH, NULL) { + if (BMO_elem_flag_test(bm, e->v1, BEVEL_FLAG) || BMO_elem_flag_test(bm, e->v2, BEVEL_FLAG)) { + BMIter liter; + BMLoop *l; + + if (!BMO_elem_flag_test(bm, e, EDGE_OLD)) { + BM_elem_index_set(e, BLI_array_count(etags)); /* set_dirty! */ + BLI_array_growone(etags); + + BMO_elem_flag_enable(bm, e, EDGE_OLD); + } + + BM_ITER(l, &liter, bm, BM_LOOPS_OF_EDGE, e) { + BMLoop *l2; + BMIter liter2; + + if (BMO_elem_flag_test(bm, l->f, BEVEL_FLAG)) + continue; + + BM_ITER(l2, &liter2, bm, BM_LOOPS_OF_FACE, l->f) { + BM_elem_index_set(l2, BLI_array_count(tags)); /* set_loop */ + BLI_array_growone(tags); + + if (!BMO_elem_flag_test(bm, l2->e, EDGE_OLD)) { + BM_elem_index_set(l2->e, BLI_array_count(etags)); /* set_dirty! */ + BLI_array_growone(etags); + + BMO_elem_flag_enable(bm, l2->e, EDGE_OLD); + } + } + + BMO_elem_flag_enable(bm, l->f, BEVEL_FLAG); + BLI_array_append(faces, l->f); + } + } + else { + BM_elem_index_set(e, -1); /* set_dirty! */ + } + } +#endif + + /* create and assign looptag structure */ + BMO_ITER(e, &siter, bm, op, "geom", BM_EDGE) { + BMLoop *l; + BMIter liter; + + BMO_elem_flag_enable(bm, e->v1, BEVEL_FLAG|BEVEL_DEL); + BMO_elem_flag_enable(bm, e->v2, BEVEL_FLAG|BEVEL_DEL); + + if (BM_edge_face_count(e) < 2) { + BMO_elem_flag_disable(bm, e, BEVEL_DEL); + BMO_elem_flag_disable(bm, e->v1, BEVEL_DEL); + BMO_elem_flag_disable(bm, e->v2, BEVEL_DEL); + } + + if (!BLI_smallhash_haskey(&hash, (intptr_t)e)) { + BLI_array_growone(etags); + BM_elem_index_set(e, BLI_array_count(etags) - 1); /* set_dirty! */ + BLI_smallhash_insert(&hash, (intptr_t)e, NULL); + BMO_elem_flag_enable(bm, e, EDGE_OLD); + } + + /* find all faces surrounding e->v1 and, e->v2 */ + for (i = 0; i < 2; i++) { + BM_ITER(l, &liter, bm, BM_LOOPS_OF_VERT, i ? e->v2:e->v1) { + BMLoop *l2; + BMIter liter2; + + /* see if we've already processed this loop's fac */ + if (BLI_smallhash_haskey(&hash, (intptr_t)l->f)) + continue; + + /* create tags for all loops in l-> */ + BM_ITER(l2, &liter2, bm, BM_LOOPS_OF_FACE, l->f) { + BLI_array_growone(tags); + BM_elem_index_set(l2, BLI_array_count(tags) - 1); /* set_loop */ + + if (!BLI_smallhash_haskey(&hash, (intptr_t)l2->e)) { + BLI_array_growone(etags); + BM_elem_index_set(l2->e, BLI_array_count(etags) - 1); /* set_dirty! */ + BLI_smallhash_insert(&hash, (intptr_t)l2->e, NULL); + BMO_elem_flag_enable(bm, l2->e, EDGE_OLD); + } + } + + BLI_smallhash_insert(&hash, (intptr_t)l->f, NULL); + BMO_elem_flag_enable(bm, l->f, BEVEL_FLAG); + BLI_array_append(faces, l->f); + } + } + } + + bm->elem_index_dirty |= BM_EDGE; + + BM_ITER(v, &iter, bm, BM_VERTS_OF_MESH, NULL) { + BMIter eiter; + + if (!BMO_elem_flag_test(bm, v, BEVEL_FLAG)) + continue; + + BM_ITER(e, &eiter, bm, BM_EDGES_OF_VERT, v) { + if (!BMO_elem_flag_test(bm, e, BEVEL_FLAG) && !ETAG_GET(e, v)) { + BMVert *v2; + float co[3]; + + v2 = BM_edge_other_vert(e, v); + sub_v3_v3v3(co, v2->co, v->co); + if (has_elens) { + float elen = *(float *)CustomData_bmesh_get_n(&bm->edata, e->head.data, CD_PROP_FLT, li); + + normalize_v3(co); + mul_v3_fl(co, elen); + } + + mul_v3_fl(co, fac); + add_v3_v3(co, v->co); + + v2 = BM_vert_create(bm, co, v); + ETAG_SET(e, v, v2); + } + } + } + + for (i = 0; i < BLI_array_count(faces); i++) { + BMLoop *l; + BMIter liter; + + BMO_elem_flag_enable(bm, faces[i], FACE_OLD); + + BM_ITER(l, &liter, bm, BM_LOOPS_OF_FACE, faces[i]) { + float co[3]; + + if (BMO_elem_flag_test(bm, l->e, BEVEL_FLAG)) { + if (BMO_elem_flag_test(bm, l->prev->e, BEVEL_FLAG)) + { + tag = tags + BM_elem_index_get(l); + calc_corner_co(bm, l, fac, co, do_dist, do_even); + tag->newv = BM_vert_create(bm, co, l->v); + } + else { + tag = tags + BM_elem_index_get(l); + tag->newv = ETAG_GET(l->prev->e, l->v); + + if (!tag->newv) { + sub_v3_v3v3(co, l->prev->v->co, l->v->co); + if (has_elens) { + float elen = *(float *)CustomData_bmesh_get_n(&bm->edata, l->prev->e->head.data, + CD_PROP_FLT, li); + + normalize_v3(co); + mul_v3_fl(co, elen); + } + + mul_v3_fl(co, fac); + add_v3_v3(co, l->v->co); + + tag->newv = BM_vert_create(bm, co, l->v); + + ETAG_SET(l->prev->e, l->v, tag->newv); + } + } + } + else if (BMO_elem_flag_test(bm, l->v, BEVEL_FLAG)) { + tag = tags + BM_elem_index_get(l); + tag->newv = ETAG_GET(l->e, l->v); + + if (!tag->newv) { + sub_v3_v3v3(co, l->next->v->co, l->v->co); + if (has_elens) { + float elen = *(float *)CustomData_bmesh_get_n(&bm->edata, l->e->head.data, CD_PROP_FLT, li); + + normalize_v3(co); + mul_v3_fl(co, elen); + } + + mul_v3_fl(co, fac); + add_v3_v3(co, l->v->co); + + tag = tags + BM_elem_index_get(l); + tag->newv = BM_vert_create(bm, co, l->v); + + ETAG_SET(l->e, l->v, tag->newv); + } + } + else { + tag = tags + BM_elem_index_get(l); + tag->newv = l->v; + BMO_elem_flag_disable(bm, l->v, BEVEL_DEL); + } + } + } + + /* create new faces inset from original face */ + for (i = 0; i < BLI_array_count(faces); i++) { + BMLoop *l; + BMIter liter; + BMFace *f; + BMVert *lastv = NULL, *firstv = NULL; + + BMO_elem_flag_enable(bm, faces[i], BEVEL_DEL); + + BLI_array_empty(verts); + BLI_array_empty(edges); + + BM_ITER(l, &liter, bm, BM_LOOPS_OF_FACE, faces[i]) { + BMVert *v2; + + tag = tags + BM_elem_index_get(l); + BLI_array_append(verts, tag->newv); + + if (!firstv) + firstv = tag->newv; + + if (lastv) { + e = BM_edge_create(bm, lastv, tag->newv, l->e, TRUE); + BM_elem_attrs_copy(bm, bm, l->prev->e, e); + BLI_array_append(edges, e); + } + lastv = tag->newv; + + v2 = ETAG_GET(l->e, l->next->v); + + tag = &tags[BM_elem_index_get(l->next)]; + if (!BMO_elem_flag_test(bm, l->e, BEVEL_FLAG) && v2 && v2 != tag->newv) { + BLI_array_append(verts, v2); + + e = BM_edge_create(bm, lastv, v2, l->e, TRUE); + BM_elem_attrs_copy(bm, bm, l->e, e); + + BLI_array_append(edges, e); + lastv = v2; + } + } + + e = BM_edge_create(bm, firstv, lastv, BM_FACE_FIRST_LOOP(faces[i])->e, TRUE); + if (BM_FACE_FIRST_LOOP(faces[i])->prev->e != e) { + BM_elem_attrs_copy(bm, bm, BM_FACE_FIRST_LOOP(faces[i])->prev->e, e); + } + BLI_array_append(edges, e); + + f = BM_face_create_ngon(bm, verts[0], verts[1], edges, BLI_array_count(edges), FALSE); + if (!f) { + printf("%s: could not make face!\n", __func__); + continue; + } + + BMO_elem_flag_enable(bm, f, FACE_NEW); + } + + for (i = 0; i < BLI_array_count(faces); i++) { + BMLoop *l; + BMIter liter; + int j; + + /* create quad spans between split edge */ + BM_ITER(l, &liter, bm, BM_LOOPS_OF_FACE, faces[i]) { + BMVert *v1 = NULL, *v2 = NULL, *v3 = NULL, *v4 = NULL; + + if (!BMO_elem_flag_test(bm, l->e, BEVEL_FLAG)) + continue; + + v1 = tags[BM_elem_index_get(l)].newv; + v2 = tags[BM_elem_index_get(l->next)].newv; + if (l->radial_next != l) { + v3 = tags[BM_elem_index_get(l->radial_next)].newv; + if (l->radial_next->next->v == l->next->v) { + v4 = v3; + v3 = tags[BM_elem_index_get(l->radial_next->next)].newv; + } + else { + v4 = tags[BM_elem_index_get(l->radial_next->next)].newv; + } + } + else { + /* the loop is on a boundar */ + v3 = l->next->v; + v4 = l->v; + + for (j = 0; j < 2; j++) { + BMIter eiter; + BMVert *v = j ? v4 : v3; + + BM_ITER(e, &eiter, bm, BM_EDGES_OF_VERT, v) { + if (!BM_vert_in_edge(e, v3) || !BM_vert_in_edge(e, v4)) + continue; + + if (!BMO_elem_flag_test(bm, e, BEVEL_FLAG) && BMO_elem_flag_test(bm, e, EDGE_OLD)) { + BMVert *vv; + + vv = ETAG_GET(e, v); + if (!vv || BMO_elem_flag_test(bm, vv, BEVEL_FLAG)) + continue; + + if (j) + v1 = vv; + else + v2 = vv; + break; + } + } + } + + BMO_elem_flag_disable(bm, v3, BEVEL_DEL); + BMO_elem_flag_disable(bm, v4, BEVEL_DEL); + } + + if (v1 != v2 && v2 != v3 && v3 != v4) { + BMIter liter2; + BMLoop *l2, *l3; + BMEdge *e1, *e2; + float d1, d2, *d3; + + f = BM_face_create_quad_tri(bm, v4, v3, v2, v1, l->f, TRUE); + + e1 = BM_edge_exists(v4, v3); + e2 = BM_edge_exists(v2, v1); + BM_elem_attrs_copy(bm, bm, l->e, e1); + BM_elem_attrs_copy(bm, bm, l->e, e2); + + /* set edge lengths of cross edges as the average of the cross edges they're based o */ + if (has_elens) { + /* angle happens not to be used. why? - not sure it just isnt - campbell. + * leave this in incase we need to use it later */ +#if 0 + float ang; +#endif + e1 = BM_edge_exists(v1, v4); + e2 = BM_edge_exists(v2, v3); + + if (l->radial_next->v == l->v) { + l2 = l->radial_next->prev; + l3 = l->radial_next->next; + } + else { + l2 = l->radial_next->next; + l3 = l->radial_next->prev; + } + + d3 = CustomData_bmesh_get_n(&bm->edata, e1->head.data, CD_PROP_FLT, li); + d1 = *(float *)CustomData_bmesh_get_n(&bm->edata, l->prev->e->head.data, CD_PROP_FLT, li); + d2 = *(float *)CustomData_bmesh_get_n(&bm->edata, l2->e->head.data, CD_PROP_FLT, li); +#if 0 + ang = angle_v3v3v3(l->prev->v->co, l->v->co, BM_edge_other_vert(l2->e, l->v)->co); +#endif + *d3 = (d1 + d2) * 0.5f; + + d3 = CustomData_bmesh_get_n(&bm->edata, e2->head.data, CD_PROP_FLT, li); + d1 = *(float *)CustomData_bmesh_get_n(&bm->edata, l->next->e->head.data, CD_PROP_FLT, li); + d2 = *(float *)CustomData_bmesh_get_n(&bm->edata, l3->e->head.data, CD_PROP_FLT, li); +#if 0 + ang = angle_v3v3v3(BM_edge_other_vert(l->next->e, l->next->v)->co, l->next->v->co, + BM_edge_other_vert(l3->e, l->next->v)->co); +#endif + *d3 = (d1 + d2) * 0.5f; + } + + if (!f) { + fprintf(stderr, "%s: face index out of range! (bmesh internal error)\n", __func__); + continue; + } + + BMO_elem_flag_enable(bm, f, FACE_NEW|FACE_SPAN); + + /* un-tag edges in f for deletio */ + BM_ITER(l2, &liter2, bm, BM_LOOPS_OF_FACE, f) { + BMO_elem_flag_disable(bm, l2->e, BEVEL_DEL); + } + } + else { + f = NULL; + } + } + } + + /* fill in holes at vertices */ + BM_ITER(v, &iter, bm, BM_VERTS_OF_MESH, NULL) { + BMIter eiter; + BMVert *vv, *vstart = NULL, *lastv = NULL; + SmallHash tmphash; + int rad, insorig = 0, err = 0; + + BLI_smallhash_init(&tmphash); + + if (!BMO_elem_flag_test(bm, v, BEVEL_FLAG)) + continue; + + BLI_array_empty(verts); + BLI_array_empty(edges); + + BM_ITER(e, &eiter, bm, BM_EDGES_OF_VERT, v) { + BMIter liter; + BMVert *v1 = NULL, *v2 = NULL; + BMLoop *l; + + if (BM_edge_face_count(e) < 2) + insorig = 1; + + if (BM_elem_index_get(e) == -1) + continue; + + rad = 0; + BM_ITER(l, &liter, bm, BM_LOOPS_OF_EDGE, e) { + if (!BMO_elem_flag_test(bm, l->f, FACE_OLD)) + continue; + + rad++; + + tag = tags + BM_elem_index_get((l->v == v) ? l : l->next); + + if (!v1) + v1 = tag->newv; + else if (!v2) + v2 = tag->newv; + } + + if (rad < 2) + insorig = 1; + + if (!v1) + v1 = ETAG_GET(e, v); + if (!v2 || v1 == v2) + v2 = ETAG_GET(e, v); + + if (v1) { + if (!BLI_smallhash_haskey(&tmphash, (intptr_t)v1)) { + BLI_array_append(verts, v1); + BLI_smallhash_insert(&tmphash, (intptr_t)v1, NULL); + } + + if (v2 && v1 != v2 && !BLI_smallhash_haskey(&tmphash, (intptr_t)v2)) { + BLI_array_append(verts, v2); + BLI_smallhash_insert(&tmphash, (intptr_t)v2, NULL); + } + } + } + + if (!BLI_array_count(verts)) + continue; + + if (insorig) { + BLI_array_append(verts, v); + BLI_smallhash_insert(&tmphash, (intptr_t)v, NULL); + } + + /* find edges that exist between vertices in verts. this is basically + * a topological walk of the edges connecting them */ + vstart = vstart ? vstart : verts[0]; + vv = vstart; + do { + BM_ITER(e, &eiter, bm, BM_EDGES_OF_VERT, vv) { + BMVert *vv2 = BM_edge_other_vert(e, vv); + + if (vv2 != lastv && BLI_smallhash_haskey(&tmphash, (intptr_t)vv2)) { + /* if we've go over the same vert twice, break out of outer loop */ + if (BLI_smallhash_lookup(&tmphash, (intptr_t)vv2) != NULL) { + e = NULL; + err = 1; + break; + } + + /* use self pointer as ta */ + BLI_smallhash_remove(&tmphash, (intptr_t)vv2); + BLI_smallhash_insert(&tmphash, (intptr_t)vv2, vv2); + + lastv = vv; + BLI_array_append(edges, e); + vv = vv2; + break; + } + } + + if (e == NULL) { + break; + } + } while (vv != vstart); + + if (err) { + continue; + } + + /* there may not be a complete loop of edges, so start again and make + * final edge afterwards. in this case, the previous loop worked to + * find one of the two edges at the extremes. */ + if (vv != vstart) { + /* undo previous taggin */ + for (i = 0; i < BLI_array_count(verts); i++) { + BLI_smallhash_remove(&tmphash, (intptr_t)verts[i]); + BLI_smallhash_insert(&tmphash, (intptr_t)verts[i], NULL); + } + + vstart = vv; + lastv = NULL; + BLI_array_empty(edges); + do { + BM_ITER(e, &eiter, bm, BM_EDGES_OF_VERT, vv) { + BMVert *vv2 = BM_edge_other_vert(e, vv); + + if (vv2 != lastv && BLI_smallhash_haskey(&tmphash, (intptr_t)vv2)) { + /* if we've go over the same vert twice, break out of outer loo */ + if (BLI_smallhash_lookup(&tmphash, (intptr_t)vv2) != NULL) { + e = NULL; + err = 1; + break; + } + + /* use self pointer as ta */ + BLI_smallhash_remove(&tmphash, (intptr_t)vv2); + BLI_smallhash_insert(&tmphash, (intptr_t)vv2, vv2); + + lastv = vv; + BLI_array_append(edges, e); + vv = vv2; + break; + } + } + if (e == NULL) + break; + } while (vv != vstart); + + if (!err) { + e = BM_edge_create(bm, vv, vstart, NULL, TRUE); + BLI_array_append(edges, e); + } + } + + if (err) + continue; + + if (BLI_array_count(edges) >= 3) { + BMFace *f; + + if (BM_face_exists(bm, verts, BLI_array_count(verts), &f)) + continue; + + f = BM_face_create_ngon(bm, lastv, vstart, edges, BLI_array_count(edges), FALSE); + if (!f) { + fprintf(stderr, "%s: in bevel vert fill! (bmesh internal error)\n", __func__); + } + else { + BMO_elem_flag_enable(bm, f, FACE_NEW|FACE_HOLE); + } + } + BLI_smallhash_release(&tmphash); + } + + /* copy over customdat */ + for (i = 0; i < BLI_array_count(faces); i++) { + BMLoop *l; + BMIter liter; + BMFace *f = faces[i]; + + BM_ITER(l, &liter, bm, BM_LOOPS_OF_FACE, f) { + BMLoop *l2; + BMIter liter2; + + tag = tags + BM_elem_index_get(l); + if (!tag->newv) + continue; + + BM_ITER(l2, &liter2, bm, BM_LOOPS_OF_VERT, tag->newv) { + if (!BMO_elem_flag_test(bm, l2->f, FACE_NEW) || (l2->v != tag->newv && l2->v != l->v)) + continue; + + if (tag->newv != l->v || HasMDisps) { + BM_elem_attrs_copy(bm, bm, l->f, l2->f); + BM_loop_interp_from_face(bm, l2, l->f, TRUE, TRUE); + } + else { + BM_elem_attrs_copy(bm, bm, l->f, l2->f); + BM_elem_attrs_copy(bm, bm, l, l2); + } + + if (HasMDisps) { + BMLoop *l3; + BMIter liter3; + + BM_ITER(l3, &liter3, bm, BM_LOOPS_OF_FACE, l2->f) { + BM_loop_interp_multires(bm, l3, l->f); + } + } + } + } + } + + /* handle vertices along boundary edge */ + BM_ITER(v, &iter, bm, BM_VERTS_OF_MESH, NULL) { + if (BMO_elem_flag_test(bm, v, VERT_OLD) && + BMO_elem_flag_test(bm, v, BEVEL_FLAG) && + !BMO_elem_flag_test(bm, v, BEVEL_DEL)) + { + BMLoop *l; + BMLoop *lorig = NULL; + BMIter liter; + + BM_ITER(l, &liter, bm, BM_LOOPS_OF_VERT, v) { + // BMIter liter2; + // BMLoop *l2 = l->v == v ? l : l->next, *l3; + + if (BMO_elem_flag_test(bm, l->f, FACE_OLD)) { + lorig = l; + break; + } + } + + if (!lorig) + continue; + + BM_ITER(l, &liter, bm, BM_LOOPS_OF_VERT, v) { + BMLoop *l2 = l->v == v ? l : l->next; + + BM_elem_attrs_copy(bm, bm, lorig->f, l2->f); + BM_elem_attrs_copy(bm, bm, lorig, l2); + } + } + } +#if 0 + /* clean up any remaining 2-edged face */ + BM_ITER(f, &iter, bm, BM_FACES_OF_MESH, NULL) { + if (f->len == 2) { + BMFace *faces[2] = {f, BM_FACE_FIRST_LOOP(f)->radial_next->f}; + + if (faces[0] == faces[1]) + BM_face_kill(bm, f); + else + BM_faces_join(bm, faces, 2); + } + } +#endif + + BMO_op_callf(bm, "del geom=%fv context=%i", BEVEL_DEL, DEL_VERTS); + + /* clean up any edges that might not get properly delete */ + BM_ITER(e, &iter, bm, BM_EDGES_OF_MESH, NULL) { + if (BMO_elem_flag_test(bm, e, EDGE_OLD) && !e->l) + BMO_elem_flag_enable(bm, e, BEVEL_DEL); + } + + BMO_op_callf(bm, "del geom=%fe context=%i", BEVEL_DEL, DEL_EDGES); + BMO_op_callf(bm, "del geom=%ff context=%i", BEVEL_DEL, DEL_FACES); + + BLI_smallhash_release(&hash); + BLI_array_free(tags); + BLI_array_free(etags); + BLI_array_free(verts); + BLI_array_free(edges); + BLI_array_free(faces); + + BMO_slot_from_flag(bm, op, "face_spans", FACE_SPAN, BM_FACE); + BMO_slot_from_flag(bm, op, "face_holes", FACE_HOLE, BM_FACE); +} diff --git a/source/blender/bmesh/operators/bmo_connect.c b/source/blender/bmesh/operators/bmo_connect.c new file mode 100644 index 00000000000..6ab6d28008b --- /dev/null +++ b/source/blender/bmesh/operators/bmo_connect.c @@ -0,0 +1,414 @@ +/* + * ***** 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 "bmesh.h" + +#include "BLI_math.h" +#include "BLI_array.h" + +#define VERT_INPUT 1 +#define EDGE_OUT 1 +#define FACE_NEW 2 +#define EDGE_MARK 4 +#define EDGE_DONE 8 + +void connectverts_exec(BMesh *bm, BMOperator *op) +{ + BMIter iter, liter; + BMFace *f, *nf; + BMLoop **loops = NULL, *lastl = NULL; + BLI_array_declare(loops); + BMLoop *l, *nl; + BMVert **verts = NULL; + BLI_array_declare(verts); + int i; + + BMO_slot_buffer_flag_enable(bm, op, "verts", VERT_INPUT, BM_VERT); + + for (f = BM_iter_new(&iter, bm, BM_FACES_OF_MESH, NULL); f; f = BM_iter_step(&iter)) { + BLI_array_empty(loops); + BLI_array_empty(verts); + + if (BMO_elem_flag_test(bm, f, FACE_NEW)) continue; + + l = BM_iter_new(&liter, bm, BM_LOOPS_OF_FACE, f); + lastl = NULL; + for ( ; l; l = BM_iter_step(&liter)) { + if (BMO_elem_flag_test(bm, l->v, VERT_INPUT)) { + if (!lastl) { + lastl = l; + continue; + } + + if (lastl != l->prev && lastl != l->next) { + BLI_array_growone(loops); + loops[BLI_array_count(loops) - 1] = lastl; + + BLI_array_growone(loops); + loops[BLI_array_count(loops) - 1] = l; + + } + lastl = l; + } + } + + if (BLI_array_count(loops) == 0) continue; + + if (BLI_array_count(loops) > 2) { + BLI_array_growone(loops); + loops[BLI_array_count(loops) - 1] = loops[BLI_array_count(loops) - 2]; + + BLI_array_growone(loops); + loops[BLI_array_count(loops) - 1] = loops[0]; + } + + BM_face_legal_splits(bm, f, (BMLoop *(*)[2])loops, BLI_array_count(loops) / 2); + + for (i = 0; i < BLI_array_count(loops) / 2; i++) { + if (loops[i * 2] == NULL) continue; + + BLI_array_growone(verts); + verts[BLI_array_count(verts) - 1] = loops[i * 2]->v; + + BLI_array_growone(verts); + verts[BLI_array_count(verts) - 1] = loops[i * 2 + 1]->v; + } + + for (i = 0; i < BLI_array_count(verts) / 2; i++) { + nf = BM_face_split(bm, f, verts[i * 2], verts[i * 2 + 1], &nl, NULL); + f = nf; + + if (!nl || !nf) { + BMO_error_raise(bm, op, BMERR_CONNECTVERT_FAILED, NULL); + BLI_array_free(loops); + return; + } + BMO_elem_flag_enable(bm, nf, FACE_NEW); + BMO_elem_flag_enable(bm, nl->e, EDGE_OUT); + } + } + + BMO_slot_from_flag(bm, op, "edgeout", EDGE_OUT, BM_EDGE); + + BLI_array_free(loops); + BLI_array_free(verts); +} + +static BMVert *get_outer_vert(BMesh *bm, BMEdge *e) +{ + BMIter iter; + BMEdge *e2; + int i; + + i = 0; + BM_ITER(e2, &iter, bm, BM_EDGES_OF_VERT, e->v1) { + if (BMO_elem_flag_test(bm, e2, EDGE_MARK)) { + i++; + } + } + + return (i == 2) ? e->v2 : e->v1; +} + +/* Clamp x to the interval {0..len-1}, with wrap-around */ +static int clamp_index(const int x, const int len) +{ + return (x < 0) ? (len - (-x % len)) : (x % len); +} + +/* There probably is a better way to swap BLI_arrays, or if there + * isn't there should be... */ +#define ARRAY_SWAP(elemtype, arr1, arr2) \ + { \ + int i; \ + elemtype *arr_tmp = NULL; \ + BLI_array_declare(arr_tmp); \ + for (i = 0; i < BLI_array_count(arr1); i++) { \ + BLI_array_append(arr_tmp, arr1[i]); \ + } \ + BLI_array_empty(arr1); \ + for (i = 0; i < BLI_array_count(arr2); i++) { \ + BLI_array_append(arr1, arr2[i]); \ + } \ + BLI_array_empty(arr2); \ + for (i = 0; i < BLI_array_count(arr_tmp); i++) { \ + BLI_array_append(arr2, arr_tmp[i]); \ + } \ + BLI_array_free(arr_tmp); \ + } + +void bmesh_bridge_loops_exec(BMesh *bm, BMOperator *op) +{ + BMEdge **ee1 = NULL, **ee2 = NULL; + BMVert **vv1 = NULL, **vv2 = NULL; + BLI_array_declare(ee1); + BLI_array_declare(ee2); + BLI_array_declare(vv1); + BLI_array_declare(vv2); + BMOIter siter; + BMIter iter; + BMEdge *e, *nexte; + int c = 0, cl1 = 0, cl2 = 0; + + BMO_slot_buffer_flag_enable(bm, op, "edges", EDGE_MARK, BM_EDGE); + + BMO_ITER(e, &siter, bm, op, "edges", BM_EDGE) { + if (!BMO_elem_flag_test(bm, e, EDGE_DONE)) { + BMVert *v, *ov; + /* BMEdge *e2, *e3, *oe = e; */ /* UNUSED */ + BMEdge *e2, *e3; + + if (c > 2) { + BMO_error_raise(bm, op, BMERR_INVALID_SELECTION, "Select only two edge loops"); + goto cleanup; + } + + e2 = e; + v = e->v1; + do { + v = BM_edge_other_vert(e2, v); + nexte = NULL; + BM_ITER(e3, &iter, bm, BM_EDGES_OF_VERT, v) { + if (e3 != e2 && BMO_elem_flag_test(bm, e3, EDGE_MARK)) { + if (nexte == NULL) { + nexte = e3; + } + else { + /* edges do not form a loop: there is a disk + * with more than two marked edges. */ + BMO_error_raise(bm, op, BMERR_INVALID_SELECTION, + "Selection must only contain edges from two edge loops"); + goto cleanup; + } + } + } + + if (nexte) + e2 = nexte; + } while (nexte && e2 != e); + + if (!e2) + e2 = e; + + e = e2; + ov = v; + do { + if (c == 0) { + BLI_array_append(ee1, e2); + BLI_array_append(vv1, v); + } + else { + BLI_array_append(ee2, e2); + BLI_array_append(vv2, v); + } + + BMO_elem_flag_enable(bm, e2, EDGE_DONE); + + v = BM_edge_other_vert(e2, v); + BM_ITER(e3, &iter, bm, BM_EDGES_OF_VERT, v) { + if (e3 != e2 && BMO_elem_flag_test(bm, e3, EDGE_MARK) && !BMO_elem_flag_test(bm, e3, EDGE_DONE)) { + break; + } + } + if (e3) + e2 = e3; + } while (e3 && e2 != e); + + if (v && !e3) { + if (c == 0) { + if (BLI_array_count(vv1) && v == vv1[BLI_array_count(vv1) - 1]) { + printf("%s: internal state waning *TODO DESCRIPTION!*\n", __func__); + } + BLI_array_append(vv1, v); + } + else { + BLI_array_append(vv2, v); + } + } + + /* test for connected loops, and set cl1 or cl2 if so */ + if (v == ov) { + if (c == 0) { + cl1 = 1; + } + else { + cl2 = 1; + } + } + + c++; + } + } + + if (ee1 && ee2) { + int i, j; + BMVert *v1, *v2, *v3, *v4; + int starti = 0, dir1 = 1, wdir = 0, lenv1, lenv2; + + /* Simplify code below by avoiding the (!cl1 && cl2) case */ + if (!cl1 && cl2) { + SWAP(int, cl1, cl2); + ARRAY_SWAP(BMVert *, vv1, vv2); + ARRAY_SWAP(BMEdge *, ee1, ee2); + } + + lenv1 = lenv2 = BLI_array_count(vv1); + + /* Below code assumes vv1/vv2 each have at least two verts. should always be + * a safe assumption, since ee1/ee2 are non-empty and an edge has two verts. */ + BLI_assert((lenv1 > 1) && (lenv2 > 1)); + + /* BMESH_TODO: Would be nice to handle cases where the edge loops + * have different edge counts by generating triangles & quads for + * the bridge instead of quads only. */ + if (BLI_array_count(ee1) != BLI_array_count(ee2)) { + BMO_error_raise(bm, op, BMERR_INVALID_SELECTION, + "Selected loops must have equal edge counts"); + goto cleanup; + } + + j = 0; + if (vv1[0] == vv1[lenv1 - 1]) { + lenv1--; + } + if (vv2[0] == vv2[lenv2 - 1]) { + lenv2--; + } + + /* Find starting point and winding direction for two unclosed loops */ + if (!cl1 && !cl2) { + /* First point of loop 1 */ + v1 = get_outer_vert(bm, ee1[0]); + /* Last point of loop 1 */ + v2 = get_outer_vert(bm, ee1[clamp_index(-1, BLI_array_count(ee1))]); + /* First point of loop 2 */ + v3 = get_outer_vert(bm, ee2[0]); + /* Last point of loop 2 */ + v4 = get_outer_vert(bm, ee2[clamp_index(-1, BLI_array_count(ee2))]); + + /* If v1 is a better match for v4 than v3, AND v2 is a better match + * for v3 than v4, the loops are in opposite directions, so reverse + * the order of reads from vv1. We can avoid sqrt for comparison */ + if (len_squared_v3v3(v1->co, v3->co) > len_squared_v3v3(v1->co, v4->co) && + len_squared_v3v3(v2->co, v4->co) > len_squared_v3v3(v2->co, v3->co)) + { + dir1 = -1; + starti = clamp_index(-1, lenv1); + } + } + + /* Find the shortest distance from a vert in vv1 to vv2[0]. Use that + * vertex in vv1 as a starting point in the first loop, while starting + * from vv2[0] in the second loop. This is a simplistic attempt to get + * a better edge-to-edge match between the two loops. */ + if (cl1) { + int previ, nexti; + float min = 1e32; + + /* BMESH_TODO: Would be nice to do a more thorough analysis of all + * the vertices in both loops to find a more accurate match for the + * starting point and winding direction of the bridge generation. */ + + for (i = 0; i < BLI_array_count(vv1); i++) { + if (len_v3v3(vv1[i]->co, vv2[0]->co) < min) { + min = len_v3v3(vv1[i]->co, vv2[0]->co); + starti = i; + } + } + + /* Reverse iteration order for the first loop if the distance of + * the (starti - 1) vert from vv1 is a better match for vv2[1] than + * the (starti + 1) vert. + * + * This is not always going to be right, but it will work better in + * the average case. + */ + previ = clamp_index(starti - 1, lenv1); + nexti = clamp_index(starti + 1, lenv1); + + /* avoid sqrt for comparison */ + if (len_squared_v3v3(vv1[nexti]->co, vv2[1]->co) > len_squared_v3v3(vv1[previ]->co, vv2[1]->co)) { + /* reverse direction for reading vv1 (1 is forward, -1 is backward) */ + dir1 = -1; + } + } + + /* Vert rough attempt to determine proper winding for the bridge quads: + * just uses the first loop it finds for any of the edges of ee2 or ee1 */ + if (wdir == 0) { + for (i = 0; i < BLI_array_count(ee2); i++) { + if (ee2[i]->l) { + wdir = (ee2[i]->l->v == vv2[i]) ? (-1) : (1); + break; + } + } + } + if (wdir == 0) { + for (i = 0; i < BLI_array_count(ee1); i++) { + j = clamp_index((i * dir1) + starti, BLI_array_count(ee1)); + if (ee1[j]->l && ee2[j]->l) { + wdir = (ee2[j]->l->v == vv2[j]) ? (1) : (-1); + break; + } + } + } + + /* Generate the bridge quads */ + for (i = 0; i < BLI_array_count(ee1) && i < BLI_array_count(ee2); i++) { + BMFace *f; + int i1, i1next, i2, i2next; + + i1 = clamp_index(i * dir1 + starti, lenv1); + i1next = clamp_index((i + 1) * dir1 + starti, lenv1); + i2 = i; + i2next = clamp_index(i + 1, lenv2); + + if (vv1[i1] == vv1[i1next]) { + continue; + } + + if (wdir < 0) { + SWAP(int, i1, i1next); + SWAP(int, i2, i2next); + } + + f = BM_face_create_quad_tri(bm, + vv1[i1], + vv2[i2], + vv2[i2next], + vv1[i1next], + NULL, TRUE); + if (!f || f->len != 4) { + fprintf(stderr, "%s: in bridge! (bmesh internal error)\n", __func__); + } + } + } + +cleanup: + BLI_array_free(ee1); + BLI_array_free(ee2); + BLI_array_free(vv1); + BLI_array_free(vv2); +} diff --git a/source/blender/bmesh/operators/bmo_create.c b/source/blender/bmesh/operators/bmo_create.c new file mode 100644 index 00000000000..d90e5c36c80 --- /dev/null +++ b/source/blender/bmesh/operators/bmo_create.c @@ -0,0 +1,1412 @@ +/* + * ***** 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_heap.h" +#include "BLI_listbase.h" +#include "BLI_math.h" +#include "BLI_array.h" +#include "BLI_smallhash.h" +#include "BLI_rand.h" + +#include "bmesh.h" + + +#define EDGE_MARK 1 +#define EDGE_VIS 2 + +#define FACE_NEW 1 + +#define ELE_NEW 1 +#define ELE_OUT 2 +#define ELE_ORIG 4 + +#define FACE_IGNORE 16 + +typedef struct EPathNode { + struct EPathNode *next, *prev; + BMVert *v; + BMEdge *e; + BMEdge *cure; +} EPathNode; + +typedef struct EPath { + ListBase nodes; + float weight; + int group; +} EPath; + +typedef struct PathBase { + BLI_mempool *nodepool, *pathpool; +} PathBase; + +typedef struct EdgeData { + int tag; + int ftag; + BMDiskLink v1_disk_link, v2_disk_link; +} EdgeData; + +typedef struct VertData { + BMEdge *e; + float no[3], offco[3], sco[3]; /* offco is vertex coordinate slightly offset randoml */ + int tag; +} VertData; + +static int count_edge_faces(BMesh *bm, BMEdge *e); + +/**** rotation system code * */ + +#define RS_GET_EDGE_LINK(e, v, e_data) ( \ + (v) == ((BMEdge *)(e))->v1 ? \ + &(((EdgeData *)(e_data))->v1_disk_link) : \ + &(((EdgeData *)(e_data))->v2_disk_link) \ + ) + + +static int rotsys_append_edge(struct BMEdge *e, struct BMVert *v, + EdgeData *edata, VertData *vdata) +{ + EdgeData *ed = &edata[BM_elem_index_get(e)]; + VertData *vd = &vdata[BM_elem_index_get(v)]; + + if (!vd->e) { + Link *e1 = (Link *)RS_GET_EDGE_LINK(e, v, ed); + + vd->e = e; + e1->next = e1->prev = (Link *)e; + } + else { + BMDiskLink *dl1, *dl2, *dl3; + EdgeData *ved = &edata[BM_elem_index_get(vd->e)]; + + dl1 = RS_GET_EDGE_LINK(e, v, ed); + dl2 = RS_GET_EDGE_LINK(vd->e, v, ved); + dl3 = dl2->prev ? RS_GET_EDGE_LINK(dl2->prev, v, &edata[BM_elem_index_get(dl2->prev)]) : NULL; + + dl1->next = vd->e; + dl1->prev = dl2->prev; + + dl2->prev = e; + if (dl3) { + dl3->next = e; + } + } + + return TRUE; +} + +static void UNUSED_FUNCTION(rotsys_remove_edge)(struct BMEdge *e, struct BMVert *v, + EdgeData *edata, VertData *vdata) +{ + EdgeData *ed = edata + BM_elem_index_get(e); + VertData *vd = vdata + BM_elem_index_get(v); + BMDiskLink *e1, *e2; + + e1 = RS_GET_EDGE_LINK(e, v, ed); + if (e1->prev) { + e2 = RS_GET_EDGE_LINK(e1->prev, v, ed); + e2->next = e1->next; + } + + if (e1->next) { + e2 = RS_GET_EDGE_LINK(e1->next, v, ed); + e2->prev = e1->prev; + } + + if (vd->e == e) + vd->e = (e != (BMEdge *)e1->next) ? (BMEdge *)e1->next : NULL; + + e1->next = e1->prev = NULL; +} + +static struct BMEdge *rotsys_nextedge(struct BMEdge *e, struct BMVert *v, + EdgeData *edata, VertData *UNUSED(vdata)) +{ + if (v == e->v1) + return edata[BM_elem_index_get(e)].v1_disk_link.next; + if (v == e->v2) + return edata[BM_elem_index_get(e)].v2_disk_link.next; + return NULL; +} + +static BMEdge *rotsys_prevedge(BMEdge *e, BMVert *v, + EdgeData *edata, VertData *UNUSED(vdata)) +{ + if (v == e->v1) + return edata[BM_elem_index_get(e)].v1_disk_link.prev; + if (v == e->v2) + return edata[BM_elem_index_get(e)].v2_disk_link.prev; + return NULL; +} + +static void rotsys_reverse(struct BMEdge *UNUSED(e), struct BMVert *v, EdgeData *edata, VertData *vdata) +{ + BMEdge **edges = NULL; + BMEdge *e_first; + BMEdge *e; + BLI_array_staticdeclare(edges, BM_NGON_STACK_SIZE); + int i, totedge; + + e = e_first = vdata[BM_elem_index_get(v)].e; + do { + BLI_array_append(edges, e); + e = rotsys_nextedge(e, v, edata, vdata); + } while (e != e_first); + + totedge = BLI_array_count(edges); + for (i = 0; i < totedge / 2; i++) { + SWAP(BMEdge *, edges[i], edges[totedge - 1 - i]); + } + + vdata[BM_elem_index_get(v)].e = NULL; + for (i = 0; i < totedge; i++) { + rotsys_append_edge(edges[i], v, edata, vdata); + } + + BLI_array_free(edges); +} + +static int UNUSED_FUNCTION(rotsys_count)(struct BMVert *v, EdgeData *edata, VertData *vdata) +{ + BMEdge *e = vdata[BM_elem_index_get(v)].e; + int i = 0; + + if (!e) + return 0; + + do { + if (!e) + return 0; + e = rotsys_nextedge(e, v, edata, vdata); + + if (i >= (1 << 20)) { + printf("bmesh error: infinite loop in disk cycle!\n"); + return 0; + } + + i += 1; + } while (e != vdata[BM_elem_index_get(v)].e); + + return i; +} + +static int UNUSED_FUNCTION(rotsys_fill_faces)(BMesh *bm, EdgeData *edata, VertData *vdata) +{ + BMIter iter; + BMEdge *e, **edges = NULL; + BLI_array_declare(edges); + BMVert *v, **verts = NULL; + BMFace *f; + BLI_array_declare(verts); + SmallHash visithash, *hash = &visithash; + int i; + + BM_ITER(e, &iter, bm, BM_EDGES_OF_MESH, NULL) { + BMEdge *e2, *starte; + BMVert *startv; + int rad, ok; + + rad = count_edge_faces(bm, e); + + if (rad < 2) + starte = e; + else + continue; + + /* do two passes, going forward then backwar */ + for (i = 0; i < 2; i++) { + BLI_smallhash_init(hash); + + BLI_array_empty(verts); + BLI_array_empty(edges); + + startv = v = starte->v1; + e2 = starte; + ok = 1; + if (!v || !e2) + continue; + + do { + if (BLI_smallhash_haskey(hash, (intptr_t)e2) || + BLI_smallhash_haskey(hash, (intptr_t)v)) + { + ok = 0; + break; + } + + BLI_array_append(verts, v); + BLI_array_append(edges, e2); + + BLI_smallhash_insert(hash, (intptr_t)e2, NULL); + + v = BM_edge_other_vert(e2, v); + e2 = i ? rotsys_prevedge(e2, v, edata, vdata) : rotsys_nextedge(e2, v, edata, vdata); + } while (e2 != starte && v != startv); + + BLI_smallhash_release(hash); + + if (!ok || BLI_array_count(edges) < 3) + continue; + + f = BM_face_create_ngon(bm, verts[0], verts[1], edges, BLI_array_count(edges), TRUE); + if (!f) + continue; + } + } + + return 0; +} + +static void rotsys_make_consistent(BMesh *bm, EdgeData *edata, VertData *vdata) +{ + BMIter iter; + BMEdge *e; + BMVert *v, **stack = NULL; + BLI_array_declare(stack); + int i; + + for (i = 0; i < bm->totvert; i++) { + vdata[i].tag = 0; + } + + while (1) { + VertData *vd; + BMVert *startv = NULL; + float dis; + + v = BM_iter_new(&iter, bm, BM_VERTS_OF_MESH, NULL); + for (i = 0; i < bm->totvert; i++, BM_iter_step(&iter)) { + vd = vdata + BM_elem_index_get(v); + + if (vd->tag) + continue; + + if (!startv || dot_v3v3(vd->offco, vd->offco) > dis) { + dis = dot_v3v3(vd->offco, vd->offco); + startv = v; + } + } + + if (!startv) + break; + + vd = vdata + BM_elem_index_get(startv); + + BLI_array_empty(stack); + BLI_array_append(stack, startv); + + vd->tag = 1; + + while (BLI_array_count(stack)) { + v = BLI_array_pop(stack); + vd = vdata + BM_elem_index_get(v); + + if (!vd->e) + continue; + + e = vd->e; + do { + BMVert *v2 = BM_edge_other_vert(e, v); + VertData *vd2 = vdata + BM_elem_index_get(v2); + + if (dot_v3v3(vd->no, vd2->no) < 0.0f + FLT_EPSILON * 2) { + rotsys_reverse(e, v2, edata, vdata); + mul_v3_fl(vd2->no, -1.0f); + } + + if (!vd2->tag) { + BLI_array_append(stack, v2); + vd2->tag = 1; + } + + e = rotsys_nextedge(e, v, edata, vdata); + } while (e != vd->e); + } + } + + BLI_array_free(stack); +} + +static void init_rotsys(BMesh *bm, EdgeData *edata, VertData *vdata) +{ + BMIter iter; + BMEdge *e; + BMEdge **edges = NULL; + BLI_array_staticdeclare(edges, BM_NGON_STACK_SIZE); + BMVert *v; + /* BMVert **verts = NULL; */ + /* BLI_array_staticdeclare(verts, BM_NGON_STACK_SIZE); */ /* UNUSE */ + int i; + +#define SIGN(n) ((n)<0.0f) + + BM_ITER(v, &iter, bm, BM_VERTS_OF_MESH, NULL) { + BMIter eiter; + float no[3], cent[3]; + int j, k = 0, totedge = 0; + + if (BM_elem_index_get(v) == -1) + continue; + + BLI_array_empty(edges); + + BM_ITER(e, &eiter, bm, BM_EDGES_OF_VERT, v) { + if (BMO_elem_flag_test(bm, e, EDGE_MARK)) { + BLI_array_append(edges, e); + totedge++; + } + } + + copy_v3_v3(cent, v->co); + + zero_v3(no); + for (i = 0; i < totedge; i++) { + BMEdge *e1, *e2; + float cno[3], vec1[3], vec2[3]; + + e1 = edges[i]; + e2 = edges[(i + 1) % totedge]; + + sub_v3_v3v3(vec1, (BM_edge_other_vert(e1, v))->co, v->co); + sub_v3_v3v3(vec2, (BM_edge_other_vert(e2, v))->co, v->co); + + cross_v3_v3v3(cno, vec1, vec2); + normalize_v3(cno); + + if (i && dot_v3v3(cno, no) < 0.0f + FLT_EPSILON * 10) + mul_v3_fl(cno, -1.0f); + + add_v3_v3(no, cno); + normalize_v3(no); + } + + /* generate plane-flattened coordinate */ + for (i = 0; i < totedge; i++) { + BMEdge *e1; + BMVert *v2; + float cvec[3], vec1[3]; + + e1 = edges[i]; + v2 = BM_edge_other_vert(e1, v); + + sub_v3_v3v3(vec1, v2->co, v->co); + + cross_v3_v3v3(cvec, vec1, no); + cross_v3_v3v3(vec1, cvec, no); + normalize_v3(vec1); + + mul_v3_fl(vec1, len_v3v3(v2->co, v->co)); + add_v3_v3(vec1, v->co); + + copy_v3_v3(vdata[BM_elem_index_get(v2)].sco, vec1); + } + + BLI_srandom(0); + + /* first, ensure no 0 or 180 angles between adjacent + * (and that adjacent's adjacent) edges */ + for (i = 0, k = 0; i < totedge; i++) { + BMEdge *e1, *e2, *e3 = NULL; + BMVert *v1, *v2, *v3; + VertData *vd1, *vd2, *vd3; + float vec1[3], vec2[3], vec3[3], size; + int s1, s2, s3; + + if (totedge < 3) + continue; + + e1 = edges[(i + totedge - 1) % totedge]; + e2 = edges[i]; + e3 = edges[(i + 1) % totedge]; + + v1 = BM_edge_other_vert(e1, v); + v2 = BM_edge_other_vert(e2, v); + v3 = BM_edge_other_vert(e3, v); + + vd1 = vdata + BM_elem_index_get(v1); + vd2 = vdata + BM_elem_index_get(v2); + vd3 = vdata + BM_elem_index_get(v3); + + sub_v3_v3v3(vec1, vd1->sco, cent); + sub_v3_v3v3(vec2, vd2->sco, cent); + sub_v3_v3v3(vec3, vd3->sco, cent); + + size = (len_v3(vec1) + len_v3(vec3)) * 0.01f; + normalize_v3(vec1); normalize_v3(vec2); normalize_v3(vec3); + +#ifdef STRAIGHT +#undef STRAIGHT +#endif +#define STRAIGHT(vec11, vec22) (fabsf(dot_v3v3((vec11), (vec22))) > 1.0f - ((float)FLT_EPSILON * 1000.0f)) + + s1 = STRAIGHT(vec1, vec2); s2 = STRAIGHT(vec2, vec3); s3 = STRAIGHT(vec1, vec3); + + if (s1 || s2 || s3) { + copy_v3_v3(cent, v->co); + + for (j = 0; j < 3; j++) { + float fac = (BLI_frand() - 0.5f)*size; + cent[j] += fac; + } + + if (k < 2000) { + i = 0; + k++; + continue; + } + else { + k++; + continue; + } + + } + } + + copy_v3_v3(vdata[BM_elem_index_get(v)].offco, cent); + //copy_v3_v3(v->co, cent); + + /* now, sort edges so the triangle fan of all edges + * has a consistent normal. this is the same as + * sorting by polar coordinates along a group normal */ + for (j = 0; j < totedge; j++) { + for (i = 0; i < totedge; i++) { + BMEdge *e1, *e2, *e3 = NULL; + BMVert *v1, *v2, *v3; + VertData *vd1, *vd2, *vd3; + float vec1[3], vec2[3], vec3[3], n1[3], n2[3], n3[3]; + int s1, s2, s3; + + e1 = edges[(i + totedge - 1) % totedge]; + e2 = edges[i]; + e3 = edges[(i + 1) % totedge]; + + v1 = BM_edge_other_vert(e1, v); + v2 = BM_edge_other_vert(e2, v); + v3 = BM_edge_other_vert(e3, v); + + vd1 = vdata + BM_elem_index_get(v1); + vd2 = vdata + BM_elem_index_get(v2); + vd3 = vdata + BM_elem_index_get(v3); + + sub_v3_v3v3(vec1, vd1->sco, cent); + sub_v3_v3v3(vec2, vd2->sco, cent); + sub_v3_v3v3(vec3, vd3->sco, cent); + + cross_v3_v3v3(n1, vec1, vec2); + cross_v3_v3v3(n2, vec2, vec3); + cross_v3_v3v3(n3, vec1, vec3); + + /* Other way to determine if two vectors approach are (nearly) parallel: the + * cross product of the two vectors will approach zero */ + s1 = (dot_v3v3(n1, n1) < (0.0f + FLT_EPSILON * 10)); + s2 = (dot_v3v3(n2, n2) < (0.0f + FLT_EPSILON * 10)); + s3 = (totedge < 3) ? 0 : (dot_v3v3(n3, n3) < (0.0f + FLT_EPSILON * 10)); + + normalize_v3(n1); normalize_v3(n2); normalize_v3(n3); + + if (s1 || s2 || s3) { + fprintf(stderr, "%s: s1: %d, s2: %d, s3: %dx (bmesh internal error)\n", __func__, s1, s2, s3); + } + if (dot_v3v3(n1, n2) < 0.0f) { + if (dot_v3v3(n1, n3) >= 0.0f + FLT_EPSILON * 10) { + SWAP(BMEdge *, edges[i], edges[(i + 1) % totedge]); + } + else { + SWAP(BMEdge *, edges[(i + totedge - 1) % totedge], edges[(i + 1) % totedge]); + SWAP(BMEdge *, edges[i], edges[(i + 1) % totedge]); + } + } + } + } + +#undef STRAIGHT + + zero_v3(no); + + /* yay, edges are sorted */ + for (i = 0; i < totedge; i++) { + BMEdge *e1 = edges[i], *e2 = edges[(i + 1) % totedge]; + float eno[3]; + + normal_tri_v3(eno, BM_edge_other_vert(e1, v)->co, v->co, BM_edge_other_vert(e2, v)->co); + add_v3_v3(no, eno); + + rotsys_append_edge(edges[i], v, edata, vdata); + } + + normalize_v3(no); + copy_v3_v3(vdata[BM_elem_index_get(v)].no, no); + } + + /* now, make sure rotation system is topologically consistent + * (e.g. vert normals consistently point either inside or outside) */ + rotsys_make_consistent(bm, edata, vdata); + + //rotsys_fill_faces(bm, edata, vdata); + +#if 0 + /* create visualizing geometr */ + BMVert *lastv; + BM_ITER(v, &iter, bm, BM_VERTS_OF_MESH, NULL) { + BMVert *v2; + BMFace *f; + int totedge = BM_vert_edge_count(v); + + if (BM_elem_index_get(v) == -1) + continue; + + //cv = BM_vert_create(bm, cent, v); + //BM_elem_index_set(cv, -1); /* set_dirty! */ + i = 0; + e = vdata[BM_elem_index_get(v)].e; + lastv = NULL; + do { + BMEdge *e2; + BMVert *v2; + float f = ((float)i / (float)totedge) * 0.35 + 0.05; + float co[3]; + + if (!e) + break; + + if (!BM_edge_other_vert(e, v)) + continue; + + sub_v3_v3v3(co, (BM_edge_other_vert(e, v))->co, vdata[BM_elem_index_get(v)].offco); + mul_v3_fl(co, f); + add_v3_v3(co, vdata[BM_elem_index_get(v)].offco); + + v2 = BM_vert_create(bm, co, NULL); + BM_elem_index_set(v2, -1); /* set_dirty! */ + //BM_edge_create(bm, cv, v2, NULL, FALSE); + + BM_elem_select_set(bm, v2, TRUE); + if (lastv) { + e2 = BM_edge_create(bm, lastv, v2, NULL, FALSE); + BM_elem_select_set(bm, e2, TRUE); + } + + lastv = v2; + + e = rotsys_nextedge(e, v, edata, vdata); + i++; + } while (e != vdata[BM_elem_index_get(v)].e); + } +#endif + + BLI_array_free(edges); +} + +static PathBase *edge_pathbase_new(void) +{ + PathBase *pb = MEM_callocN(sizeof(PathBase), "PathBase"); + + pb->nodepool = BLI_mempool_create(sizeof(EPathNode), 1, 512, TRUE, FALSE); + pb->pathpool = BLI_mempool_create(sizeof(EPath), 1, 512, TRUE, FALSE); + + return pb; +} + +static void edge_pathbase_free(PathBase *pathbase) +{ + BLI_mempool_destroy(pathbase->nodepool); + BLI_mempool_destroy(pathbase->pathpool); + MEM_freeN(pathbase); +} + +static EPath *edge_copy_add_path(PathBase *pb, EPath *path, BMVert *appendv, BMEdge *e) +{ + EPath *path2; + EPathNode *node, *node2; + + path2 = BLI_mempool_alloc(pb->pathpool); + path2->nodes.first = path2->nodes.last = NULL; + path2->weight = 0.0f; + path2->group = path->group; + + for (node = path->nodes.first; node; node = node->next) { + node2 = BLI_mempool_alloc(pb->nodepool); + *node2 = *node; + BLI_addtail(&path2->nodes, node2); + } + + node2 = BLI_mempool_alloc(pb->nodepool); + node2->v = appendv; + node2->e = e; + node2->cure = NULL; + + BLI_addtail(&path2->nodes, node2); + + return path2; +} + +static EPath *edge_path_new(PathBase *pb, BMVert *start, BMEdge *starte) +{ + EPath *path; + EPathNode *node; + + path = BLI_mempool_alloc(pb->pathpool); + node = BLI_mempool_alloc(pb->nodepool); + + path->nodes.first = path->nodes.last = NULL; + + node->v = start; + node->e = starte; + node->cure = NULL; + + BLI_addtail(&path->nodes, node); + path->weight = 0.0f; + + return path; +} + +static float edge_weight_path(EPath *path, EdgeData *edata, VertData *UNUSED(vdata)) +{ + EPathNode *node, *first = path->nodes.first; + float w = 0.0; + + for (node = path->nodes.first; node; node = node->next) { + if (node->e && node != path->nodes.first) { + w += edata[BM_elem_index_get(node->e)].ftag; + if (node->prev) { + /* BMESH_TOD */ + (void)first; + //w += len_v3v3(node->v->co, first->e->v1->co) * 0.0001f; + //w += len_v3v3(node->v->co, first->e->v2->co) * 0.0001f; + } + } + + w += 1.0f; + } + + return w; +} + + +static void edge_free_path(PathBase *pathbase, EPath *path) +{ + EPathNode *node, *next; + + for (node = path->nodes.first; node; node = next) { + next = node->next; + BLI_mempool_free(pathbase->nodepool, node); + } + + BLI_mempool_free(pathbase->pathpool, path); +} + +static EPath *edge_find_shortest_path(BMesh *bm, BMOperator *op, BMEdge *edge, EdgeData *edata, + VertData *vdata, PathBase *pathbase, int group) +{ + BMEdge *e; + GHash *gh = BLI_ghash_new(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp, "createops find shortest path"); + BMVert *v1, *v2; + BMVert **verts = NULL; + BLI_array_staticdeclare(verts, 1024); + Heap *heap = BLI_heap_new(); + EPath *path = NULL, *path2; + BMVert *startv; + BMVert *endv; + EPathNode *node; + int i, use_restrict = BMO_slot_int_get(op, "use_restrict"); + + startv = edata[BM_elem_index_get(edge)].ftag ? edge->v2 : edge->v1; + endv = edata[BM_elem_index_get(edge)].ftag ? edge->v1 : edge->v2; + + path = edge_path_new(pathbase, startv, edge); + BLI_ghash_insert(gh, startv, NULL); + BLI_heap_insert(heap, path->weight, path); + path->group = group; + + while (BLI_heap_size(heap)) { + VertData *vd; + EPathNode *last; + BMFace *f = NULL; + + path = BLI_heap_popmin(heap); + last = path->nodes.last; + v1 = last->v; + + if (v1 == endv) { + /* make sure this path loop doesn't already exist */ + i = 0; + BLI_array_empty(verts); + for (i = 0, node = path->nodes.first; node; node = node->next, i++) { + BLI_array_growone(verts); + verts[i] = node->v; + } + + if (BM_face_exists(bm, verts, i, &f)) { + if (!BMO_elem_flag_test(bm, f, FACE_IGNORE)) { + BLI_ghash_remove(gh, endv, NULL, NULL); + continue; + } + } + break; + } + + vd = vdata + BM_elem_index_get(v1); + if (!vd->e) + continue; + + v2 = NULL; + while (1) { + if (!last->cure) { + last->cure = e = vdata[BM_elem_index_get(last->v)].e; + } + else { + last->cure = e = rotsys_nextedge(last->cure, last->v, edata, vdata); + if (last->cure == vdata[BM_elem_index_get(last->v)].e) { + v2 = NULL; + break; + } + } + + if (e == edge || !BMO_elem_flag_test(bm, e, EDGE_MARK)) { + continue; + } + + v2 = BM_edge_other_vert(e, last->v); + + if (BLI_ghash_haskey(gh, v2)) { + v2 = NULL; + continue; + } + + if (use_restrict && BMO_slot_map_contains(bm, op, "restrict", e)) { + int group = BMO_slot_map_int_get(bm, op, "restrict", e); + + if (!(group & path->group)) { + v2 = NULL; + continue; + } + } + + break; + } + + if (!v2) { + if (path) { + edge_free_path(pathbase, path); + path = NULL; + } + continue; + } + + /* add path back into hea */ + BLI_heap_insert(heap, path->weight, path); + + /* put v2 in gh ma */ + BLI_ghash_insert(gh, v2, NULL); + + path2 = edge_copy_add_path(pathbase, path, v2, e); + path2->weight = edge_weight_path(path2, edata, vdata); + + BLI_heap_insert(heap, path2->weight, path2); + } + + if (path && ((EPathNode *)path->nodes.last)->v != endv) { + edge_free_path(pathbase, path); + path = NULL; + } + + BLI_array_free(verts); + BLI_heap_free(heap, NULL); + BLI_ghash_free(gh, NULL, NULL); + + return path; +} + +static int count_edge_faces(BMesh *bm, BMEdge *e) +{ + int i = 0; + BMLoop *l = e->l; + + if (!l) { + return 0; + } + + do { + if (!BMO_elem_flag_test(bm, l->f, FACE_IGNORE)) { + i++; + } + + l = l->radial_next; + } while (l != e->l); + + return i; +} + +void bmesh_edgenet_fill_exec(BMesh *bm, BMOperator *op) +{ + BMIter iter; + BMOIter siter; + BMFace *f; + BMEdge *e, *edge; + BMVert **verts = NULL; + BLI_array_declare(verts); + EPath *path; + EPathNode *node; + EdgeData *edata; + VertData *vdata; + BMEdge **edges = NULL; + PathBase *pathbase = edge_pathbase_new(); + BLI_array_declare(edges); + int use_restrict = BMO_slot_int_get(op, "use_restrict"); + int i, j, group = 0; + unsigned int winding[2]; /* accumulte winding directions for each edge which has a face */ + + if (!bm->totvert || !bm->totedge) + return; + + edata = MEM_callocN(sizeof(EdgeData)*bm->totedge, "EdgeData"); + vdata = MEM_callocN(sizeof(VertData)*bm->totvert, "VertData"); + + BMO_slot_buffer_flag_enable(bm, op, "edges", EDGE_MARK, BM_EDGE); + BMO_slot_buffer_flag_enable(bm, op, "excludefaces", FACE_IGNORE, BM_FACE); + + BM_mesh_elem_index_ensure(bm, BM_VERT); + + BM_ITER(f, &iter, bm, BM_FACES_OF_MESH, NULL) { + BMO_elem_flag_enable(bm, f, ELE_ORIG); + } + + i = 0; + BM_ITER(e, &iter, bm, BM_EDGES_OF_MESH, NULL) { + BM_elem_index_set(e, i); /* set_inline */ + + if (!BMO_elem_flag_test(bm, e, EDGE_MARK)) { + edata[i].tag = 2; + } + + i++; + } + bm->elem_index_dirty &= ~BM_EDGE; + + init_rotsys(bm, edata, vdata); + + while (1) { + edge = NULL; + group = 0; + + BMO_ITER(e, &siter, bm, op, "edges", BM_EDGE) { + /* if restrict is on, only start on faces in the restrict map */ + if (use_restrict && !BMO_slot_map_contains(bm, op, "restrict", e)) + continue; + + if (edata[BM_elem_index_get(e)].tag < 2) { + edge = e; + + if (use_restrict) { + int i = 0, j = 0, gi = 0; + + group = BMO_slot_map_int_get(bm, op, "restrict", e); + + for (i = 0; i < 30; i++) { + if (group & (1 << i)) { + j++; + gi = i; + + if (j - 1 == edata[BM_elem_index_get(e)].tag) { + break; + } + } + } + + group = (1 << gi); + } + + break; + } + } + + if (!edge) + break; + + edata[BM_elem_index_get(edge)].tag += 1; + + path = edge_find_shortest_path(bm, op, edge, edata, vdata, pathbase, group); + if (!path) + continue; + + winding[0] = winding[1] = 0; + + BLI_array_empty(edges); + BLI_array_empty(verts); + i = 0; + for (node = path->nodes.first; node; node = node->next) { + if (!node->next) + continue; + + e = BM_edge_exists(node->v, node->next->v); + + /* this should never happe */ + if (!e) + break; + + /* check on the winding */ + if (e->l) { + BMVert *test_v1, *test_v2; + /* we want to use the reverse winding to the existing order */ + BM_edge_ordered_verts(edge, &test_v2, &test_v1); + + /* edges vote on which winding wins out */ + winding[(test_v1 == node->v)]++; + } + + edata[BM_elem_index_get(e)].ftag++; + BLI_array_growone(edges); + edges[i++] = e; + + BLI_array_append(verts, node->v); + } + + BLI_array_growone(edges); + edges[i++] = edge; + edata[BM_elem_index_get(edge)].ftag++; + + for (j = 0; j < i; j++) { + if (count_edge_faces(bm, edges[j]) >= 2) { + edge_free_path(pathbase, path); + break; + } + } + + if (j != i) { + continue; + } + + if (i) { + BMVert *v1, *v2; + + /* to define the winding order must select first edge, + * otherwise we could leave this as-is */ + edge = edges[0]; + + /* if these are even it doesnt really matter what to do, + * with consistent geometry one will be zero, the choice is clear */ + if (winding[0] > winding[1]) { + v1 = verts[0]; + v2 = verts[1]; + } + else { + v1 = verts[1]; + v2 = verts[0]; + } + + f = BM_face_create_ngon(bm, v1, v2, edges, i, TRUE); + if (f && !BMO_elem_flag_test(bm, f, ELE_ORIG)) { + BMO_elem_flag_enable(bm, f, FACE_NEW); + } + + if (use_restrict) + BMO_slot_map_int_insert(bm, op, "faceout_groupmap", f, path->group); + } + + edge_free_path(pathbase, path); + } + + BMO_slot_from_flag(bm, op, "faceout", FACE_NEW, BM_FACE); + + BLI_array_free(edges); + BLI_array_free(verts); + edge_pathbase_free(pathbase); + MEM_freeN(edata); + MEM_freeN(vdata); +} + +static BMEdge *edge_next(BMesh *bm, BMEdge *e) +{ + BMIter iter; + BMEdge *e2; + int i; + + for (i = 0; i < 2; i++) { + BM_ITER(e2, &iter, bm, BM_EDGES_OF_VERT, i ? e->v2 : e->v1) { + if ( (BMO_elem_flag_test(bm, e2, EDGE_MARK)) && + (!BMO_elem_flag_test(bm, e2, EDGE_VIS)) && + (e2 != e)) + { + return e2; + } + } + } + + return NULL; +} + +void bmesh_edgenet_prepare(BMesh *bm, BMOperator *op) +{ + BMOIter siter; + BMEdge *e; + BMEdge **edges1 = NULL, **edges2 = NULL, **edges; + BLI_array_declare(edges1); + BLI_array_declare(edges2); + BLI_array_declare(edges); + int ok = 1; + int i, count; + + BMO_slot_buffer_flag_enable(bm, op, "edges", EDGE_MARK, BM_EDGE); + + /* validate that each edge has at most one other tagged edge in the + * disk cycle around each of it's vertices */ + BMO_ITER(e, &siter, bm, op, "edges", BM_EDGE) { + for (i = 0; i < 2; i++) { + count = BMO_vert_edge_flags_count(bm, i ? e->v2 : e->v1, EDGE_MARK); + if (count > 2) { + ok = 0; + break; + } + } + + if (!ok) { + break; + } + } + + /* we don't have valid edge layouts, retur */ + if (!ok) { + return; + } + + /* find connected loops within the input edge */ + count = 0; + while (1) { + BMO_ITER(e, &siter, bm, op, "edges", BM_EDGE) { + if (!BMO_elem_flag_test(bm, e, EDGE_VIS)) { + if ( BMO_vert_edge_flags_count(bm, e->v1, EDGE_MARK) == 1 || + BMO_vert_edge_flags_count(bm, e->v2, EDGE_MARK) == 1) + { + break; + } + } + } + + if (!e) { + break; + } + + if (!count) { + edges = edges1; + } + else if (count == 1) { + edges = edges2; + } + else { + break; + } + + i = 0; + while (e) { + BMO_elem_flag_enable(bm, e, EDGE_VIS); + BLI_array_growone(edges); + edges[i] = e; + + e = edge_next(bm, e); + i++; + } + + if (!count) { + edges1 = edges; + BLI_array_set_length(edges1, BLI_array_count(edges)); + } + else { + edges2 = edges; + BLI_array_set_length(edges2, BLI_array_count(edges)); + } + + BLI_array_empty(edges); + count++; + } + + if (edges1 && BLI_array_count(edges1) > 2 && + BM_edge_share_vert(edges1[0], edges1[BLI_array_count(edges1) - 1])) + { + if (edges2 && BLI_array_count(edges2) > 2 && + BM_edge_share_vert(edges2[0], edges2[BLI_array_count(edges2) - 1])) + { + BLI_array_free(edges1); + BLI_array_free(edges2); + return; + } + else { + edges1 = edges2; + edges2 = NULL; + } + } + + if (edges2 && BLI_array_count(edges2) > 2 && + BM_edge_share_vert(edges2[0], edges2[BLI_array_count(edges2) - 1])) + { + edges2 = NULL; + } + + /* two unconnected loops, connect the */ + if (edges1 && edges2) { + BMVert *v1, *v2, *v3, *v4; + + if (BLI_array_count(edges1) == 1) { + v1 = edges1[0]->v1; + v2 = edges1[0]->v2; + } + else { + if (BM_vert_in_edge(edges1[1], edges1[0]->v1)) + v1 = edges1[0]->v2; + else v1 = edges1[0]->v1; + + i = BLI_array_count(edges1) - 1; + if (BM_vert_in_edge(edges1[i - 1], edges1[i]->v1)) + v2 = edges1[i]->v2; + else v2 = edges1[i]->v1; + } + + if (BLI_array_count(edges2) == 1) { + v3 = edges2[0]->v1; + v4 = edges2[0]->v2; + } + else { + if (BM_vert_in_edge(edges2[1], edges2[0]->v1)) + v3 = edges2[0]->v2; + else v3 = edges2[0]->v1; + + i = BLI_array_count(edges2) - 1; + if (BM_vert_in_edge(edges2[i - 1], edges2[i]->v1)) + v4 = edges2[i]->v2; + else v4 = edges2[i]->v1; + } + + /* avoid sqrt for comparison */ + if (len_squared_v3v3(v1->co, v3->co) + len_squared_v3v3(v2->co, v4->co) > + len_squared_v3v3(v1->co, v4->co) + len_squared_v3v3(v2->co, v3->co)) + { + BMVert *v; + v = v3; + v3 = v4; + v4 = v; + } + + e = BM_edge_create(bm, v1, v3, NULL, TRUE); + BMO_elem_flag_enable(bm, e, ELE_NEW); + e = BM_edge_create(bm, v2, v4, NULL, TRUE); + BMO_elem_flag_enable(bm, e, ELE_NEW); + } + else if (edges1) { + BMVert *v1, *v2; + + if (BLI_array_count(edges1) > 1) { + if (BM_vert_in_edge(edges1[1], edges1[0]->v1)) + v1 = edges1[0]->v2; + else v1 = edges1[0]->v1; + + i = BLI_array_count(edges1) - 1; + if (BM_vert_in_edge(edges1[i - 1], edges1[i]->v1)) + v2 = edges1[i]->v2; + else v2 = edges1[i]->v1; + + e = BM_edge_create(bm, v1, v2, NULL, TRUE); + BMO_elem_flag_enable(bm, e, ELE_NEW); + } + } + + BMO_slot_from_flag(bm, op, "edgeout", ELE_NEW, BM_EDGE); + + BLI_array_free(edges1); + BLI_array_free(edges2); +} + +/* this is essentially new fke */ +void bmesh_contextual_create_exec(BMesh *bm, BMOperator *op) +{ + BMOperator op2; + BMOIter oiter; + BMIter iter; + BMHeader *h; + BMVert *v, *verts[4]; + BMEdge *e; + BMFace *f; + int totv = 0, tote = 0, totf = 0, amount; + + /* count number of each element type we were passe */ + BMO_ITER(h, &oiter, bm, op, "geom", BM_VERT|BM_EDGE|BM_FACE) { + switch (h->htype) { + case BM_VERT: totv++; break; + case BM_EDGE: tote++; break; + case BM_FACE: totf++; break; + } + + BMO_elem_flag_enable(bm, (BMElemF *)h, ELE_NEW); + } + + /* --- Support for Special Case --- + * where there is a contiguous edge ring with one isolated vertex. + * + * This example shows 2 edges created from 3 verts + * with 1 free standing vertex. Dotted lines denote the 2 edges that are created. + * + * note that this works for any sided shape. + * + * +--------+ + * | . + * | . + * | . + * | . + * +........+ <-- starts out free standing. + * + */ + + /* Here we check for consistancy and create 2 edges */ + if (totf == 0 && totv >= 4 && totv == tote + 2) { + /* find a free standing vertex and 2 endpoint verts */ + BMVert *v_free = NULL, *v_a = NULL, *v_b = NULL; + int ok = TRUE; + + + BMO_ITER(v, &oiter, bm, op, "geom", BM_VERT) { + /* count how many flagged edges this vertex uses */ + int tot_edges = 0; + BM_ITER(e, &iter, bm, BM_EDGES_OF_VERT, v) { + if (BMO_elem_flag_test(bm, e, ELE_NEW)) { + tot_edges++; + if (tot_edges > 2) { + break; + } + } + } + + if (tot_edges == 0) { + /* only accept 1 free vert */ + if (v_free == NULL) v_free = v; + else ok = FALSE; /* only ever want one of these */ + } + else if (tot_edges == 1) { + if (v_a == NULL) v_a = v; + else if (v_b == NULL) v_b = v; + else ok = FALSE; /* only ever want 2 of these */ + } + else if (tot_edges == 2) { + /* do nothing, regular case */ + } + else { + ok = FALSE; /* if a vertex has 3+ edge users then cancel - this is only simple cases */ + } + + if (ok == FALSE) { + break; + } + } + + if (ok == TRUE && v_free && v_a && v_b) { + e = BM_edge_create(bm, v_free, v_a, NULL, TRUE); + BMO_elem_flag_enable(bm, e, ELE_NEW); + + e = BM_edge_create(bm, v_free, v_b, NULL, TRUE); + BMO_elem_flag_enable(bm, e, ELE_NEW); + } + } + /* --- end special case support, continue as normal --- */ + + + /* possible bug?, selecting 2 triangles and pressing F will make a quad rather then joining them, + * perhaps this should be looked into? - campbell */ + + /* call edgenet create */ + /* call edgenet prepare op so additional face creation cases wor */ + BMO_op_initf(bm, &op2, "edgenet_prepare edges=%fe", ELE_NEW); + BMO_op_exec(bm, &op2); + BMO_slot_buffer_flag_enable(bm, &op2, "edgeout", ELE_NEW, BM_EDGE); + BMO_op_finish(bm, &op2); + + BMO_op_initf(bm, &op2, "edgenet_fill edges=%fe", ELE_NEW); + BMO_op_exec(bm, &op2); + + /* return if edge net create did somethin */ + if (BMO_slot_buf_count(bm, &op2, "faceout")) { + BMO_slot_copy(&op2, op, "faceout", "faceout"); + BMO_op_finish(bm, &op2); + return; + } + + BMO_op_finish(bm, &op2); + + /* now call dissolve face */ + BMO_op_initf(bm, &op2, "dissolvefaces faces=%ff", ELE_NEW); + BMO_op_exec(bm, &op2); + + /* if we dissolved anything, then return */ + if (BMO_slot_buf_count(bm, &op2, "regionout")) { + BMO_slot_copy(&op2, op, "regionout", "faceout"); + BMO_op_finish(bm, &op2); + return; + } + + BMO_op_finish(bm, &op2); + + /* now, count how many verts we hav */ + amount = 0; + BM_ITER(v, &iter, bm, BM_VERTS_OF_MESH, NULL) { + if (BMO_elem_flag_test(bm, v, ELE_NEW)) { + verts[amount] = v; + amount++; + + if (amount > 4) break; + } + } + + if (amount == 2) { + /* create edg */ + e = BM_edge_create(bm, verts[0], verts[1], NULL, TRUE); + BMO_elem_flag_enable(bm, e, ELE_OUT); + } + else if (amount == 3) { + /* create triangl */ + BM_face_create_quad_tri(bm, verts[0], verts[1], verts[2], NULL, NULL, TRUE); + } + else if (amount == 4) { + f = NULL; + + /* the order of vertices can be anything, 6 cases to check */ + if (is_quad_convex_v3(verts[0]->co, verts[1]->co, verts[2]->co, verts[3]->co)) { + f = BM_face_create_quad_tri(bm, verts[0], verts[1], verts[2], verts[3], NULL, TRUE); + } + else if (is_quad_convex_v3(verts[0]->co, verts[2]->co, verts[3]->co, verts[1]->co)) { + f = BM_face_create_quad_tri(bm, verts[0], verts[2], verts[3], verts[1], NULL, TRUE); + } + else if (is_quad_convex_v3(verts[0]->co, verts[2]->co, verts[1]->co, verts[3]->co)) { + f = BM_face_create_quad_tri(bm, verts[0], verts[2], verts[1], verts[3], NULL, TRUE); + } + else if (is_quad_convex_v3(verts[0]->co, verts[1]->co, verts[3]->co, verts[2]->co)) { + f = BM_face_create_quad_tri(bm, verts[0], verts[1], verts[3], verts[2], NULL, TRUE); + } + else if (is_quad_convex_v3(verts[0]->co, verts[3]->co, verts[2]->co, verts[1]->co)) { + f = BM_face_create_quad_tri(bm, verts[0], verts[3], verts[2], verts[1], NULL, TRUE); + } + else if (is_quad_convex_v3(verts[0]->co, verts[3]->co, verts[1]->co, verts[2]->co)) { + f = BM_face_create_quad_tri(bm, verts[0], verts[3], verts[1], verts[2], NULL, TRUE); + } + else { + printf("cannot find nice quad from concave set of vertices\n"); + } + + if (f) BMO_elem_flag_enable(bm, f, ELE_OUT); + } +} diff --git a/source/blender/bmesh/operators/bmo_dissolve.c b/source/blender/bmesh/operators/bmo_dissolve.c new file mode 100644 index 00000000000..05aead466d1 --- /dev/null +++ b/source/blender/bmesh/operators/bmo_dissolve.c @@ -0,0 +1,559 @@ +/* + * ***** 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_array.h" +#include "BLI_math.h" + +#include "bmesh.h" +#include "bmesh_private.h" + +#include "bmesh_operators_private.h" /* own include */ + +#define FACE_MARK 1 +#define FACE_ORIG 2 +#define FACE_NEW 4 +#define EDGE_MARK 1 + +#define VERT_MARK 1 + +static int UNUSED_FUNCTION(check_hole_in_region)(BMesh *bm, BMFace *f) +{ + BMWalker regwalker; + BMIter liter2; + BMLoop *l2, *l3; + BMFace *f2; + + /* checks if there are any unmarked boundary edges in the face regio */ + + BMW_init(®walker, bm, BMW_ISLAND, + BMW_MASK_NOP, BMW_MASK_NOP, BMW_MASK_NOP, FACE_MARK, + BMW_NIL_LAY); + f2 = BMW_begin(®walker, f); + for ( ; f2; f2 = BMW_step(®walker)) { + l2 = BM_iter_new(&liter2, bm, BM_LOOPS_OF_FACE, f2); + for ( ; l2; l2 = BM_iter_step(&liter2)) { + l3 = bmesh_radial_nextloop(l2); + if ( BMO_elem_flag_test(bm, l3->f, FACE_MARK) != + BMO_elem_flag_test(bm, l2->f, FACE_MARK)) + { + if (!BMO_elem_flag_test(bm, l2->e, EDGE_MARK)) { + return FALSE; + } + } + } + } + BMW_end(®walker); + + return TRUE; +} + +void dissolvefaces_exec(BMesh *bm, BMOperator *op) +{ + BMOIter oiter; + BMFace *f, *f2 /* , *nf = NULL */; + BLI_array_declare(faces); + BLI_array_declare(regions); + BMFace ***regions = NULL; + BMFace **faces = NULL; + BMWalker regwalker; + int i; + + int use_verts = BMO_slot_int_get(op, "use_verts"); + + if (use_verts) { + /* tag verts that start out with only 2 edges, + * don't remove these later */ + BMIter viter; + BMVert *v; + + BM_ITER(v, &viter, bm, BM_VERTS_OF_MESH, NULL) { + if (BM_vert_edge_count(v) == 2) { + BMO_elem_flag_disable(bm, v, VERT_MARK); + } + else { + BMO_elem_flag_enable(bm, v, VERT_MARK); + } + } + } + + BMO_slot_buffer_flag_enable(bm, op, "faces", FACE_MARK, BM_FACE); + + /* collect region */ + BMO_ITER(f, &oiter, bm, op, "faces", BM_FACE) { + if (!BMO_elem_flag_test(bm, f, FACE_MARK)) continue; + + BLI_array_empty(faces); + faces = NULL; /* forces different allocatio */ + + /* yay, walk */ + BMW_init(®walker, bm, BMW_ISLAND, + BMW_MASK_NOP, BMW_MASK_NOP, BMW_MASK_NOP, FACE_MARK, + BMW_NIL_LAY); + + f2 = BMW_begin(®walker, f); + for ( ; f2; f2 = BMW_step(®walker)) { + BLI_array_append(faces, f2); + } + BMW_end(®walker); + + for (i = 0; i < BLI_array_count(faces); i++) { + f2 = faces[i]; + BMO_elem_flag_disable(bm, f2, FACE_MARK); + BMO_elem_flag_enable(bm, f2, FACE_ORIG); + } + + if (BMO_error_occurred(bm)) { + BMO_error_clear(bm); + BMO_error_raise(bm, op, BMERR_DISSOLVEFACES_FAILED, NULL); + goto cleanup; + } + + BLI_array_append(faces, NULL); + BLI_array_append(regions, faces); + } + + for (i = 0; i < BLI_array_count(regions); i++) { + int tot = 0; + + faces = regions[i]; + if (!faces[0]) { + BMO_error_raise(bm, op, BMERR_DISSOLVEFACES_FAILED, + "Could not find boundary of dissolve region"); + goto cleanup; + } + + while (faces[tot]) + tot++; + + f = BM_faces_join(bm, faces, tot); + if (!f) { + BMO_error_raise(bm, op, BMERR_DISSOLVEFACES_FAILED, + "Could not create merged face"); + goto cleanup; + } + + /* if making the new face failed (e.g. overlapping test) + * unmark the original faces for deletion */ + BMO_elem_flag_disable(bm, f, FACE_ORIG); + BMO_elem_flag_enable(bm, f, FACE_NEW); + + } + + BMO_op_callf(bm, "del geom=%ff context=%d", FACE_ORIG, DEL_FACES); + + + if (use_verts) { + BMIter viter; + BMVert *v; + + BM_ITER(v, &viter, bm, BM_VERTS_OF_MESH, NULL) { + if (BMO_elem_flag_test(bm, v, VERT_MARK)) { + if (BM_vert_edge_count(v) == 2) { + BM_vert_collapse_edges(bm, v->e, v); + } + } + } + } + + if (BMO_error_occurred(bm)) goto cleanup; + + BMO_slot_from_flag(bm, op, "regionout", FACE_NEW, BM_FACE); + +cleanup: + /* free/cleanu */ + for (i = 0; i < BLI_array_count(regions); i++) { + if (regions[i]) MEM_freeN(regions[i]); + } + + BLI_array_free(regions); +} + +/* almost identical to dissolve edge, except it cleans up vertice */ +void dissolve_edgeloop_exec(BMesh *bm, BMOperator *op) +{ + /* BMOperator fop; */ + BMOIter oiter; + BMIter iter; + BMVert *v, **verts = NULL; + BLI_array_declare(verts); + BMEdge *e; + /* BMFace *f; */ + int i; + + BMO_ITER(e, &oiter, bm, op, "edges", BM_EDGE) { + if (BM_edge_face_count(e) == 2) { + BMO_elem_flag_enable(bm, e->v1, VERT_MARK); + BMO_elem_flag_enable(bm, e->v2, VERT_MARK); + + BM_faces_join_pair(bm, e->l->f, + e->l->radial_next->f, + e); + } + } + + BM_ITER(v, &iter, bm, BM_VERTS_OF_MESH, NULL) { + if (BMO_elem_flag_test(bm, v, VERT_MARK) && BM_vert_edge_count(v) == 2) { + BLI_array_append(verts, v); + } + } + + /* clean up extreneous 2-valence vertice */ + for (i = 0; i < BLI_array_count(verts); i++) { + if (verts[i]->e) { + BM_vert_collapse_edges(bm, verts[i]->e, verts[i]); + } + } + + BLI_array_free(verts); + + //BMO_op_initf(bm, &fop, "dissolvefaces faces=%ff", FACE_MARK); + //BMO_op_exec(bm, &fop); + + //BMO_slot_copy(op, &fop, "regionout", "regionout"); + + //BMO_op_finish(bm, &fop); +} + + +void dissolveedges_exec(BMesh *bm, BMOperator *op) +{ + /* might want to make this an option or mode - campbell */ + + /* BMOperator fop; */ + BMOIter eiter; + BMEdge *e; + + BMIter viter; + BMVert *v; + + int use_verts = BMO_slot_int_get(op, "use_verts"); + + if (use_verts) { + BM_ITER(v, &viter, bm, BM_VERTS_OF_MESH, NULL) { + if (BM_vert_edge_count(v) == 2) { + BMO_elem_flag_disable(bm, v, VERT_MARK); + } + else { + BMO_elem_flag_enable(bm, v, VERT_MARK); + } + } + } + + BMO_ITER(e, &eiter, bm, op, "edges", BM_EDGE) { + const int edge_face_count = BM_edge_face_count(e); + if (edge_face_count == 2) { + + /* join faces */ + BM_faces_join_pair(bm, e->l->f, + e->l->radial_next->f, + e); + } + } + + if (use_verts) { + BM_ITER(v, &viter, bm, BM_VERTS_OF_MESH, NULL) { + if (BMO_elem_flag_test(bm, v, VERT_MARK)) { + if (BM_vert_edge_count(v) == 2) { + BM_vert_collapse_edges(bm, v->e, v); + } + } + } + } +} + +static int test_extra_verts(BMesh *bm, BMVert *v) +{ + BMIter iter, liter, iter2, iter3; + BMFace *f, *f2; + BMLoop *l; + BMEdge *e; + int found; + + /* test faces around verts for verts that would be wronly killed + * by dissolve faces. */ + f = BM_iter_new(&iter, bm, BM_FACES_OF_VERT, v); + for ( ; f; f = BM_iter_step(&iter)) { + l = BM_iter_new(&liter, bm, BM_LOOPS_OF_FACE, f); + for ( ; l; l = BM_iter_step(&liter)) { + if (!BMO_elem_flag_test(bm, l->v, VERT_MARK)) { + /* if an edge around a vert is a boundary edge, + * then dissolve faces won't destroy it. + * also if it forms a boundary with one + * of the face region */ + found = FALSE; + e = BM_iter_new(&iter2, bm, BM_EDGES_OF_VERT, l->v); + for ( ; e; e = BM_iter_step(&iter2)) { + if (BM_edge_face_count(e) == 1) { + found = TRUE; + } + f2 = BM_iter_new(&iter3, bm, BM_FACES_OF_EDGE, e); + for ( ; f2; f2 = BM_iter_step(&iter3)) { + if (!BMO_elem_flag_test(bm, f2, FACE_MARK)) { + found = TRUE; + break; + } + } + if (found == TRUE) { + break; + } + } + if (found == FALSE) { + return FALSE; + } + } + } + } + + return TRUE; +} +void dissolveverts_exec(BMesh *bm, BMOperator *op) +{ + BMOpSlot *vinput; + BMIter iter, fiter; + BMVert *v; + BMFace *f; + /* int i; */ + + vinput = BMO_slot_get(op, "verts"); + BMO_slot_buffer_flag_enable(bm, op, "verts", VERT_MARK, BM_VERT); + + for (v = BM_iter_new(&iter, bm, BM_VERTS_OF_MESH, NULL); v; v = BM_iter_step(&iter)) { + if (BMO_elem_flag_test(bm, v, VERT_MARK)) { + /* check if it's a two-valence ver */ + if (BM_vert_edge_count(v) == 2) { + + /* collapse the ver */ + BM_vert_collapse_faces(bm, v->e, v, 1.0f, TRUE); + continue; + } + + f = BM_iter_new(&fiter, bm, BM_FACES_OF_VERT, v); + for ( ; f; f = BM_iter_step(&fiter)) { + BMO_elem_flag_enable(bm, f, FACE_ORIG); + BMO_elem_flag_enable(bm, f, FACE_MARK); + } + + /* check if our additions to the input to face dissolve + * will destroy nonmarked vertices. */ + if (!test_extra_verts(bm, v)) { + f = BM_iter_new(&fiter, bm, BM_FACES_OF_VERT, v); + for ( ; f; f = BM_iter_step(&fiter)) { + if (BMO_elem_flag_test(bm, f, FACE_ORIG)) { + BMO_elem_flag_disable(bm, f, FACE_MARK); + BMO_elem_flag_disable(bm, f, FACE_ORIG); + } + } + } + else { + f = BM_iter_new(&fiter, bm, BM_FACES_OF_VERT, v); + for ( ; f; f = BM_iter_step(&fiter)) { + BMO_elem_flag_disable(bm, f, FACE_ORIG); + } + } + } + } + + BMO_op_callf(bm, "dissolvefaces faces=%ff", FACE_MARK); + if (BMO_error_occurred(bm)) { + const char *msg; + + BMO_error_get(bm, &msg, NULL); + BMO_error_clear(bm); + BMO_error_raise(bm, op, BMERR_DISSOLVEVERTS_FAILED, msg); + } + + /* clean up any remainin */ + for (v = BM_iter_new(&iter, bm, BM_VERTS_OF_MESH, NULL); v; v = BM_iter_step(&iter)) { + if (BMO_elem_flag_test(bm, v, VERT_MARK)) { + if (!BM_vert_dissolve(bm, v)) { + BMO_error_raise(bm, op, BMERR_DISSOLVEVERTS_FAILED, NULL); + return; + } + } + } + +} + +/* this code is for cleaning up two-edged faces, it shall become + * it's own function one day */ +#if 0 +void dummy_exec(BMesh *bm, BMOperator *op) +{ + { + /* clean up two-edged face */ + /* basic idea is to keep joining 2-edged faces until their + * gone. this however relies on joining two 2-edged faces + * together to work, which doesn't */ + found3 = 1; + while (found3) { + found3 = 0; + for (f = BM_iter_new(&iter, bm, BM_FACES_OF_MESH, NULL); f; f = BM_iter_step(&iter)) { + if (!BM_face_validate(bm, f, stderr)) { + printf("error.\n"); + } + + if (f->len == 2) { + //this design relies on join faces working + //with two-edged faces properly. + //commenting this line disables the + //outermost loop. + //found3 = 1; + found2 = 0; + l = BM_iter_new(&liter, bm, BM_LOOPS_OF_FACE, f); + fe = l->e; + for ( ; l; l = BM_iter_step(&liter)) { + f2 = BM_iter_new(&fiter, bm, + BM_FACES_OF_EDGE, l->e); + for ( ; f2; f2 = BM_iter_step(&fiter)) { + if (f2 != f) { + BM_faces_join_pair(bm, f, f2, l->e); + found2 = 1; + break; + } + } + if (found2) break; + } + + if (!found2) { + bmesh_kf(bm, f); + bmesh_ke(bm, fe); + } + } /* else if (f->len == 3) { + BMEdge *ed[3]; + BMVert *vt[3]; + BMLoop *lp[3]; + int i = 0; + + //check for duplicate edges + l = BM_iter_new(&liter, bm, BM_LOOPS_OF_FACE, f); + for ( ; l; l = BM_iter_step(&liter)) { + ed[i] = l->e; + lp[i] = l; + vt[i++] = l->v; + } + if (vt[0] == vt[1] || vt[0] == vt[2]) { + i += 1; + } + */ + } + } + if (oldlen == len) break; + oldlen = len; + } +} + +#endif + +/**/ +typedef struct DissolveElemWeight_t { + BMHeader *ele; + float weight; +} DissolveElemWeight_t; + +static int dissolve_elem_cmp(const void *a1, const void *a2) +{ + const struct DissolveElemWeight_t *d1 = a1, *d2 = a2; + + if (d1->weight > d2->weight) return 1; + else if (d1->weight < d2->weight) return -1; + return 0; +} + +void dissolvelimit_exec(BMesh *bm, BMOperator *op) +{ + BMOpSlot *einput = BMO_slot_get(op, "edges"); + BMOpSlot *vinput = BMO_slot_get(op, "verts"); + const float angle_max = (float)M_PI / 2.0f; + const float angle_limit = minf(angle_max, BMO_slot_float_get(op, "angle_limit")); + DissolveElemWeight_t *weight_elems = MEM_mallocN(MAX2(einput->len, vinput->len) * + sizeof(DissolveElemWeight_t), __func__); + int i, tot_found; + + /* --- first edges --- */ + + /* go through and split edge */ + for (i = 0, tot_found = 0; i < einput->len; i++) { + BMEdge *e = ((BMEdge **)einput->data.p)[i]; + const float angle = BM_edge_face_angle(bm, e); + + if (angle < angle_limit) { + weight_elems[i].ele = (BMHeader *)e; + weight_elems[i].weight = angle; + tot_found++; + } + else { + weight_elems[i].ele = NULL; + weight_elems[i].weight = angle_max; + } + } + + if (tot_found != 0) { + qsort(weight_elems, einput->len, sizeof(DissolveElemWeight_t), dissolve_elem_cmp); + + for (i = 0; i < tot_found; i++) { + BMEdge *e = (BMEdge *)weight_elems[i].ele; + /* check twice because cumulative effect could disolve over angle limit */ + if (BM_edge_face_angle(bm, e) < angle_limit) { + BMFace *nf = BM_faces_join_pair(bm, e->l->f, + e->l->radial_next->f, + e); /* join faces */ + + /* there may be some errors, we dont mind, just move on */ + if (nf == NULL) { + BMO_error_clear(bm); + } + } + } + } + + /* --- second verts --- */ + for (i = 0, tot_found = 0; i < vinput->len; i++) { + BMVert *v = ((BMVert **)vinput->data.p)[i]; + const float angle = BM_vert_edge_angle(bm, v); + + if (angle < angle_limit) { + weight_elems[i].ele = (BMHeader *)v; + weight_elems[i].weight = angle; + tot_found++; + } + else { + weight_elems[i].ele = NULL; + weight_elems[i].weight = angle_max; + } + } + + if (tot_found != 0) { + qsort(weight_elems, vinput->len, sizeof(DissolveElemWeight_t), dissolve_elem_cmp); + + for (i = 0; i < tot_found; i++) { + BMVert *v = (BMVert *)weight_elems[i].ele; + /* check twice because cumulative effect could disolve over angle limit */ + if (BM_vert_edge_angle(bm, v) < angle_limit) { + BM_vert_collapse_edges(bm, v->e, v); /* join edges */ + } + } + } + + MEM_freeN(weight_elems); +} diff --git a/source/blender/bmesh/operators/bmo_dupe.c b/source/blender/bmesh/operators/bmo_dupe.c new file mode 100644 index 00000000000..2e1c91d920c --- /dev/null +++ b/source/blender/bmesh/operators/bmo_dupe.c @@ -0,0 +1,512 @@ +/* + * ***** 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_array.h" +#include "BLI_math.h" + +#include "bmesh.h" + +/* local flag define */ +#define DUPE_INPUT 1 /* input from operato */ +#define DUPE_NEW 2 +#define DUPE_DONE 4 +#define DUPE_MAPPED 8 + +/* + * COPY VERTEX + * + * Copy an existing vertex from one bmesh to another. + * + */ +static BMVert *copy_vertex(BMesh *source_mesh, BMVert *source_vertex, BMesh *target_mesh, GHash *vhash) +{ + BMVert *target_vertex = NULL; + + /* Create a new verte */ + target_vertex = BM_vert_create(target_mesh, source_vertex->co, NULL); + + /* Insert new vertex into the vert has */ + BLI_ghash_insert(vhash, source_vertex, target_vertex); + + /* Copy attribute */ + BM_elem_attrs_copy(source_mesh, target_mesh, source_vertex, target_vertex); + + /* Set internal op flag */ + BMO_elem_flag_enable(target_mesh, target_vertex, DUPE_NEW); + + return target_vertex; +} + +/* + * COPY EDGE + * + * Copy an existing edge from one bmesh to another. + * + */ +static BMEdge *copy_edge(BMOperator *op, BMesh *source_mesh, + BMEdge *source_edge, BMesh *target_mesh, + GHash *vhash, GHash *ehash) +{ + BMEdge *target_edge = NULL; + BMVert *target_vert1, *target_vert2; + BMFace *face; + BMIter fiter; + int rlen; + + /* see if any of the neighboring faces are + * not being duplicated. in that case, + * add it to the new/old map. */ + rlen = 0; + for (face = BM_iter_new(&fiter, source_mesh, BM_FACES_OF_EDGE, source_edge); + face; + face = BM_iter_step(&fiter)) + { + if (BMO_elem_flag_test(source_mesh, face, DUPE_INPUT)) { + rlen++; + } + } + + /* Lookup v1 and v2 */ + target_vert1 = BLI_ghash_lookup(vhash, source_edge->v1); + target_vert2 = BLI_ghash_lookup(vhash, source_edge->v2); + + /* Create a new edg */ + target_edge = BM_edge_create(target_mesh, target_vert1, target_vert2, NULL, FALSE); + + /* add to new/old edge map if necassar */ + if (rlen < 2) { + /* not sure what non-manifold cases of greater then three + * radial should do. */ + BMO_slot_map_ptr_insert(source_mesh, op, "boundarymap", + source_edge, target_edge); + } + + /* Insert new edge into the edge hash */ + BLI_ghash_insert(ehash, source_edge, target_edge); + + /* Copy attributes */ + BM_elem_attrs_copy(source_mesh, target_mesh, source_edge, target_edge); + + /* Set internal op flags */ + BMO_elem_flag_enable(target_mesh, target_edge, DUPE_NEW); + + return target_edge; +} + +/* + * COPY FACE + * + * Copy an existing face from one bmesh to another. + */ + +static BMFace *copy_face(BMOperator *op, BMesh *source_mesh, + BMFace *source_face, BMesh *target_mesh, + BMVert **vtar, BMEdge **edar, GHash *vhash, GHash *ehash) +{ + /* BMVert *target_vert1, *target_vert2; */ /* UNUSED */ + BMLoop *source_loop, *target_loop; + BMFace *target_face = NULL; + BMIter iter, iter2; + int i; + + /* lookup the first and second vert */ +#if 0 /* UNUSED */ + target_vert1 = BLI_ghash_lookup(vhash, BM_iter_new(&iter, source_mesh, BM_VERTS_OF_FACE, source_face)); + target_vert2 = BLI_ghash_lookup(vhash, BM_iter_step(&iter)); +#else + BM_iter_new(&iter, source_mesh, BM_VERTS_OF_FACE, source_face); + BM_iter_step(&iter); +#endif + + /* lookup edge */ + for (i = 0, source_loop = BM_iter_new(&iter, source_mesh, BM_LOOPS_OF_FACE, source_face); + source_loop; + source_loop = BM_iter_step(&iter), i++) + { + vtar[i] = BLI_ghash_lookup(vhash, source_loop->v); + edar[i] = BLI_ghash_lookup(ehash, source_loop->e); + } + + /* create new fac */ + target_face = BM_face_create(target_mesh, vtar, edar, source_face->len, FALSE); + BMO_slot_map_ptr_insert(source_mesh, op, + "facemap", source_face, target_face); + BMO_slot_map_ptr_insert(source_mesh, op, + "facemap", target_face, source_face); + + BM_elem_attrs_copy(source_mesh, target_mesh, source_face, target_face); + + /* mark the face for outpu */ + BMO_elem_flag_enable(target_mesh, target_face, DUPE_NEW); + + /* copy per-loop custom dat */ + BM_ITER(source_loop, &iter, source_mesh, BM_LOOPS_OF_FACE, source_face) { + BM_ITER(target_loop, &iter2, target_mesh, BM_LOOPS_OF_FACE, target_face) { + if (BLI_ghash_lookup(vhash, source_loop->v) == target_loop->v) { + BM_elem_attrs_copy(source_mesh, target_mesh, source_loop, target_loop); + break; + } + } + } + + return target_face; +} + +/* + * COPY MESH + * + * Internal Copy function. + */ +static void copy_mesh(BMOperator *op, BMesh *source, BMesh *target) +{ + + BMVert *v = NULL, *v2; + BMEdge *e = NULL; + BMFace *f = NULL; + + BLI_array_declare(vtar); + BLI_array_declare(edar); + BMVert **vtar = NULL; + BMEdge **edar = NULL; + + BMIter verts; + BMIter edges; + BMIter faces; + + GHash *vhash; + GHash *ehash; + + /* initialize pointer hashe */ + vhash = BLI_ghash_new(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp, "bmesh dupeops v"); + ehash = BLI_ghash_new(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp, "bmesh dupeops e"); + + for (v = BM_iter_new(&verts, source, BM_VERTS_OF_MESH, source); v; v = BM_iter_step(&verts)) { + if ( BMO_elem_flag_test(source, v, DUPE_INPUT) && + !BMO_elem_flag_test(source, v, DUPE_DONE)) + { + BMIter iter; + int iso = 1; + + v2 = copy_vertex(source, v, target, vhash); + + BM_ITER(f, &iter, source, BM_FACES_OF_VERT, v) { + if (BMO_elem_flag_test(source, f, DUPE_INPUT)) { + iso = 0; + break; + } + } + + if (iso) { + BM_ITER(e, &iter, source, BM_EDGES_OF_VERT, v) { + if (BMO_elem_flag_test(source, e, DUPE_INPUT)) { + iso = 0; + break; + } + } + } + + if (iso) { + BMO_slot_map_ptr_insert(source, op, "isovertmap", v, v2); + } + + BMO_elem_flag_enable(source, v, DUPE_DONE); + } + } + + /* now we dupe all the edge */ + for (e = BM_iter_new(&edges, source, BM_EDGES_OF_MESH, source); e; e = BM_iter_step(&edges)) { + if ( BMO_elem_flag_test(source, e, DUPE_INPUT) && + !BMO_elem_flag_test(source, e, DUPE_DONE)) + { + /* make sure that verts are copie */ + if (!BMO_elem_flag_test(source, e->v1, DUPE_DONE)) { + copy_vertex(source, e->v1, target, vhash); + BMO_elem_flag_enable(source, e->v1, DUPE_DONE); + } + if (!BMO_elem_flag_test(source, e->v2, DUPE_DONE)) { + copy_vertex(source, e->v2, target, vhash); + BMO_elem_flag_enable(source, e->v2, DUPE_DONE); + } + /* now copy the actual edg */ + copy_edge(op, source, e, target, vhash, ehash); + BMO_elem_flag_enable(source, e, DUPE_DONE); + } + } + + /* first we dupe all flagged faces and their elements from sourc */ + for (f = BM_iter_new(&faces, source, BM_FACES_OF_MESH, source); f; f = BM_iter_step(&faces)) { + if (BMO_elem_flag_test(source, f, DUPE_INPUT)) { + /* vertex pas */ + for (v = BM_iter_new(&verts, source, BM_VERTS_OF_FACE, f); v; v = BM_iter_step(&verts)) { + if (!BMO_elem_flag_test(source, v, DUPE_DONE)) { + copy_vertex(source, v, target, vhash); + BMO_elem_flag_enable(source, v, DUPE_DONE); + } + } + + /* edge pas */ + for (e = BM_iter_new(&edges, source, BM_EDGES_OF_FACE, f); e; e = BM_iter_step(&edges)) { + if (!BMO_elem_flag_test(source, e, DUPE_DONE)) { + copy_edge(op, source, e, target, vhash, ehash); + BMO_elem_flag_enable(source, e, DUPE_DONE); + } + } + + /* ensure arrays are the right size */ + BLI_array_empty(vtar); + BLI_array_empty(edar); + + BLI_array_growitems(vtar, f->len); + BLI_array_growitems(edar, f->len); + + copy_face(op, source, f, target, vtar, edar, vhash, ehash); + BMO_elem_flag_enable(source, f, DUPE_DONE); + } + } + + /* free pointer hashe */ + BLI_ghash_free(vhash, NULL, NULL); + BLI_ghash_free(ehash, NULL, NULL); + + BLI_array_free(vtar); /* free vert pointer array */ + BLI_array_free(edar); /* free edge pointer array */ +} + +/* + * Duplicate Operator + * + * Duplicates verts, edges and faces of a mesh. + * + * INPUT SLOTS: + * + * BMOP_DUPE_VINPUT: Buffer containing pointers to mesh vertices to be duplicated + * BMOP_DUPE_EINPUT: Buffer containing pointers to mesh edges to be duplicated + * BMOP_DUPE_FINPUT: Buffer containing pointers to mesh faces to be duplicated + * + * OUTPUT SLOTS: + * + * BMOP_DUPE_VORIGINAL: Buffer containing pointers to the original mesh vertices + * BMOP_DUPE_EORIGINAL: Buffer containing pointers to the original mesh edges + * BMOP_DUPE_FORIGINAL: Buffer containing pointers to the original mesh faces + * BMOP_DUPE_VNEW: Buffer containing pointers to the new mesh vertices + * BMOP_DUPE_ENEW: Buffer containing pointers to the new mesh edges + * BMOP_DUPE_FNEW: Buffer containing pointers to the new mesh faces + * + */ + +void dupeop_exec(BMesh *bm, BMOperator *op) +{ + BMOperator *dupeop = op; + BMesh *bm2 = BMO_slot_ptr_get(op, "dest"); + + if (!bm2) + bm2 = bm; + + /* flag inpu */ + BMO_slot_buffer_flag_enable(bm, dupeop, "geom", DUPE_INPUT, BM_ALL); + + /* use the internal copy functio */ + copy_mesh(dupeop, bm, bm2); + + /* Outpu */ + /* First copy the input buffers to output buffers - original dat */ + BMO_slot_copy(dupeop, dupeop, "geom", "origout"); + + /* Now alloc the new output buffer */ + BMO_slot_from_flag(bm, dupeop, "newout", DUPE_NEW, BM_ALL); +} + +#if 0 /* UNUSED */ +/* executes the duplicate operation, feeding elements of + * type flag etypeflag and header flag flag to it. note, + * to get more useful information (such as the mapping from + * original to new elements) you should run the dupe op manually */ +void BMO_dupe_from_flag(BMesh *bm, int etypeflag, const char hflag) +{ + BMOperator dupeop; + + BMO_op_init(bm, &dupeop, "dupe"); + BMO_slot_from_hflag(bm, &dupeop, "geom", hflag, etypeflag); + + BMO_op_exec(bm, &dupeop); + BMO_op_finish(bm, &dupeop); +} +#endif + +/* + * Split Operator + * + * Duplicates verts, edges and faces of a mesh but also deletes the originals. + * + * INPUT SLOTS: + * + * BMOP_DUPE_VINPUT: Buffer containing pointers to mesh vertices to be split + * BMOP_DUPE_EINPUT: Buffer containing pointers to mesh edges to be split + * BMOP_DUPE_FINPUT: Buffer containing pointers to mesh faces to be split + * + * OUTPUT SLOTS: + * + * BMOP_DUPE_VOUTPUT: Buffer containing pointers to the split mesh vertices + * BMOP_DUPE_EOUTPUT: Buffer containing pointers to the split mesh edges + * BMOP_DUPE_FOUTPUT: Buffer containing pointers to the split mesh faces + */ + +#define SPLIT_INPUT 1 +void splitop_exec(BMesh *bm, BMOperator *op) +{ + BMOperator *splitop = op; + BMOperator dupeop; + BMOperator delop; + BMVert *v; + BMEdge *e; + BMFace *f; + BMIter iter, iter2; + int found; + + /* initialize our sub-operator */ + BMO_op_init(bm, &dupeop, "dupe"); + BMO_op_init(bm, &delop, "del"); + + BMO_slot_copy(splitop, &dupeop, "geom", "geom"); + BMO_op_exec(bm, &dupeop); + + BMO_slot_buffer_flag_enable(bm, splitop, "geom", SPLIT_INPUT, BM_ALL); + + /* make sure to remove edges and verts we don't need */ + for (e = BM_iter_new(&iter, bm, BM_EDGES_OF_MESH, NULL); e; e = BM_iter_step(&iter)) { + found = 0; + f = BM_iter_new(&iter2, bm, BM_FACES_OF_EDGE, e); + for ( ; f; f = BM_iter_step(&iter2)) { + if (!BMO_elem_flag_test(bm, f, SPLIT_INPUT)) { + found = 1; + break; + } + } + if (!found) BMO_elem_flag_enable(bm, e, SPLIT_INPUT); + } + + for (v = BM_iter_new(&iter, bm, BM_VERTS_OF_MESH, NULL); v; v = BM_iter_step(&iter)) { + found = 0; + e = BM_iter_new(&iter2, bm, BM_EDGES_OF_VERT, v); + for ( ; e; e = BM_iter_step(&iter2)) { + if (!BMO_elem_flag_test(bm, e, SPLIT_INPUT)) { + found = 1; + break; + } + } + if (!found) BMO_elem_flag_enable(bm, v, SPLIT_INPUT); + + } + + /* connect outputs of dupe to delete, exluding keep geometr */ + BMO_slot_int_set(&delop, "context", DEL_FACES); + BMO_slot_from_flag(bm, &delop, "geom", SPLIT_INPUT, BM_ALL); + + BMO_op_exec(bm, &delop); + + /* now we make our outputs by copying the dupe output */ + BMO_slot_copy(&dupeop, splitop, "newout", "geomout"); + BMO_slot_copy(&dupeop, splitop, "boundarymap", + "boundarymap"); + BMO_slot_copy(&dupeop, splitop, "isovertmap", + "isovertmap"); + + /* cleanu */ + BMO_op_finish(bm, &delop); + BMO_op_finish(bm, &dupeop); +} + + +void delop_exec(BMesh *bm, BMOperator *op) +{ +#define DEL_INPUT 1 + + BMOperator *delop = op; + + /* Mark Buffer */ + BMO_slot_buffer_flag_enable(bm, delop, "geom", DEL_INPUT, BM_ALL); + + BMO_remove_tagged_context(bm, DEL_INPUT, BMO_slot_int_get(op, "context")); + +#undef DEL_INPUT +} + +/* + * Spin Operator + * + * Extrude or duplicate geometry a number of times, + * rotating and possibly translating after each step + */ + +void spinop_exec(BMesh *bm, BMOperator *op) +{ + BMOperator dupop, extop; + float cent[3], dvec[3]; + float axis[3] = {0.0f, 0.0f, 1.0f}; + float q[4]; + float rmat[3][3]; + float phi, si; + int steps, dupli, a, usedvec; + + BMO_slot_vec_get(op, "cent", cent); + BMO_slot_vec_get(op, "axis", axis); + normalize_v3(axis); + BMO_slot_vec_get(op, "dvec", dvec); + usedvec = !is_zero_v3(dvec); + steps = BMO_slot_int_get(op, "steps"); + phi = BMO_slot_float_get(op, "ang") * (float)M_PI / (360.0f * steps); + dupli = BMO_slot_int_get(op, "dupli"); + + si = (float)sin(phi); + q[0] = (float)cos(phi); + q[1] = axis[0]*si; + q[2] = axis[1]*si; + q[3] = axis[2]*si; + quat_to_mat3(rmat, q); + + BMO_slot_copy(op, op, "geom", "lastout"); + for (a = 0; a < steps; a++) { + if (dupli) { + BMO_op_initf(bm, &dupop, "dupe geom=%s", op, "lastout"); + BMO_op_exec(bm, &dupop); + BMO_op_callf(bm, "rotate cent=%v mat=%m3 verts=%s", + cent, rmat, &dupop, "newout"); + BMO_slot_copy(&dupop, op, "newout", "lastout"); + BMO_op_finish(bm, &dupop); + } + else { + BMO_op_initf(bm, &extop, "extrudefaceregion edgefacein=%s", + op, "lastout"); + BMO_op_exec(bm, &extop); + BMO_op_callf(bm, "rotate cent=%v mat=%m3 verts=%s", + cent, rmat, &extop, "geomout"); + BMO_slot_copy(&extop, op, "geomout", "lastout"); + BMO_op_finish(bm, &extop); + } + + if (usedvec) + BMO_op_callf(bm, "translate vec=%v verts=%s", dvec, op, "lastout"); + } +} diff --git a/source/blender/bmesh/operators/bmo_edgesplit.c b/source/blender/bmesh/operators/bmo_edgesplit.c new file mode 100644 index 00000000000..7d719e1ddc9 --- /dev/null +++ b/source/blender/bmesh/operators/bmo_edgesplit.c @@ -0,0 +1,426 @@ +/* + * ***** 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_array.h" + +#include "bmesh.h" + +#include "bmesh_operators_private.h" /* own include */ + +typedef struct EdgeTag { + BMVert *newv1, *newv2; + BMEdge *newe1, *newe2; + int tag; +} EdgeTag; + +/* (EDGE_DEL == FACE_DEL) - this must be the case */ +#define EDGE_DEL 1 +#define EDGE_SEAM 2 +#define EDGE_MARK 4 +#define EDGE_RET1 8 +#define EDGE_RET2 16 + +#define FACE_DEL 1 +#define FACE_NEW 2 + +static BMFace *remake_face(BMesh *bm, EdgeTag *etags, BMFace *f, BMVert **verts, BMEdge **edges_tmp) +{ + BMIter liter1, liter2; + EdgeTag *et; + BMFace *f2; + BMLoop *l, *l2; + BMEdge *e; + BMVert *lastv1, *lastv2 /* , *v1, *v2 */ /* UNUSED */; + int i; + + /* we do final edge last */ + lastv1 = verts[f->len - 1]; + lastv2 = verts[0]; + /* v1 = verts[0]; */ /* UNUSED */ + /* v2 = verts[1]; */ /* UNUSED */ + for (i = 0; i < f->len - 1; i++) { + e = BM_edge_create(bm, verts[i], verts[i + 1], NULL, TRUE); + if (!e) { + return NULL; + } + edges_tmp[i] = e; + } + + edges_tmp[i] = BM_edge_create(bm, lastv1, lastv2, NULL, TRUE); + + f2 = BM_face_create(bm, verts, edges_tmp, f->len, FALSE); + if (!f2) { + return NULL; + } + + BM_elem_attrs_copy(bm, bm, f, f2); + + l = BM_iter_new(&liter1, bm, BM_LOOPS_OF_FACE, f); + l2 = BM_iter_new(&liter2, bm, BM_LOOPS_OF_FACE, f2); + for ( ; l && l2; l = BM_iter_step(&liter1), l2 = BM_iter_step(&liter2)) { + BM_elem_attrs_copy(bm, bm, l, l2); + if (l->e != l2->e) { + /* set up data for figuring out the two sides of + * the split */ + + /* set edges index as dirty after running all */ + BM_elem_index_set(l2->e, BM_elem_index_get(l->e)); /* set_dirty! */ + et = &etags[BM_elem_index_get(l->e)]; + + if (!et->newe1) { + et->newe1 = l2->e; + } + else if (!et->newe2) { + et->newe2 = l2->e; + } + else { + /* Only two new edges should be created from each original edge + * for edge split operation */ + + //BLI_assert(et->newe1 == l2->e || et->newe2 == l2->e); + et->newe2 = l2->e; + } + + if (BMO_elem_flag_test(bm, l->e, EDGE_SEAM)) { + BMO_elem_flag_enable(bm, l2->e, EDGE_SEAM); + } + + BM_elem_attrs_copy(bm, bm, l->e, l2->e); + } + + BMO_elem_flag_enable(bm, l->e, EDGE_MARK); + BMO_elem_flag_enable(bm, l2->e, EDGE_MARK); + } + + return f2; +} + +static void tag_out_edges(BMesh *bm, EdgeTag *etags, BMOperator *UNUSED(op)) +{ + EdgeTag *et; + BMIter iter; + BMLoop *l, *startl; + BMEdge *e; + BMVert *v; + int i, ok; + + ok = 0; + while (ok++ < 100000) { + BM_ITER(e, &iter, bm, BM_EDGES_OF_MESH, NULL) { + if (!BMO_elem_flag_test(bm, e, EDGE_SEAM)) + continue; + + et = &etags[BM_elem_index_get(e)]; + if (!et->tag && e->l) { + break; + } + } + + if (!e) { + break; + } + + /* ok we found an edge, part of a region of splits we need + * to identify. now walk along it */ + for (i = 0; i < 2; i++) { + l = e->l; + + v = i ? l->next->v : l->v; + + while (1) { + et = &etags[BM_elem_index_get(l->e)]; + if (et->newe1 == l->e) { + if (et->newe1) { + BMO_elem_flag_enable(bm, et->newe1, EDGE_RET1); + BMO_elem_flag_disable(bm, et->newe1, EDGE_SEAM); + } + if (et->newe2) { + BMO_elem_flag_enable(bm, et->newe2, EDGE_RET2); + BMO_elem_flag_disable(bm, et->newe2, EDGE_SEAM); + } + } + else { + if (et->newe1) { + BMO_elem_flag_enable(bm, et->newe1, EDGE_RET2); + BMO_elem_flag_disable(bm, et->newe1, EDGE_SEAM); + } + if (et->newe2) { + BMO_elem_flag_enable(bm, et->newe2, EDGE_RET1); + BMO_elem_flag_disable(bm, et->newe2, EDGE_SEAM); + } + } + + /* If the original edge was non-manifold edges, then it is + * possible l->e is not et->newe1 or et->newe2. So always clear + * the flag on l->e as well, to prevent infinite looping. */ + BMO_elem_flag_disable(bm, l->e, EDGE_SEAM); + + startl = l; + do { + l = BM_face_other_loop(l->e, l->f, v); + if (l == startl || BM_edge_face_count(l->e) != 2) { + break; + } + l = l->radial_next; + } while (l != startl && !BMO_elem_flag_test(bm, l->e, EDGE_SEAM)); + + if (l == startl || !BMO_elem_flag_test(bm, l->e, EDGE_SEAM)) { + break; + } + + v = (l->v == v) ? l->next->v : l->v; + } + } + } +} + +void bmesh_edgesplitop_exec(BMesh *bm, BMOperator *op) +{ + EdgeTag *etags, *et; + BMIter iter, liter; + BMOIter siter; + BMFace *f, *f2; + BMLoop *l, *nextl, *prevl, *l2, *l3; + BMEdge *e, *e2; + BMVert *v, *v2, **verts = NULL; + BLI_array_declare(verts); + BMEdge **edges_tmp = NULL; + BLI_array_declare(edges_tmp); + int i, j; + + BMO_slot_buffer_flag_enable(bm, op, "edges", EDGE_SEAM, BM_EDGE); + + /* single marked edges unconnected to any other marked edges + * are illegal, go through and unmark them */ + BMO_ITER(e, &siter, bm, op, "edges", BM_EDGE) { + for (i = 0; i < 2; i++) { + BM_ITER(e2, &iter, bm, BM_EDGES_OF_VERT, i ? e->v2 : e->v1) { + if (e != e2 && BMO_elem_flag_test(bm, e2, EDGE_SEAM)) { + break; + } + } + if (e2) { + break; + } + } + + if (!e2) { + BMO_elem_flag_disable(bm, e, EDGE_SEAM); + } + } + + etags = MEM_callocN(sizeof(EdgeTag)*bm->totedge, "EdgeTag"); + + BM_mesh_elem_index_ensure(bm, BM_EDGE); + +#ifdef ETV +# undef ETV +#endif +#ifdef SETETV +# undef SETETV +#endif + +#define ETV(et, v, l) (l->e->v1 == v ? et->newv1 : et->newv2) +#define SETETV(et, v, l, vs) l->e->v1 == v ? (et->newv1 = vs) : (et->newv2 = vs) + + BM_ITER(f, &iter, bm, BM_FACES_OF_MESH, NULL) { + + if (BMO_elem_flag_test(bm, f, FACE_NEW)) { + continue; + } + + BLI_array_empty(verts); + BLI_array_growitems(verts, f->len); + memset(verts, 0, sizeof(BMVert *) * f->len); + + /* this is passed onto remake_face() so it doesnt need to allocate + * a new array on each call. */ + BLI_array_empty(edges_tmp); + BLI_array_growitems(edges_tmp, f->len); + + i = 0; + BM_ITER(l, &liter, bm, BM_LOOPS_OF_FACE, f) { + if (!BMO_elem_flag_test(bm, l->e, EDGE_SEAM)) { + if (!verts[i]) { + + et = &etags[BM_elem_index_get(l->e)]; + if (ETV(et, l->v, l)) { + verts[i] = ETV(et, l->v, l); + } + else + { + verts[i] = l->v; + } + } + i++; + continue; + } + + nextl = l->next; + prevl = l->prev; + + for (j = 0; j < 2; j++) { + /* correct as long as i & j dont change during the loop */ + const int fv_index = j ? (i + 1) % f->len : i; /* face vert index */ + l2 = j ? nextl : prevl; + v = j ? l2->v : l->v; + + if (BMO_elem_flag_test(bm, l2->e, EDGE_SEAM)) { + if (verts[fv_index] == NULL) { + /* make unique vert here for this face only */ + v2 = BM_vert_create(bm, v->co, v); + verts[fv_index] = v2; + } + else { + v2 = verts[fv_index]; + } + } + else { + /* generate unique vert for non-seam edge(s) + * around the manifold vert fan if necassary */ + + /* first check that we have two seam edges + * somewhere within this fa */ + l3 = l2; + do { + if (BM_edge_face_count(l3->e) != 2) { + /* if we hit a boundary edge, tag + * l3 as null so we know to disconnect + * it */ + if (BM_edge_face_count(l3->e) == 1) { + l3 = NULL; + } + break; + } + + l3 = l3->radial_next; + l3 = BM_face_other_loop(l3->e, l3->f, v); + } while (l3 != l2 && !BMO_elem_flag_test(bm, l3->e, EDGE_SEAM)); + + if (l3 == NULL || (BMO_elem_flag_test(bm, l3->e, EDGE_SEAM) && l3->e != l->e)) { + et = &etags[BM_elem_index_get(l2->e)]; + if (ETV(et, v, l2) == NULL) { + v2 = BM_vert_create(bm, v->co, v); + + l3 = l2; + do { + SETETV(et, v, l3, v2); + if (BM_edge_face_count(l3->e) != 2) { + break; + } + + l3 = l3->radial_next; + l3 = BM_face_other_loop(l3->e, l3->f, v); + + et = &etags[BM_elem_index_get(l3->e)]; + } while (l3 != l2 && !BMO_elem_flag_test(bm, l3->e, EDGE_SEAM)); + } + else { + v2 = ETV(et, v, l2); + } + + verts[fv_index] = v2; + } + else { + verts[fv_index] = v; + } + } + } + + i++; + } + + /* debugging code, quick way to find the face/vert combination + * which is failing assuming quads start planer - campbell */ +#if 0 + if (f->len == 4) { + float no1[3]; + float no2[3]; + float angle_error; + printf(" ** found QUAD\n"); + normal_tri_v3(no1, verts[0]->co, verts[1]->co, verts[2]->co); + normal_tri_v3(no2, verts[0]->co, verts[2]->co, verts[3]->co); + if ((angle_error = angle_v3v3(no1, no2)) > 0.05) { + printf(" ERROR %.4f\n", angle_error); + print_v3("0", verts[0]->co); + print_v3("1", verts[1]->co); + print_v3("2", verts[2]->co); + print_v3("3", verts[3]->co); + + } + } + else { + printf(" ** fount %d len face\n", f->len); + } +#endif + + f2 = remake_face(bm, etags, f, verts, edges_tmp); + if (!f2) { + continue; + } + + BMO_elem_flag_enable(bm, f, FACE_DEL); + BMO_elem_flag_enable(bm, f2, FACE_NEW); + } + + /* remake_face() sets invalid indecies, + * likely these will be corrected on operator exit anyway */ + bm->elem_index_dirty &= ~BM_EDGE; + + /* cant call the operator because 'tag_out_edges' + * relies on original index values, from before editing geometry */ + +#if 0 + BMO_op_callf(bm, "del geom=%ff context=%i", FACE_DEL, DEL_ONLYFACES); +#else + BMO_remove_tagged_context(bm, FACE_DEL, DEL_ONLYFACES); +#endif + + /* test EDGE_MARK'd edges if we need to delete them, EDGE_MARK + * is set in remake_face */ + BM_ITER(e, &iter, bm, BM_EDGES_OF_MESH, NULL) { + if (BMO_elem_flag_test(bm, e, EDGE_MARK)) { + if (!e->l) { + BMO_elem_flag_enable(bm, e, EDGE_DEL); + } + } + } + +#if 0 + BMO_op_callf(bm, "del geom=%fe context=%i", EDGE_DEL, DEL_EDGES); +#else + BMO_remove_tagged_context(bm, EDGE_DEL, DEL_EDGES); +#endif + + tag_out_edges(bm, etags, op); + BMO_slot_from_flag(bm, op, "edgeout1", EDGE_RET1, BM_EDGE); + BMO_slot_from_flag(bm, op, "edgeout2", EDGE_RET2, BM_EDGE); + + BLI_array_free(verts); + BLI_array_free(edges_tmp); + if (etags) MEM_freeN(etags); +} + +#undef ETV +#undef SETETV diff --git a/source/blender/bmesh/operators/bmo_extrude.c b/source/blender/bmesh/operators/bmo_extrude.c new file mode 100644 index 00000000000..b6a87e604ec --- /dev/null +++ b/source/blender/bmesh/operators/bmo_extrude.c @@ -0,0 +1,591 @@ +/* + * ***** 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_array.h" + +#include "bmesh.h" + +#include "bmesh_operators_private.h" /* own include */ + +#define EXT_INPUT 1 +#define EXT_KEEP 2 +#define EXT_DEL 4 + +#define VERT_MARK 1 +#define EDGE_MARK 1 +#define FACE_MARK 1 +#define VERT_NONMAN 2 +#define EDGE_NONMAN 2 + +void bmesh_extrude_face_indiv_exec(BMesh *bm, BMOperator *op) +{ + BMOIter siter; + BMIter liter, liter2; + BMFace *f, *f2, *f3; + BMLoop *l, *l2, *l3, *l4, *l_tmp; + BMEdge **edges = NULL, *e, *laste; + BMVert *v, *lastv, *firstv; + BLI_array_declare(edges); + int i; + + BMO_ITER(f, &siter, bm, op, "faces", BM_FACE) { + BLI_array_empty(edges); + i = 0; + firstv = lastv = NULL; + BM_ITER(l, &liter, bm, BM_LOOPS_OF_FACE, f) { + BLI_array_growone(edges); + + v = BM_vert_create(bm, l->v->co, l->v); + + if (lastv) { + e = BM_edge_create(bm, lastv, v, l->e, FALSE); + edges[i++] = e; + } + + lastv = v; + laste = l->e; + if (!firstv) firstv = v; + } + + BLI_array_growone(edges); + e = BM_edge_create(bm, v, firstv, laste, FALSE); + edges[i++] = e; + + BMO_elem_flag_enable(bm, f, EXT_DEL); + + f2 = BM_face_create_ngon(bm, firstv, BM_edge_other_vert(edges[0], firstv), edges, f->len, FALSE); + if (!f2) { + BMO_error_raise(bm, op, BMERR_MESH_ERROR, "Extrude failed; could not create face"); + BLI_array_free(edges); + return; + } + + BMO_elem_flag_enable(bm, f2, EXT_KEEP); + BM_elem_attrs_copy(bm, bm, f, f2); + + l2 = BM_iter_new(&liter2, bm, BM_LOOPS_OF_FACE, f2); + BM_ITER(l, &liter, bm, BM_LOOPS_OF_FACE, f) { + BM_elem_attrs_copy(bm, bm, l, l2); + + l3 = l->next; + l4 = l2->next; + + f3 = BM_face_create_quad_tri(bm, l3->v, l4->v, l2->v, l->v, f, FALSE); + + l_tmp = BM_FACE_FIRST_LOOP(f3); + + BM_elem_attrs_copy(bm, bm, l->next, l_tmp); l_tmp = l_tmp->next; + BM_elem_attrs_copy(bm, bm, l->next, l_tmp); l_tmp = l_tmp->next; + BM_elem_attrs_copy(bm, bm, l, l_tmp); l_tmp = l_tmp->next; + BM_elem_attrs_copy(bm, bm, l, l_tmp); + + l2 = BM_iter_step(&liter2); + } + } + + BLI_array_free(edges); + + BMO_op_callf(bm, "del geom=%ff context=%d", EXT_DEL, DEL_ONLYFACES); + BMO_slot_from_flag(bm, op, "faceout", EXT_KEEP, BM_FACE); +} + +void bmesh_extrude_onlyedge_exec(BMesh *bm, BMOperator *op) +{ + BMOIter siter; + BMOperator dupeop; + BMVert *v1, *v2, *v3, *v4; + BMEdge *e, *e2; + BMFace *f; + + BMO_ITER(e, &siter, bm, op, "edges", BM_EDGE) { + BMO_elem_flag_enable(bm, e, EXT_INPUT); + BMO_elem_flag_enable(bm, e->v1, EXT_INPUT); + BMO_elem_flag_enable(bm, e->v2, EXT_INPUT); + } + + BMO_op_initf(bm, &dupeop, "dupe geom=%fve", EXT_INPUT); + BMO_op_exec(bm, &dupeop); + + e = BMO_iter_new(&siter, bm, &dupeop, "boundarymap", 0); + for ( ; e; e = BMO_iter_step(&siter)) { + e2 = BMO_iter_map_value(&siter); + e2 = *(BMEdge **)e2; + + if (e->l && e->v1 != e->l->v) { + v1 = e->v1; + v2 = e->v2; + v3 = e2->v2; + v4 = e2->v1; + } + else { + v1 = e2->v1; + v2 = e2->v2; + v3 = e->v2; + v4 = e->v1; + } + /* not sure what to do about example face, pass NULL for now */ + f = BM_face_create_quad_tri(bm, v1, v2, v3, v4, NULL, FALSE); + + if (BMO_elem_flag_test(bm, e, EXT_INPUT)) + e = e2; + + BMO_elem_flag_enable(bm, f, EXT_KEEP); + BMO_elem_flag_enable(bm, e, EXT_KEEP); + BMO_elem_flag_enable(bm, e->v1, EXT_KEEP); + BMO_elem_flag_enable(bm, e->v2, EXT_KEEP); + + } + + BMO_op_finish(bm, &dupeop); + + BMO_slot_from_flag(bm, op, "geomout", EXT_KEEP, BM_ALL); +} + +void extrude_vert_indiv_exec(BMesh *bm, BMOperator *op) +{ + BMOIter siter; + BMVert *v, *dupev; + BMEdge *e; + + v = BMO_iter_new(&siter, bm, op, "verts", BM_VERT); + for ( ; v; v = BMO_iter_step(&siter)) { + dupev = BM_vert_create(bm, v->co, v); + + e = BM_edge_create(bm, v, dupev, NULL, FALSE); + + BMO_elem_flag_enable(bm, e, EXT_KEEP); + BMO_elem_flag_enable(bm, dupev, EXT_KEEP); + } + + BMO_slot_from_flag(bm, op, "vertout", EXT_KEEP, BM_VERT); + BMO_slot_from_flag(bm, op, "edgeout", EXT_KEEP, BM_EDGE); +} + +void extrude_edge_context_exec(BMesh *bm, BMOperator *op) +{ + BMOperator dupeop, delop; + BMOIter siter; + BMIter iter, fiter, viter; + BMEdge *e, *newedge; + BMLoop *l, *l2; + BMVert *verts[4], *v, *v2; + BMFace *f; + int rlen, found, fwd, delorig = 0; + + /* initialize our sub-operators */ + BMO_op_init(bm, &dupeop, "dupe"); + + BMO_slot_buffer_flag_enable(bm, op, "edgefacein", EXT_INPUT, BM_EDGE|BM_FACE); + + /* if one flagged face is bordered by an unflagged face, then we delete + * original geometry unless caller explicitly asked to keep it. */ + if (!BMO_slot_int_get(op, "alwayskeeporig")) { + BM_ITER(e, &iter, bm, BM_EDGES_OF_MESH, NULL) { + if (!BMO_elem_flag_test(bm, e, EXT_INPUT)) continue; + + found = 0; + f = BM_iter_new(&fiter, bm, BM_FACES_OF_EDGE, e); + for (rlen = 0; f; f = BM_iter_step(&fiter), rlen++) { + if (!BMO_elem_flag_test(bm, f, EXT_INPUT)) { + found = 1; + delorig = 1; + break; + } + } + + if (!found && (rlen > 1)) BMO_elem_flag_enable(bm, e, EXT_DEL); + } + } + + /* calculate verts to delet */ + BM_ITER(v, &iter, bm, BM_VERTS_OF_MESH, NULL) { + found = 0; + + BM_ITER(e, &viter, bm, BM_EDGES_OF_VERT, v) { + if (!BMO_elem_flag_test(bm, e, EXT_INPUT) || !BMO_elem_flag_test(bm, e, EXT_DEL)) { + found = 1; + break; + } + } + + BM_ITER(f, &viter, bm, BM_FACES_OF_VERT, v) { + if (!BMO_elem_flag_test(bm, f, EXT_INPUT)) { + found = 1; + break; + } + } + + if (!found) { + BMO_elem_flag_enable(bm, v, EXT_DEL); + } + } + + BM_ITER(f, &iter, bm, BM_FACES_OF_MESH, NULL) { + if (BMO_elem_flag_test(bm, f, EXT_INPUT)) + BMO_elem_flag_enable(bm, f, EXT_DEL); + } + + if (delorig) { + BMO_op_initf(bm, &delop, "del geom=%fvef context=%d", + EXT_DEL, DEL_ONLYTAGGED); + } + + BMO_slot_copy(op, &dupeop, "edgefacein", "geom"); + BMO_op_exec(bm, &dupeop); + + if (bm->act_face && BMO_elem_flag_test(bm, bm->act_face, EXT_INPUT)) + bm->act_face = BMO_slot_map_ptr_get(bm, &dupeop, "facemap", bm->act_face); + + if (delorig) BMO_op_exec(bm, &delop); + + /* if not delorig, reverse loops of original face */ + if (!delorig) { + for (f = BM_iter_new(&iter, bm, BM_FACES_OF_MESH, NULL); f; f = BM_iter_step(&iter)) { + if (BMO_elem_flag_test(bm, f, EXT_INPUT)) { + BM_face_normal_flip(bm, f); + } + } + } + + BMO_slot_copy(&dupeop, op, "newout", "geomout"); + e = BMO_iter_new(&siter, bm, &dupeop, "boundarymap", 0); + for ( ; e; e = BMO_iter_step(&siter)) { + if (BMO_slot_map_contains(bm, op, "exclude", e)) continue; + + newedge = BMO_iter_map_value(&siter); + newedge = *(BMEdge **)newedge; + if (!newedge) continue; + + /* orient loop to give same normal as a loop of newedge + * if it exists (will be an extruded face), + * else same normal as a loop of e, if it exists */ + if (!newedge->l) + fwd = !e->l || !(e->l->v == e->v1); + else + fwd = (newedge->l->v == newedge->v1); + + + if (fwd) { + verts[0] = e->v1; + verts[1] = e->v2; + verts[2] = newedge->v2; + verts[3] = newedge->v1; + } + else { + verts[3] = e->v1; + verts[2] = e->v2; + verts[1] = newedge->v2; + verts[0] = newedge->v1; + } + + /* not sure what to do about example face, pass NULL for now */ + f = BM_face_create_quad_tri_v(bm, verts, 4, NULL, FALSE); + + /* copy attribute */ + l = BM_iter_new(&iter, bm, BM_LOOPS_OF_FACE, f); + for ( ; l; l = BM_iter_step(&iter)) { + if (l->e != e && l->e != newedge) continue; + l2 = l->radial_next; + + if (l2 == l) { + l2 = newedge->l; + BM_elem_attrs_copy(bm, bm, l2->f, l->f); + + BM_elem_attrs_copy(bm, bm, l2, l); + l2 = l2->next; + l = l->next; + BM_elem_attrs_copy(bm, bm, l2, l); + } + else { + BM_elem_attrs_copy(bm, bm, l2->f, l->f); + + /* copy dat */ + if (l2->v == l->v) { + BM_elem_attrs_copy(bm, bm, l2, l); + l2 = l2->next; + l = l->next; + BM_elem_attrs_copy(bm, bm, l2, l); + } + else { + l2 = l2->next; + BM_elem_attrs_copy(bm, bm, l2, l); + l2 = l2->prev; + l = l->next; + BM_elem_attrs_copy(bm, bm, l2, l); + } + } + } + } + + /* link isolated vert */ + v = BMO_iter_new(&siter, bm, &dupeop, "isovertmap", 0); + for ( ; v; v = BMO_iter_step(&siter)) { + v2 = *((void **)BMO_iter_map_value(&siter)); + BM_edge_create(bm, v, v2, v->e, TRUE); + } + + /* cleanu */ + if (delorig) BMO_op_finish(bm, &delop); + BMO_op_finish(bm, &dupeop); +} + +/* + * Compute higher-quality vertex normals used by solidify. + * Only considers geometry in the marked solidify region. + * Note that this does not work so well for non-manifold + * regions. + */ +static void calc_solidify_normals(BMesh *bm) +{ + BMIter viter, eiter, fiter; + BMVert *v; + BMEdge *e; + BMFace *f, *f1, *f2; + float edge_normal[3]; + int i; + + /* can't use BM_edge_face_count because we need to count only marked faces */ + int *edge_face_count = MEM_callocN(sizeof(int) * bm->totedge, __func__); + + BM_ITER(v, &viter, bm, BM_VERTS_OF_MESH, NULL) { + BM_elem_flag_enable(v, BM_ELEM_TAG); + } + + BM_mesh_elem_index_ensure(bm, BM_EDGE); + + BM_ITER(f, &fiter, bm, BM_FACES_OF_MESH, NULL) { + if (!BMO_elem_flag_test(bm, f, FACE_MARK)) { + continue; + } + + BM_ITER(e, &eiter, bm, BM_EDGES_OF_FACE, f) { + + /* And mark all edges and vertices on the + * marked faces */ + BMO_elem_flag_enable(bm, e, EDGE_MARK); + BMO_elem_flag_enable(bm, e->v1, VERT_MARK); + BMO_elem_flag_enable(bm, e->v2, VERT_MARK); + edge_face_count[BM_elem_index_get(e)]++; + } + } + + BM_ITER(e, &eiter, bm, BM_EDGES_OF_MESH, NULL) { + if (!BMO_elem_flag_test(bm, e, EDGE_MARK)) { + continue; + } + + i = edge_face_count[BM_elem_index_get(e)]++; + + if (i == 0 || i > 2) { + /* Edge & vertices are non-manifold even when considering + * only marked faces */ + BMO_elem_flag_enable(bm, e, EDGE_NONMAN); + BMO_elem_flag_enable(bm, e->v1, VERT_NONMAN); + BMO_elem_flag_enable(bm, e->v2, VERT_NONMAN); + } + } + MEM_freeN(edge_face_count); + edge_face_count = NULL; /* dont re-use */ + + BM_ITER(v, &viter, bm, BM_VERTS_OF_MESH, NULL) { + if (!BM_vert_is_manifold(bm, v)) { + BMO_elem_flag_enable(bm, v, VERT_NONMAN); + continue; + } + + if (BMO_elem_flag_test(bm, v, VERT_MARK)) { + zero_v3(v->no); + } + } + + BM_ITER(e, &eiter, bm, BM_EDGES_OF_MESH, NULL) { + + /* If the edge is not part of a the solidify region + * its normal should not be considered */ + if (!BMO_elem_flag_test(bm, e, EDGE_MARK)) { + continue; + } + + /* If the edge joins more than two marked faces high + * quality normal computation won't work */ + if (BMO_elem_flag_test(bm, e, EDGE_NONMAN)) { + continue; + } + + f1 = f2 = NULL; + + BM_ITER(f, &fiter, bm, BM_FACES_OF_EDGE, e) { + if (BMO_elem_flag_test(bm, f, FACE_MARK)) { + if (f1 == NULL) { + f1 = f; + } + else { + BLI_assert(f2 == NULL); + f2 = f; + } + } + } + + BLI_assert(f1 != NULL); + + if (f2 != NULL) { + const float angle = angle_normalized_v3v3(f1->no, f2->no); + + if (angle > 0.0f) { + /* two faces using this edge, calculate the edge normal + * using the angle between the faces as a weighting */ + add_v3_v3v3(edge_normal, f1->no, f2->no); + normalize_v3(edge_normal); + mul_v3_fl(edge_normal, angle); + } + else { + /* can't do anything useful here! + * Set the face index for a vert incase it gets a zero normal */ + BM_elem_flag_disable(e->v1, BM_ELEM_TAG); + BM_elem_flag_disable(e->v2, BM_ELEM_TAG); + continue; + } + } + else { + /* only one face attached to that edge */ + /* an edge without another attached- the weight on this is + * undefined, M_PI / 2 is 90d in radians and that seems good enough */ + copy_v3_v3(edge_normal, f1->no); + mul_v3_fl(edge_normal, M_PI / 2); + } + + add_v3_v3(e->v1->no, edge_normal); + add_v3_v3(e->v2->no, edge_normal); + } + + /* normalize accumulated vertex normal */ + BM_ITER(v, &viter, bm, BM_VERTS_OF_MESH, NULL) { + if (!BMO_elem_flag_test(bm, v, VERT_MARK)) { + continue; + } + + if (BMO_elem_flag_test(bm, v, VERT_NONMAN)) { + /* use standard normals for vertices connected to non-manifold edges */ + BM_vert_normal_update(bm, v); + } + else if (normalize_v3(v->no) == 0.0f && !BM_elem_flag_test(v, BM_ELEM_TAG)) { + /* exceptional case, totally flat. use the normal + * of any marked face around the vertex */ + BM_ITER(f, &fiter, bm, BM_FACES_OF_VERT, v) { + if (BMO_elem_flag_test(bm, f, FACE_MARK)) { + break; + } + } + copy_v3_v3(v->no, f->no); + } + } +} + +static void solidify_add_thickness(BMesh *bm, float dist) +{ + BMFace *f; + BMVert *v; + BMLoop *l; + BMIter iter, loopIter; + float *vert_angles = MEM_callocN(sizeof(float) * bm->totvert * 2, "solidify"); /* 2 in 1 */ + float *vert_accum = vert_angles + bm->totvert; + float angle; + int i, index; + float maxdist = dist * sqrtf(3.0f); + + /* array for passing verts to angle_poly_v3 */ + float **verts = NULL; + BLI_array_staticdeclare(verts, BM_NGON_STACK_SIZE); + /* array for receiving angles from angle_poly_v3 */ + float *angles = NULL; + BLI_array_staticdeclare(angles, BM_NGON_STACK_SIZE); + + BM_mesh_elem_index_ensure(bm, BM_VERT); + + BM_ITER(f, &iter, bm, BM_FACES_OF_MESH, NULL) { + if (!BMO_elem_flag_test(bm, f, FACE_MARK)) { + continue; + } + + BM_ITER(l, &loopIter, bm, BM_LOOPS_OF_FACE, f) { + BLI_array_append(verts, l->v->co); + BLI_array_growone(angles); + } + + angle_poly_v3(angles, (const float **)verts, f->len); + + i = 0; + BM_ITER(l, &loopIter, bm, BM_LOOPS_OF_FACE, f) { + v = l->v; + index = BM_elem_index_get(v); + angle = angles[i]; + vert_accum[index] += angle; + vert_angles[index] += shell_angle_to_dist(angle_normalized_v3v3(v->no, f->no)) * angle; + i++; + } + + BLI_array_empty(verts); + BLI_array_empty(angles); + } + + BM_ITER(v, &iter, bm, BM_VERTS_OF_MESH, NULL) { + index = BM_elem_index_get(v); + if (vert_accum[index]) { /* zero if unselected */ + float vdist = MIN2(maxdist, dist * vert_angles[index] / vert_accum[index]); + madd_v3_v3fl(v->co, v->no, vdist); + } + } + + MEM_freeN(vert_angles); +} + +void bmesh_solidify_face_region_exec(BMesh *bm, BMOperator *op) +{ + BMOperator extrudeop; + BMOperator reverseop; + float thickness; + + thickness = BMO_slot_float_get(op, "thickness"); + + /* Flip original faces (so the shell is extruded inward) */ + BMO_op_init(bm, &reverseop, "reversefaces"); + BMO_slot_copy(op, &reverseop, "geom", "faces"); + BMO_op_exec(bm, &reverseop); + BMO_op_finish(bm, &reverseop); + + /* Extrude the region */ + BMO_op_initf(bm, &extrudeop, "extrudefaceregion alwayskeeporig=%i", TRUE); + BMO_slot_copy(op, &extrudeop, "geom", "edgefacein"); + BMO_op_exec(bm, &extrudeop); + + /* Push the verts of the extruded faces inward to create thickness */ + BMO_slot_buffer_flag_enable(bm, &extrudeop, "geomout", FACE_MARK, BM_FACE); + calc_solidify_normals(bm); + solidify_add_thickness(bm, thickness); + + BMO_slot_copy(&extrudeop, op, "geomout", "geomout"); + + BMO_op_finish(bm, &extrudeop); +} diff --git a/source/blender/bmesh/operators/bmo_join_triangles.c b/source/blender/bmesh/operators/bmo_join_triangles.c new file mode 100644 index 00000000000..921c579447b --- /dev/null +++ b/source/blender/bmesh/operators/bmo_join_triangles.c @@ -0,0 +1,373 @@ +/* + * ***** 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 "DNA_meshdata_types.h" + +#include "BKE_customdata.h" + +#include "BLI_math.h" +#include "BLI_array.h" + +#include "bmesh.h" + +#include "bmesh_operators_private.h" /* own include */ + +/* + * JOIN_TRIANGLES.C + * + * utility bmesh operators, e.g. transform, + * translate, rotate, scale, etc. + * + */ + +/* Bitflags for edges */ +#define T2QDELETE 1 +#define T2QCOMPLEX 2 +#define T2QJOIN 4 + +/* assumes edges are validated before reaching this poin */ +static float measure_facepair(BMesh *UNUSED(bm), BMVert *v1, BMVert *v2, + BMVert *v3, BMVert *v4, float limit) +{ + /* gives a 'weight' to a pair of triangles that join an edge to decide how good a join they would mak */ + /* Note: this is more complicated than it needs to be and should be cleaned up.. */ + float n1[3], n2[3], measure = 0.0f, angle1, angle2, diff; + float edgeVec1[3], edgeVec2[3], edgeVec3[3], edgeVec4[3]; + float minarea, maxarea, areaA, areaB; + + /* First Test: Normal differenc */ + normal_tri_v3(n1, v1->co, v2->co, v3->co); + normal_tri_v3(n2, v1->co, v3->co, v4->co); + + if (n1[0] == n2[0] && n1[1] == n2[1] && n1[2] == n2[2]) angle1 = 0.0f; + else angle1 = angle_v3v3(n1, n2); + + normal_tri_v3(n1, v2->co, v3->co, v4->co); + normal_tri_v3(n2, v4->co, v1->co, v2->co); + + if (n1[0] == n2[0] && n1[1] == n2[1] && n1[2] == n2[2]) angle2 = 0.0f; + else angle2 = angle_v3v3(n1, n2); + + measure += (angle1 + angle2) * 0.5f; + if (measure > limit) { + return measure; + } + + /* Second test: Colinearit */ + sub_v3_v3v3(edgeVec1, v1->co, v2->co); + sub_v3_v3v3(edgeVec2, v2->co, v3->co); + sub_v3_v3v3(edgeVec3, v3->co, v4->co); + sub_v3_v3v3(edgeVec4, v4->co, v1->co); + + /* a completely skinny face is 'pi' after halving */ + diff = 0.25f * (fabsf(angle_v3v3(edgeVec1, edgeVec2) - (float)M_PI_2) + + fabsf(angle_v3v3(edgeVec2, edgeVec3) - (float)M_PI_2) + + fabsf(angle_v3v3(edgeVec3, edgeVec4) - (float)M_PI_2) + + fabsf(angle_v3v3(edgeVec4, edgeVec1) - (float)M_PI_2)); + + if (!diff) { + return 0.0; + } + + measure += diff; + if (measure > limit) { + return measure; + } + + /* Third test: Concavit */ + areaA = area_tri_v3(v1->co, v2->co, v3->co) + area_tri_v3(v1->co, v3->co, v4->co); + areaB = area_tri_v3(v2->co, v3->co, v4->co) + area_tri_v3(v4->co, v1->co, v2->co); + + if (areaA <= areaB) minarea = areaA; + else minarea = areaB; + + if (areaA >= areaB) maxarea = areaA; + else maxarea = areaB; + + if (!maxarea) measure += 1; + else measure += (1 - (minarea / maxarea)); + + return measure; +} + +#define T2QUV_LIMIT 0.005f +#define T2QCOL_LIMIT 3 + +static int compareFaceAttribs(BMesh *bm, BMEdge *e, int douvs, int dovcols) +{ + MTexPoly *tp1, *tp2; + MLoopCol *lcol1, *lcol2, *lcol3, *lcol4; + MLoopUV *luv1, *luv2, *luv3, *luv4; + BMLoop *l1, *l2, *l3, *l4; + int mergeok_uvs = !douvs, mergeok_vcols = !dovcols; + + l1 = e->l; + l3 = e->l->radial_next; + + /* match up loops on each side of an edge corrusponding to each ver */ + if (l1->v == l3->v) { + l2 = l1->next; + l4 = l2->next; + } + else { + l2 = l1->next; + + l4 = l3; + l3 = l4->next; + } + + lcol1 = CustomData_bmesh_get(&bm->ldata, l1->head.data, CD_MLOOPCOL); + lcol2 = CustomData_bmesh_get(&bm->ldata, l1->head.data, CD_MLOOPCOL); + lcol3 = CustomData_bmesh_get(&bm->ldata, l1->head.data, CD_MLOOPCOL); + lcol4 = CustomData_bmesh_get(&bm->ldata, l1->head.data, CD_MLOOPCOL); + + luv1 = CustomData_bmesh_get(&bm->ldata, l1->head.data, CD_MLOOPUV); + luv2 = CustomData_bmesh_get(&bm->ldata, l1->head.data, CD_MLOOPUV); + luv3 = CustomData_bmesh_get(&bm->ldata, l1->head.data, CD_MLOOPUV); + luv4 = CustomData_bmesh_get(&bm->ldata, l1->head.data, CD_MLOOPUV); + + tp1 = CustomData_bmesh_get(&bm->pdata, l1->f->head.data, CD_MTEXPOLY); + tp2 = CustomData_bmesh_get(&bm->pdata, l2->f->head.data, CD_MTEXPOLY); + + if (!lcol1) + mergeok_vcols = 1; + + if (!luv1) + mergeok_uvs = 1; + + /* compare faceedges for each face attribute. Additional per face attributes can be added late */ + + /* do VCOL */ + if (lcol1 && dovcols) { + char *cols[4] = {(char *)lcol1, (char *)lcol2, (char *)lcol3, (char *)lcol4}; + int i; + + for (i = 0; i < 3; i++) { + if (cols[0][i] + T2QCOL_LIMIT < cols[2][i] - T2QCOL_LIMIT) + break; + if (cols[1][i] + T2QCOL_LIMIT < cols[3][i] - T2QCOL_LIMIT) + break; + } + + if (i == 3) + mergeok_vcols = 1; + } + + /* do UV */ + if (luv1 && douvs) { + if (tp1->tpage != tp2->tpage); /* do nothin */ + else { + int i; + + for (i = 0; i < 2; i++) { + if (luv1->uv[0] + T2QUV_LIMIT > luv3->uv[0] && luv1->uv[0] - T2QUV_LIMIT < luv3->uv[0] && + luv1->uv[1] + T2QUV_LIMIT > luv3->uv[1] && luv1->uv[1] - T2QUV_LIMIT < luv3->uv[1]) + { + if (luv2->uv[0] + T2QUV_LIMIT > luv4->uv[0] && luv2->uv[0] - T2QUV_LIMIT < luv4->uv[0] && + luv2->uv[1] + T2QUV_LIMIT > luv4->uv[1] && luv2->uv[1] - T2QUV_LIMIT < luv4->uv[1]) + { + mergeok_uvs = 1; + } + } + } + } + } + + if (douvs == mergeok_uvs && dovcols == mergeok_vcols) { + return TRUE; + } + + return FALSE; +} + +typedef struct JoinEdge { + float weight; + BMEdge *e; +} JoinEdge; + +#define EDGE_MARK 1 +#define EDGE_CHOSEN 2 + +#define FACE_MARK 1 +#define FACE_INPUT 2 + +static int fplcmp(const void *v1, const void *v2) +{ + const JoinEdge *e1 = (JoinEdge *)v1, *e2 = (JoinEdge *)v2; + + if (e1->weight > e2->weight) return 1; + else if (e1->weight < e2->weight) return -1; + + return 0; +} + +void bmesh_jointriangles_exec(BMesh *bm, BMOperator *op) +{ + BMIter iter, liter; + BMOIter siter; + BMFace *f1, *f2; + BMLoop *l; + BMEdge *e; + BLI_array_declare(jedges); + JoinEdge *jedges = NULL; + int dosharp = BMO_slot_int_get(op, "compare_sharp"), douvs = BMO_slot_int_get(op, "compare_uvs"); + int dovcols = BMO_slot_int_get(op, "compare_vcols"), domat = BMO_slot_int_get(op, "compare_materials"); + float limit = BMO_slot_float_get(op, "limit"); + int i, totedge; + + /* flag all edges of all input face */ + BMO_ITER(f1, &siter, bm, op, "faces", BM_FACE) { + BMO_elem_flag_enable(bm, f1, FACE_INPUT); + BM_ITER(l, &liter, bm, BM_LOOPS_OF_FACE, f1) { + BMO_elem_flag_enable(bm, l->e, EDGE_MARK); + } + } + + /* unflag edges that are invalid; e.g. aren't surrounded by triangle */ + BM_ITER(e, &iter, bm, BM_EDGES_OF_MESH, NULL) { + if (!BMO_elem_flag_test(bm, e, EDGE_MARK)) + continue; + + if (BM_edge_face_count(e) != 2) { + BMO_elem_flag_disable(bm, e, EDGE_MARK); + continue; + } + + f1 = e->l->f; + f2 = e->l->radial_next->f; + + if (f1->len != 3 || f2->len != 3) { + BMO_elem_flag_disable(bm, e, EDGE_MARK); + continue; + } + + if (!BMO_elem_flag_test(bm, f1, FACE_INPUT) || !BMO_elem_flag_test(bm, f2, FACE_INPUT)) { + BMO_elem_flag_disable(bm, e, EDGE_MARK); + continue; + } + } + + i = 0; + BM_ITER(e, &iter, bm, BM_EDGES_OF_MESH, NULL) { + BMVert *v1, *v2, *v3, *v4; + BMFace *f1, *f2; + float measure; + + if (!BMO_elem_flag_test(bm, e, EDGE_MARK)) + continue; + + f1 = e->l->f; + f2 = e->l->radial_next->f; + + v1 = e->l->v; + v2 = e->l->prev->v; + v3 = e->l->next->v; + v4 = e->l->radial_next->prev->v; + + if (dosharp && !BM_elem_flag_test(e, BM_ELEM_SMOOTH)) + continue; + + if ((douvs || dovcols) && compareFaceAttribs(bm, e, douvs, dovcols)) + continue; + + if (domat && f1->mat_nr != f2->mat_nr) + continue; + + measure = measure_facepair(bm, v1, v2, v3, v4, limit); + if (measure < limit) { + BLI_array_growone(jedges); + + jedges[i].e = e; + jedges[i].weight = measure; + + i++; + } + } + + if (!jedges) + return; + + qsort(jedges, BLI_array_count(jedges), sizeof(JoinEdge), fplcmp); + + totedge = BLI_array_count(jedges); + for (i = 0; i < totedge; i++) { + BMFace *f1, *f2; + + e = jedges[i].e; + f1 = e->l->f; + f2 = e->l->radial_next->f; + + if (BMO_elem_flag_test(bm, f1, FACE_MARK) || BMO_elem_flag_test(bm, f2, FACE_MARK)) + continue; + + BMO_elem_flag_enable(bm, f1, FACE_MARK); + BMO_elem_flag_enable(bm, f2, FACE_MARK); + BMO_elem_flag_enable(bm, e, EDGE_CHOSEN); + } + + BM_ITER(e, &iter, bm, BM_EDGES_OF_MESH, NULL) { + if (!BMO_elem_flag_test(bm, e, EDGE_CHOSEN)) + continue; + + f1 = e->l->f; + f2 = e->l->radial_next->f; + + BM_faces_join_pair(bm, f1, f2, e); + } + + BM_ITER(e, &iter, bm, BM_EDGES_OF_MESH, NULL) { + if (BMO_elem_flag_test(bm, e, EDGE_MARK)) { + /* ok, this edge wasn't merged, check if it's + * in a 2-tri-pair island, and if so merg */ + + f1 = e->l->f; + f2 = e->l->radial_next->f; + + if (f1->len != 3 || f2->len != 3) + continue; + + for (i = 0; i < 2; i++) { + BM_ITER(l, &liter, bm, BM_LOOPS_OF_FACE, i ? f2 : f1) { + if (l->e != e && BMO_elem_flag_test(bm, l->e, EDGE_MARK)) { + break; + } + } + + /* if l isn't NULL, we broke out of the loo */ + if (l) { + break; + } + } + + /* if i isn't 2, we broke out of that loo */ + if (i != 2) { + continue; + } + + BM_faces_join_pair(bm, f1, f2, e); + } + } + + BLI_array_free(jedges); +} diff --git a/source/blender/bmesh/operators/bmo_mesh_conv.c b/source/blender/bmesh/operators/bmo_mesh_conv.c new file mode 100644 index 00000000000..4f6db9056e5 --- /dev/null +++ b/source/blender/bmesh/operators/bmo_mesh_conv.c @@ -0,0 +1,906 @@ +/* + * ***** 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 "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" +#include "DNA_object_types.h" +#include "DNA_key_types.h" +#include "DNA_modifier_types.h" + +#include "BKE_mesh.h" +#include "BLI_listbase.h" +#include "BKE_global.h" +#include "BKE_key.h" +#include "BKE_main.h" +#include "BKE_customdata.h" + +#include "BLI_math.h" +#include "BLI_array.h" + +#include "bmesh.h" +#include "bmesh_private.h" + +#include "bmesh_operators_private.h" /* own include */ + +/* + * MESH CONV.C + * + * This file contains functions + * for converting a Mesh + * into a Bmesh, and back again. + * + */ + +void mesh_to_bmesh_exec(BMesh *bm, BMOperator *op) +{ + Object *ob = BMO_slot_ptr_get(op, "object"); + Mesh *me = BMO_slot_ptr_get(op, "mesh"); + MVert *mvert; + BLI_array_declare(verts); + MEdge *medge; + MLoop *ml; + MPoly *mpoly; + KeyBlock *actkey, *block; + BMVert *v, **vt = NULL, **verts = NULL; + BMEdge *e, **fedges = NULL, **et = NULL; + BMFace *f; + BMLoop *l; + BLI_array_declare(fedges); + float (*keyco)[3] = NULL; + int *keyi; + int set_key = BMO_slot_int_get(op, "set_shapekey"); + int totuv, i, j; + + if (!me || !me->totvert) { + return; /* sanity check */ + } + + vt = MEM_mallocN(sizeof(void **) * me->totvert, "mesh to bmesh vtable"); + + CustomData_copy(&me->vdata, &bm->vdata, CD_MASK_BMESH, CD_CALLOC, 0); + CustomData_copy(&me->edata, &bm->edata, CD_MASK_BMESH, CD_CALLOC, 0); + CustomData_copy(&me->ldata, &bm->ldata, CD_MASK_BMESH, CD_CALLOC, 0); + CustomData_copy(&me->pdata, &bm->pdata, CD_MASK_BMESH, CD_CALLOC, 0); + + /* make sure uv layer names are consisten */ + totuv = CustomData_number_of_layers(&bm->pdata, CD_MTEXPOLY); + for (i = 0; i < totuv; i++) { + int li = CustomData_get_layer_index_n(&bm->pdata, CD_MTEXPOLY, i); + CustomData_set_layer_name(&bm->ldata, CD_MLOOPUV, i, bm->pdata.layers[li].name); + } + + if (!CustomData_has_layer(&bm->edata, CD_CREASE)) + CustomData_add_layer(&bm->edata, CD_CREASE, CD_ASSIGN, NULL, 0); + + if (!CustomData_has_layer(&bm->edata, CD_BWEIGHT)) + CustomData_add_layer(&bm->edata, CD_BWEIGHT, CD_ASSIGN, NULL, 0); + + if (!CustomData_has_layer(&bm->vdata, CD_BWEIGHT)) + CustomData_add_layer(&bm->vdata, CD_BWEIGHT, CD_ASSIGN, NULL, 0); + + + if (me->key && ob->shapenr > me->key->totkey) { + ob->shapenr = me->key->totkey - 1; + } + + actkey = ob_get_keyblock(ob); + if (actkey && actkey->totelem == me->totvert) { + CustomData_add_layer(&bm->vdata, CD_SHAPE_KEYINDEX, CD_ASSIGN, NULL, 0); + + /* check if we need to generate unique ids for the shapekeys. + * this also exists in the file reading code, but is here for + * a sanity chec */ + if (!me->key->uidgen) { + fprintf(stderr, + "%s had to generate shape key uid's in a situation we shouldn't need to! " + "(bmesh internal error)\n", + __func__); + + me->key->uidgen = 1; + for (block = me->key->block.first; block; block = block->next) { + block->uid = me->key->uidgen++; + } + } + + keyco = actkey->data; + bm->shapenr = ob->shapenr; + for (i = 0, block = me->key->block.first; block; block = block->next, i++) { + CustomData_add_layer_named(&bm->vdata, CD_SHAPEKEY, + CD_ASSIGN, NULL, 0, block->name); + + j = CustomData_get_layer_index_n(&bm->vdata, CD_SHAPEKEY, i); + bm->vdata.layers[j].uid = block->uid; + } + } + else if (actkey) { + printf("shapekey<->mesh mismatch!\n"); + } + + CustomData_bmesh_init_pool(&bm->vdata, bm_mesh_allocsize_default[0]); + CustomData_bmesh_init_pool(&bm->edata, bm_mesh_allocsize_default[1]); + CustomData_bmesh_init_pool(&bm->ldata, bm_mesh_allocsize_default[2]); + CustomData_bmesh_init_pool(&bm->pdata, bm_mesh_allocsize_default[3]); + + for (i = 0, mvert = me->mvert; i < me->totvert; i++, mvert++) { + v = BM_vert_create(bm, keyco && set_key ? keyco[i] : mvert->co, NULL); + BM_elem_index_set(v, i); /* set_ok */ + vt[i] = v; + + /* transfer flag */ + v->head.hflag = BM_vert_flag_from_mflag(mvert->flag); + + /* this is necassary for selection counts to work properl */ + if (BM_elem_flag_test(v, BM_ELEM_SELECT)) BM_vert_select_set(bm, v, TRUE); + + normal_short_to_float_v3(v->no, mvert->no); + + BM_elem_float_data_set(&bm->vdata, v, CD_BWEIGHT, (float)mvert->bweight / 255.0f); + + /* Copy Custom Dat */ + CustomData_to_bmesh_block(&me->vdata, &bm->vdata, i, &v->head.data); + + /* set shapekey dat */ + if (me->key) { + /* set shape key original inde */ + keyi = CustomData_bmesh_get(&bm->vdata, v->head.data, CD_SHAPE_KEYINDEX); + if (keyi) { + *keyi = i; + } + + for (block = me->key->block.first, j = 0; block; block = block->next, j++) { + float *co = CustomData_bmesh_get_n(&bm->vdata, v->head.data, CD_SHAPEKEY, j); + + if (co) { + copy_v3_v3(co, ((float *)block->data) + 3 * i); + } + } + } + } + + bm->elem_index_dirty &= ~BM_VERT; /* added in order, clear dirty flag */ + + if (!me->totedge) { + MEM_freeN(vt); + return; + } + + et = MEM_mallocN(sizeof(void **) * me->totedge, "mesh to bmesh etable"); + + medge = me->medge; + for (i = 0; i < me->totedge; i++, medge++) { + e = BM_edge_create(bm, vt[medge->v1], vt[medge->v2], NULL, FALSE); + BM_elem_index_set(e, i); /* set_ok */ + et[i] = e; + + /* transfer flags */ + e->head.hflag = BM_edge_flag_from_mflag(medge->flag); + + /* this is necassary for selection counts to work properly */ + if (BM_elem_flag_test(e, BM_ELEM_SELECT)) BM_elem_select_set(bm, e, TRUE); + + /* Copy Custom Dat */ + CustomData_to_bmesh_block(&me->edata, &bm->edata, i, &e->head.data); + + BM_elem_float_data_set(&bm->edata, e, CD_CREASE, (float)medge->crease / 255.0f); + BM_elem_float_data_set(&bm->edata, e, CD_BWEIGHT, (float)medge->bweight / 255.0f); + } + + bm->elem_index_dirty &= ~BM_EDGE; /* added in order, clear dirty flag */ + + mpoly = me->mpoly; + for (i = 0; i < me->totpoly; i++, mpoly++) { + BMIter iter; + + BLI_array_empty(fedges); + BLI_array_empty(verts); + + BLI_array_growitems(fedges, mpoly->totloop); + BLI_array_growitems(verts, mpoly->totloop); + + for (j = 0; j < mpoly->totloop; j++) { + ml = &me->mloop[mpoly->loopstart + j]; + v = vt[ml->v]; + e = et[ml->e]; + + fedges[j] = e; + verts[j] = v; + } + + /* not sure what this block is supposed to do, + * but its unused. so commenting - campbell */ +#if 0 + { + BMVert *v1, *v2; + v1 = vt[me->mloop[mpoly->loopstart].v]; + v2 = vt[me->mloop[mpoly->loopstart + 1].v]; + + if (v1 == fedges[0]->v1) { + v2 = fedges[0]->v2; + } + else { + v1 = fedges[0]->v2; + v2 = fedges[0]->v1; + } + } +#endif + + f = BM_face_create(bm, verts, fedges, mpoly->totloop, FALSE); + + if (!f) { + printf("%s: Warning! Bad face in mesh" + " \"%s\" at index %d!, skipping\n", + __func__, me->id.name + 2, i); + continue; + } + + /* dont use 'i' since we may have skipped the face */ + BM_elem_index_set(f, bm->totface - 1); /* set_ok */ + + /* transfer flag */ + f->head.hflag = BM_face_flag_from_mflag(mpoly->flag); + + /* this is necassary for selection counts to work properl */ + if (BM_elem_flag_test(f, BM_ELEM_SELECT)) BM_elem_select_set(bm, f, TRUE); + + f->mat_nr = mpoly->mat_nr; + if (i == me->act_face) bm->act_face = f; + + j = 0; + BM_ITER_INDEX(l, &iter, bm, BM_LOOPS_OF_FACE, f, j) { + /* Save index of correspsonding MLoop */ + BM_elem_index_set(l, mpoly->loopstart + j); /* set_loop */ + } + + /* Copy Custom Dat */ + CustomData_to_bmesh_block(&me->pdata, &bm->pdata, i, &f->head.data); + } + + bm->elem_index_dirty &= ~BM_FACE; /* added in order, clear dirty flag */ + + { + BMIter fiter; + BMIter liter; + + /* Copy over loop CustomData. Doing this in a separate loop isn't necessary + * but is an optimization, to avoid copying a bunch of interpolated customdata + * for each BMLoop (from previous BMLoops using the same edge), always followed + * by freeing the interpolated data and overwriting it with data from the Mesh. */ + BM_ITER(f, &fiter, bm, BM_FACES_OF_MESH, NULL) { + BM_ITER(l, &liter, bm, BM_LOOPS_OF_FACE, f) { + int li = BM_elem_index_get(l); + CustomData_to_bmesh_block(&me->ldata, &bm->ldata, li, &l->head.data); + BM_elem_index_set(l, 0); /* set_loop */ + } + } + } + + if (me->mselect && me->totselect != 0) { + BMIter iter; + BMVert *vertex; + BMEdge *edge; + BMFace *face; + BMVert **vertex_array = MEM_callocN(sizeof(BMVert *) * bm->totvert, + "Selection Conversion Vertex Pointer Array"); + BMEdge **edge_array = MEM_callocN(sizeof(BMEdge *) * bm->totedge, + "Selection Conversion Edge Pointer Array"); + BMFace **face_array = MEM_callocN(sizeof(BMFace *) * bm->totface, + "Selection Conversion Face Pointer Array"); + + for (i = 0, vertex = BM_iter_new(&iter, bm, BM_VERTS_OF_MESH, NULL); + vertex; + i++, vertex = BM_iter_step(&iter)) + { + vertex_array[i] = vertex; + } + + for (i = 0, edge = BM_iter_new(&iter, bm, BM_EDGES_OF_MESH, NULL); + edge; + i++, edge = BM_iter_step(&iter)) + { + edge_array[i] = edge; + } + + for (i = 0, face = BM_iter_new(&iter, bm, BM_FACES_OF_MESH, NULL); + face; + i++, face = BM_iter_step(&iter)) + { + face_array[i] = face; + } + + if (me->mselect) { + for (i = 0; i < me->totselect; i++) { + if (me->mselect[i].type == ME_VSEL) { + BM_select_history_store(bm, vertex_array[me->mselect[i].index]); + } + else if (me->mselect[i].type == ME_ESEL) { + BM_select_history_store(bm, edge_array[me->mselect[i].index]); + } + else if (me->mselect[i].type == ME_FSEL) { + BM_select_history_store(bm, face_array[me->mselect[i].index]); + } + } + } + else { + me->totselect = 0; + } + + MEM_freeN(vertex_array); + MEM_freeN(edge_array); + MEM_freeN(face_array); + } + else { + me->totselect = 0; + if (me->mselect) { + MEM_freeN(me->mselect); + me->mselect = NULL; + } + } + + BLI_array_free(fedges); + BLI_array_free(verts); + + MEM_freeN(vt); + MEM_freeN(et); +} + +void object_load_bmesh_exec(BMesh *bm, BMOperator *op) +{ + Object *ob = BMO_slot_ptr_get(op, "object"); + /* Scene *scene = BMO_slot_ptr_get(op, "scene"); */ + Mesh *me = ob->data; + + BMO_op_callf(bm, "bmesh_to_mesh mesh=%p object=%p notesselation=%i", me, ob, TRUE); +} + + +static BMVert **bmesh_to_mesh_vertex_map(BMesh *bm, int ototvert) +{ + BMVert **vertMap = NULL; + BMVert *eve; + int index; + int i = 0; + BMIter iter; + + /* caller needs to ensure this */ + BLI_assert(ototvert > 0); + + vertMap = MEM_callocN(sizeof(*vertMap)*ototvert, "vertMap"); + if (CustomData_has_layer(&bm->vdata, CD_SHAPE_KEYINDEX)) { + int *keyi; + BM_ITER(eve, &iter, bm, BM_VERTS_OF_MESH, NULL) { + keyi = CustomData_bmesh_get(&bm->vdata, eve->head.data, CD_SHAPE_KEYINDEX); + if (keyi) { + if (((index = *keyi) != ORIGINDEX_NONE) && (index < ototvert)) { + vertMap[index] = eve; + } + } + else { + if (i < ototvert) { + vertMap[i] = eve; + } + } + i++; + } + } + else { + BM_ITER(eve, &iter, bm, BM_VERTS_OF_MESH, NULL) { + if (i < ototvert) { + vertMap[i] = eve; + } + else { + break; + } + i++; + } + } + + return vertMap; +} + +BM_INLINE void bmesh_quick_edgedraw_flag(MEdge *med, BMEdge *e) +{ + /* this is a cheap way to set the edge draw, its not precise and will + * pick the first 2 faces an edge uses */ + + + if ( /* (med->flag & ME_EDGEDRAW) && */ /* assume to be true */ + (e->l && (e->l != e->l->radial_next)) && + (dot_v3v3(e->l->f->no, e->l->radial_next->f->no) > 0.998f)) + { + med->flag &= ~ME_EDGEDRAW; + } +} + + +void bmesh_to_mesh_exec(BMesh *bm, BMOperator *op) +{ + Mesh *me = BMO_slot_ptr_get(op, "mesh"); + /* Object *ob = BMO_slot_ptr_get(op, "object"); */ + MLoop *mloop; + MPoly *mpoly; + MVert *mvert, *oldverts; + MEdge *med, *medge; + BMVert *v, *eve; + BMEdge *e; + BMLoop *l; + BMFace *f; + BMIter iter, liter; + int i, j, *keyi, ototvert, totloop; + int dotess = !BMO_slot_int_get(op, "notesselation"); + + ototvert = me->totvert; + + /* new Vertex block */ + if (bm->totvert == 0) mvert = NULL; + else mvert = MEM_callocN(bm->totvert * sizeof(MVert), "loadeditbMesh vert"); + + /* new Edge block */ + if (bm->totedge == 0) medge = NULL; + else medge = MEM_callocN(bm->totedge * sizeof(MEdge), "loadeditbMesh edge"); + + /* build ngon dat */ + /* new Ngon Face block */ + if (bm->totface == 0) mpoly = NULL; + else mpoly = MEM_callocN(bm->totface * sizeof(MPoly), "loadeditbMesh poly"); + + /* find number of loops to allocat */ + totloop = 0; + BM_ITER(f, &iter, bm, BM_FACES_OF_MESH, NULL) { + totloop += f->len; + } + + if (totloop == 0) mloop = NULL; + else mloop = MEM_callocN(totloop * sizeof(MLoop), "loadeditbMesh loop"); + + /* lets save the old verts just in case we are actually working on + * a key ... we now do processing of the keys at the end */ + oldverts = me->mvert; + + /* don't free this yet */ + CustomData_set_layer(&me->vdata, CD_MVERT, NULL); + + /* free custom data */ + CustomData_free(&me->vdata, me->totvert); + CustomData_free(&me->edata, me->totedge); + CustomData_free(&me->fdata, me->totface); + CustomData_free(&me->ldata, me->totloop); + CustomData_free(&me->pdata, me->totpoly); + + /* add new custom data */ + me->totvert = bm->totvert; + me->totedge = bm->totedge; + me->totloop = totloop; + me->totpoly = bm->totface; + /* will be overwritten with a valid value if 'dotess' is set, otherwise we + * end up with 'me->totface' and me->mface == NULL which can crash [#28625] + */ + me->totface = 0; + + CustomData_copy(&bm->vdata, &me->vdata, CD_MASK_MESH, CD_CALLOC, me->totvert); + CustomData_copy(&bm->edata, &me->edata, CD_MASK_MESH, CD_CALLOC, me->totedge); + CustomData_copy(&bm->ldata, &me->ldata, CD_MASK_MESH, CD_CALLOC, me->totloop); + CustomData_copy(&bm->pdata, &me->pdata, CD_MASK_MESH, CD_CALLOC, me->totpoly); + + CustomData_add_layer(&me->vdata, CD_MVERT, CD_ASSIGN, mvert, me->totvert); + CustomData_add_layer(&me->edata, CD_MEDGE, CD_ASSIGN, medge, me->totedge); + CustomData_add_layer(&me->ldata, CD_MLOOP, CD_ASSIGN, mloop, me->totloop); + CustomData_add_layer(&me->pdata, CD_MPOLY, CD_ASSIGN, mpoly, me->totpoly); + + /* this is called again, 'dotess' arg is used there */ + mesh_update_customdata_pointers(me, 0); + + i = 0; + BM_ITER(v, &iter, bm, BM_VERTS_OF_MESH, NULL) { + float *bweight = CustomData_bmesh_get(&bm->vdata, v->head.data, CD_BWEIGHT); + + mvert->bweight = bweight ? (char)((*bweight) * 255) : 0; + + copy_v3_v3(mvert->co, v->co); + normal_float_to_short_v3(mvert->no, v->no); + + mvert->flag = BM_vert_flag_to_mflag(v); + + BM_elem_index_set(v, i); /* set_inline */ + + /* copy over customdat */ + CustomData_from_bmesh_block(&bm->vdata, &me->vdata, v->head.data, i); + + i++; + mvert++; + + BM_CHECK_ELEMENT(bm, v); + } + bm->elem_index_dirty &= ~BM_VERT; + + med = medge; + i = 0; + BM_ITER(e, &iter, bm, BM_EDGES_OF_MESH, NULL) { + float *crease = CustomData_bmesh_get(&bm->edata, e->head.data, CD_CREASE); + float *bweight = CustomData_bmesh_get(&bm->edata, e->head.data, CD_BWEIGHT); + + med->v1 = BM_elem_index_get(e->v1); + med->v2 = BM_elem_index_get(e->v2); + med->crease = crease ? (char)((*crease) * 255) : 0; + med->bweight = bweight ? (char)((*bweight) * 255) : 0; + + med->flag = BM_edge_flag_to_mflag(e); + + BM_elem_index_set(e, i); /* set_inline */ + + /* copy over customdat */ + CustomData_from_bmesh_block(&bm->edata, &me->edata, e->head.data, i); + + bmesh_quick_edgedraw_flag(med, e); + + i++; + med++; + BM_CHECK_ELEMENT(bm, e); + } + bm->elem_index_dirty &= ~BM_EDGE; + + i = 0; + j = 0; + BM_ITER(f, &iter, bm, BM_FACES_OF_MESH, NULL) { + mpoly->loopstart = j; + mpoly->totloop = f->len; + mpoly->mat_nr = f->mat_nr; + mpoly->flag = BM_face_flag_to_mflag(f); + + l = BM_iter_new(&liter, bm, BM_LOOPS_OF_FACE, f); + for ( ; l; l = BM_iter_step(&liter), j++, mloop++) { + mloop->e = BM_elem_index_get(l->e); + mloop->v = BM_elem_index_get(l->v); + + /* copy over customdat */ + CustomData_from_bmesh_block(&bm->ldata, &me->ldata, l->head.data, j); + BM_CHECK_ELEMENT(bm, l); + BM_CHECK_ELEMENT(bm, l->e); + BM_CHECK_ELEMENT(bm, l->v); + } + + if (f == bm->act_face) me->act_face = i; + + /* copy over customdat */ + CustomData_from_bmesh_block(&bm->pdata, &me->pdata, f->head.data, i); + + i++; + mpoly++; + BM_CHECK_ELEMENT(bm, f); + } + + /* patch hook indices and vertex parents */ + if (ototvert > 0) { + Object *ob; + ModifierData *md; + BMVert **vertMap = NULL; + int i, j; + + for (ob = G.main->object.first; ob; ob = ob->id.next) { + if (ob->parent == bm->ob && ELEM(ob->partype, PARVERT1, PARVERT3)) { + + if (vertMap == NULL) { + vertMap = bmesh_to_mesh_vertex_map(bm, ototvert); + } + + if (ob->par1 < ototvert) { + eve = vertMap[ob->par1]; + if (eve) ob->par1 = BM_elem_index_get(eve); + } + if (ob->par2 < ototvert) { + eve = vertMap[ob->par2]; + if (eve) ob->par2 = BM_elem_index_get(eve); + } + if (ob->par3 < ototvert) { + eve = vertMap[ob->par3]; + if (eve) ob->par3 = BM_elem_index_get(eve); + } + + } + if (ob->data == me) { + for (md = ob->modifiers.first; md; md = md->next) { + if (md->type == eModifierType_Hook) { + HookModifierData *hmd = (HookModifierData *) md; + + if (vertMap == NULL) { + vertMap = bmesh_to_mesh_vertex_map(bm, ototvert); + } + + for (i = j = 0; i < hmd->totindex; i++) { + if (hmd->indexar[i] < ototvert) { + eve = vertMap[hmd->indexar[i]]; + + if (eve) { + hmd->indexar[j++] = BM_elem_index_get(eve); + } + } + else j++; + } + + hmd->totindex = j; + } + } + } + } + + if (vertMap) MEM_freeN(vertMap); + } + + if (dotess) { + BKE_mesh_tessface_calc(me); + } + + mesh_update_customdata_pointers(me, dotess); + + { + BMEditSelection *selected; + me->totselect = BLI_countlist(&(bm->selected)); + + if (me->mselect) MEM_freeN(me->mselect); + + me->mselect = MEM_callocN(sizeof(MSelect) * me->totselect, "Mesh selection history"); + + + for (i = 0, selected = bm->selected.first; selected; i++, selected = selected->next) { + if (selected->htype == BM_VERT) { + me->mselect[i].type = ME_VSEL; + + } + else if (selected->htype == BM_EDGE) { + me->mselect[i].type = ME_ESEL; + + } + else if (selected->htype == BM_FACE) { + me->mselect[i].type = ME_FSEL; + } + + me->mselect[i].index = BM_elem_index_get(selected->data); + } + } + + /* see comment below, this logic is in twice */ + + if (me->key) { + KeyBlock *currkey; + KeyBlock *actkey = BLI_findlink(&me->key->block, bm->shapenr - 1); + + float (*ofs)[3] = NULL; + + /* go through and find any shapekey customdata layers + * that might not have corrusponding KeyBlocks, and add them if + * necassary */ + j = 0; + for (i = 0; i < bm->vdata.totlayer; i++) { + if (bm->vdata.layers[i].type != CD_SHAPEKEY) + continue; + + for (currkey = me->key->block.first; currkey; currkey = currkey->next) { + if (currkey->uid == bm->vdata.layers[i].uid) + break; + } + + if (!currkey) { + currkey = MEM_callocN(sizeof(KeyBlock), "KeyBlock mesh_conv.c"); + currkey->type = KEY_LINEAR; + currkey->slidermin = 0.0f; + currkey->slidermax = 1.0f; + + BLI_addtail(&me->key->block, currkey); + me->key->totkey++; + } + + j++; + } + + + /* editing the base key should update others */ + if (me->key->type == KEY_RELATIVE && oldverts) { + int act_is_basis = 0; + /* find if this key is a basis for any others */ + for (currkey = me->key->block.first; currkey; currkey = currkey->next) { + if (bm->shapenr - 1 == currkey->relative) { + act_is_basis = 1; + break; + } + } + + if (act_is_basis) { /* active key is a base */ + float (*fp)[3] = actkey->data; + int *keyi; + i = 0; + ofs = MEM_callocN(sizeof(float) * 3 * bm->totvert, "currkey->data"); + mvert = me->mvert; + BM_ITER(eve, &iter, bm, BM_VERTS_OF_MESH, NULL) { + keyi = CustomData_bmesh_get(&bm->vdata, eve->head.data, CD_SHAPE_KEYINDEX); + if (keyi && *keyi != ORIGINDEX_NONE) { + sub_v3_v3v3(ofs[i], mvert->co, fp[*keyi]); + } + i++; + mvert++; + } + } + } + + + for (currkey = me->key->block.first; currkey; currkey = currkey->next) { + j = 0; + + for (i = 0; i < bm->vdata.totlayer; i++) { + if (bm->vdata.layers[i].type != CD_SHAPEKEY) + continue; + + if (currkey->uid == bm->vdata.layers[i].uid) { + int apply_offset = (ofs && (currkey != actkey) && (bm->shapenr - 1 == currkey->relative)); + float *fp, *co; + float (*ofs_pt)[3] = ofs; + + if (currkey->data) + MEM_freeN(currkey->data); + currkey->data = fp = MEM_mallocN(sizeof(float) * 3 * bm->totvert, "shape key data"); + currkey->totelem = bm->totvert; + + BM_ITER(eve, &iter, bm, BM_VERTS_OF_MESH, NULL) { + co = (currkey == actkey) ? + eve->co : + CustomData_bmesh_get_n(&bm->vdata, eve->head.data, CD_SHAPEKEY, j); + + copy_v3_v3(fp, co); + + /* propagate edited basis offsets to other shapes */ + if (apply_offset) { + add_v3_v3(fp, *ofs_pt++); + } + + fp += 3; + } + break; + } + + j++; + } + + /* if we didn't find a shapekey, tag the block to be reconstructed + * via the old method below */ + if (j == CustomData_number_of_layers(&bm->vdata, CD_SHAPEKEY)) { + currkey->flag |= KEYBLOCK_MISSING; + } + } + + if (ofs) MEM_freeN(ofs); + } + + /* XXX, code below is from trunk and a duplicate functionality + * to the block above. + * We should use one or the other, having both means we have to maintain + * both and keep them working the same way which is a hassle - campbell */ + + /* old method of reconstructing keys via vertice's original key indices, + * currently used if the new method above fails (which is theoretically + * possible in certain cases of undo) */ + if (me->key) { + float *fp, *newkey, *oldkey; + KeyBlock *currkey; + KeyBlock *actkey = BLI_findlink(&me->key->block, bm->shapenr - 1); + + float (*ofs)[3] = NULL; + + /* editing the base key should update others */ + if (me->key->type == KEY_RELATIVE && oldverts) { + int act_is_basis = 0; + /* find if this key is a basis for any others */ + for (currkey = me->key->block.first; currkey; currkey = currkey->next) { + if (bm->shapenr - 1 == currkey->relative) { + act_is_basis = 1; + break; + } + } + + if (act_is_basis) { /* active key is a base */ + float (*fp)[3] = actkey->data; + int *keyi; + i = 0; + ofs = MEM_callocN(sizeof(float) * 3 * bm->totvert, "currkey->data"); + mvert = me->mvert; + BM_ITER(eve, &iter, bm, BM_VERTS_OF_MESH, NULL) { + keyi = CustomData_bmesh_get(&bm->vdata, eve->head.data, CD_SHAPE_KEYINDEX); + if (keyi && *keyi != ORIGINDEX_NONE) { + sub_v3_v3v3(ofs[i], mvert->co, fp[*keyi]); + } + i++; + mvert++; + } + } + } + + /* Lets reorder the key data so that things line up roughly + * with the way things were before editmode */ + currkey = me->key->block.first; + while (currkey) { + int apply_offset = (ofs && (currkey != actkey) && (bm->shapenr - 1 == currkey->relative)); + + if (!(currkey->flag & KEYBLOCK_MISSING)) { + currkey = currkey->next; + continue; + } + + printf("warning: had to hackishly reconstruct shape key \"%s\"," + " it may not be correct anymore.\n", currkey->name); + + currkey->flag &= ~KEYBLOCK_MISSING; + + fp = newkey = MEM_callocN(me->key->elemsize * bm->totvert, "currkey->data"); + oldkey = currkey->data; + + eve = BM_iter_new(&iter, bm, BM_VERTS_OF_MESH, NULL); + + i = 0; + mvert = me->mvert; + while (eve) { + keyi = CustomData_bmesh_get(&bm->vdata, eve->head.data, CD_SHAPE_KEYINDEX); + if (!keyi) { + break; + } + if (*keyi >= 0 && *keyi < currkey->totelem) { // valid old vertex + if (currkey == actkey) { + if (actkey == me->key->refkey) { + copy_v3_v3(fp, mvert->co); + } + else { + copy_v3_v3(fp, mvert->co); + if (oldverts) { + copy_v3_v3(mvert->co, oldverts[*keyi].co); + } + } + } + else { + if (oldkey) { + copy_v3_v3(fp, oldkey + 3 * *keyi); + } + } + } + else { + copy_v3_v3(fp, mvert->co); + } + + /* propagate edited basis offsets to other shapes */ + if (apply_offset) { + add_v3_v3(fp, ofs[i]); + } + + fp+= 3; + ++i; + ++mvert; + eve = BM_iter_step(&iter); + } + currkey->totelem = bm->totvert; + if (currkey->data) MEM_freeN(currkey->data); + currkey->data = newkey; + + currkey = currkey->next; + } + + if (ofs) MEM_freeN(ofs); + } + + if (oldverts) MEM_freeN(oldverts); +} diff --git a/source/blender/bmesh/operators/bmo_mirror.c b/source/blender/bmesh/operators/bmo_mirror.c new file mode 100644 index 00000000000..82e77fc9a96 --- /dev/null +++ b/source/blender/bmesh/operators/bmo_mirror.c @@ -0,0 +1,126 @@ +/* + * ***** 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 "DNA_meshdata_types.h" + +#include "BLI_math.h" +#include "BLI_array.h" + +#include "BKE_customdata.h" + +#include "bmesh.h" +#include "bmesh_operators_private.h" /* own include */ + +#define ELE_NEW 1 + +void bmesh_mirror_exec(BMesh *bm, BMOperator *op) +{ + BMOperator dupeop, weldop; + BMOIter siter; + BMIter iter; + BMVert *v, *v2, **vmap = NULL; + BLI_array_declare(vmap); + BMEdge /* *e, */ **emap = NULL; + BLI_array_declare(emap); + float mtx[4][4]; + float imtx[4][4]; + float scale[3] = {1.0f, 1.0f, 1.0f}; + float dist = BMO_slot_float_get(op, "mergedist"); + int i, ototvert, ototedge, axis = BMO_slot_int_get(op, "axis"); + int mirroru = BMO_slot_int_get(op, "mirror_u"); + int mirrorv = BMO_slot_int_get(op, "mirror_v"); + + ototvert = bm->totvert; + ototedge = bm->totedge; + + BMO_slot_mat4_get(op, "mat", mtx); + invert_m4_m4(imtx, mtx); + + BMO_op_initf(bm, &dupeop, "dupe geom=%s", op, "geom"); + BMO_op_exec(bm, &dupeop); + + BMO_slot_buffer_flag_enable(bm, &dupeop, "newout", ELE_NEW, BM_ALL); + + /* create old -> new mappin */ + i = 0; + v2 = BM_iter_new(&iter, bm, BM_VERTS_OF_MESH, NULL); + BMO_ITER(v, &siter, bm, &dupeop, "newout", BM_VERT) { + BLI_array_growone(vmap); + vmap[i] = v; + + /* BMESH_TODO, double check this is being used, calling following operators will overwrite anyway - campbell */ + BM_elem_index_set(v2, i); /* set_dirty! */ + v2 = BM_iter_step(&iter); + + i++; + } + bm->elem_index_dirty |= BM_VERT; + + /* feed old data to transform bmo */ + scale[axis] = -1.0f; + BMO_op_callf(bm, "transform verts=%fv mat=%m4", ELE_NEW, mtx); + BMO_op_callf(bm, "scale verts=%fv vec=%v", ELE_NEW, scale); + BMO_op_callf(bm, "transform verts=%fv mat=%m4", ELE_NEW, imtx); + + BMO_op_init(bm, &weldop, "weldverts"); + + v = BM_iter_new(&iter, bm, BM_VERTS_OF_MESH, NULL); + for (i = 0; i < ototvert; i++) { + if (ABS(v->co[axis]) <= dist) { + BMO_slot_map_ptr_insert(bm, &weldop, "targetmap", vmap[i], v); + } + v = BM_iter_step(&iter); + } + + if (mirroru || mirrorv) { + BMFace *f; + BMLoop *l; + MLoopUV *luv; + int totlayer; + BMIter liter; + + BMO_ITER(f, &siter, bm, &dupeop, "newout", BM_FACE) { + BM_ITER(l, &liter, bm, BM_LOOPS_OF_FACE, f) { + totlayer = CustomData_number_of_layers(&bm->ldata, CD_MLOOPUV); + for (i = 0; i < totlayer; i++) { + luv = CustomData_bmesh_get_n(&bm->ldata, l->head.data, CD_MLOOPUV, i); + if (mirroru) + luv->uv[0] = 1.0f - luv->uv[0]; + if (mirrorv) + luv->uv[1] = 1.0f - luv->uv[1]; + } + } + } + } + + BMO_op_exec(bm, &weldop); + + BMO_op_finish(bm, &weldop); + BMO_op_finish(bm, &dupeop); + + BMO_slot_from_flag(bm, op, "newout", ELE_NEW, BM_ALL); + + BLI_array_free(vmap); + BLI_array_free(emap); +} diff --git a/source/blender/bmesh/operators/bmo_primitive.c b/source/blender/bmesh/operators/bmo_primitive.c new file mode 100644 index 00000000000..f7a1a8f4d06 --- /dev/null +++ b/source/blender/bmesh/operators/bmo_primitive.c @@ -0,0 +1,734 @@ +/* + * ***** 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 "ED_mesh.h" + +#include "bmesh.h" +#include "bmesh_private.h" + + + +/* ************************ primitives ******************* */ + +static float icovert[12][3] = { + {0.0f,0.0f,-200.0f}, + {144.72f, -105.144f,-89.443f}, + {-55.277f, -170.128,-89.443f}, + {-178.885f,0.0f,-89.443f}, + {-55.277f,170.128f,-89.443f}, + {144.72f,105.144f,-89.443f}, + {55.277f,-170.128f,89.443f}, + {-144.72f,-105.144f,89.443f}, + {-144.72f,105.144f,89.443f}, + {55.277f,170.128f,89.443f}, + {178.885f,0.0f,89.443f}, + {0.0f,0.0f,200.0f} +}; + +static short icoface[20][3] = { + {0,1,2}, + {1,0,5}, + {0,2,3}, + {0,3,4}, + {0,4,5}, + {1,5,10}, + {2,1,6}, + {3,2,7}, + {4,3,8}, + {5,4,9}, + {1,10,6}, + {2,6,7}, + {3,7,8}, + {4,8,9}, + {5,9,10}, + {6,10,11}, + {7,6,11}, + {8,7,11}, + {9,8,11}, + {10,9,11} +}; + +// HACK: these can also be found in cmoview.tga.c, but are here so that they can be found by linker +// this hack is only used so that scons & mingw + split-sources hack works +// ------------------------------- start copied code +/* these are not the monkeys you are looking for */ +static int monkeyo = 4; +static int monkeynv = 271; +static int monkeynf = 250; +static signed char monkeyv[271][3] = { + {-71,21,98},{-63,12,88},{-57,7,74},{-82,-3,79},{-82,4,92}, + {-82,17,100},{-92,21,102},{-101,12,95},{-107,7,83}, + {-117,31,84},{-109,31,95},{-96,31,102},{-92,42,102}, + {-101,50,95},{-107,56,83},{-82,66,79},{-82,58,92}, + {-82,46,100},{-71,42,98},{-63,50,88},{-57,56,74}, + {-47,31,72},{-55,31,86},{-67,31,97},{-66,31,99}, + {-70,43,100},{-82,48,103},{-93,43,105},{-98,31,105}, + {-93,20,105},{-82,31,106},{-82,15,103},{-70,20,100}, + {-127,55,95},{-127,45,105},{-127,-87,94},{-127,-41,100}, + {-127,-24,102},{-127,-99,92},{-127,52,77},{-127,73,73}, + {-127,115,-70},{-127,72,-109},{-127,9,-106},{-127,-49,-45}, + {-101,-24,72},{-87,-56,73},{-82,-89,73},{-80,-114,68}, + {-85,-121,67},{-104,-124,71},{-127,-126,74},{-71,-18,68}, + {-46,-5,69},{-21,19,57},{-17,55,76},{-36,62,80}, + {-64,77,88},{-86,97,94},{-107,92,97},{-119,63,96}, + {-106,53,99},{-111,39,98},{-101,12,95},{-79,2,90}, + {-64,8,86},{-47,24,83},{-45,38,83},{-50,48,85}, + {-72,56,92},{-95,60,97},{-127,-98,94},{-113,-92,94}, + {-112,-107,91},{-119,-113,89},{-127,-114,88},{-127,-25,96}, + {-127,-18,95},{-114,-19,95},{-111,-29,96},{-116,-37,95}, + {-76,-6,86},{-48,7,80},{-34,26,77},{-32,48,84}, + {-39,53,93},{-71,70,102},{-87,82,107},{-101,79,109}, + {-114,55,108},{-111,-13,104},{-100,-57,91},{-95,-90,88}, + {-93,-105,85},{-97,-117,81},{-106,-119,81},{-127,-121,82}, + {-127,6,93},{-127,27,98},{-85,61,95},{-106,18,96}, + {-110,27,97},{-112,-88,94},{-117,-57,96},{-127,-57,96}, + {-127,-42,95},{-115,-35,100},{-110,-29,102},{-113,-17,100}, + {-122,-16,100},{-127,-26,106},{-121,-19,104},{-115,-20,104}, + {-113,-29,106},{-117,-32,103},{-127,-37,103},{-94,-40,71}, + {-106,-31,91},{-104,-40,91},{-97,-32,71},{-127,-112,88}, + {-121,-111,88},{-115,-105,91},{-115,-95,93},{-127,-100,84}, + {-115,-96,85},{-115,-104,82},{-121,-109,81},{-127,-110,81}, + {-105,28,100},{-103,20,99},{-84,55,97},{-92,54,99}, + {-73,51,99},{-55,45,89},{-52,37,88},{-53,25,87}, + {-66,13,92},{-79,8,95},{-98,14,100},{-104,38,100}, + {-100,48,100},{-97,46,97},{-102,38,97},{-96,16,97}, + {-79,11,93},{-68,15,90},{-57,27,86},{-56,36,86}, + {-59,43,87},{-74,50,96},{-91,51,98},{-84,52,96}, + {-101,22,96},{-102,29,96},{-113,59,78},{-102,85,79}, + {-84,88,76},{-65,71,71},{-40,58,63},{-25,52,59}, + {-28,21,48},{-50,0,53},{-71,-12,60},{-127,115,37}, + {-127,126,-10},{-127,-25,-86},{-127,-59,24},{-127,-125,59}, + {-127,-103,44},{-127,-73,41},{-127,-62,36},{-18,30,7}, + {-17,41,-6},{-28,34,-56},{-68,56,-90},{-33,-6,9}, + {-51,-16,-21},{-45,-1,-55},{-84,7,-85},{-97,-45,52}, + {-104,-53,33},{-90,-91,49},{-95,-64,50},{-85,-117,51}, + {-109,-97,47},{-111,-69,46},{-106,-121,56},{-99,-36,55}, + {-100,-29,60},{-101,-22,64},{-100,-50,21},{-89,-40,-34}, + {-83,-19,-69},{-69,111,-49},{-69,119,-9},{-69,109,30}, + {-68,67,55},{-34,52,43},{-46,58,36},{-45,90,7}, + {-25,72,16},{-25,79,-15},{-45,96,-25},{-45,87,-57}, + {-25,69,-46},{-48,42,-75},{-65,3,-70},{-22,42,-26}, + {-75,-22,19},{-72,-25,-27},{-13,52,-30},{-28,-18,-16}, + {6,-13,-42},{37,7,-55},{46,41,-54},{31,65,-54}, + {4,61,-40},{3,53,-37},{25,56,-50},{35,37,-52}, + {28,10,-52},{5,-5,-39},{-21,-9,-17},{-9,46,-28}, + {-6,39,-37},{-14,-3,-27},{6,0,-47},{25,12,-57}, + {31,32,-57},{23,46,-56},{4,44,-46},{-19,37,-27}, + {-20,22,-35},{-30,12,-35},{-22,11,-35},{-19,2,-35}, + {-23,-2,-35},{-34,0,-9},{-35,-3,-22},{-35,5,-24}, + {-25,26,-27},{-13,31,-34},{-13,30,-41},{-23,-2,-41}, + {-18,2,-41},{-21,10,-41},{-29,12,-41},{-19,22,-41}, + {6,42,-53},{25,44,-62},{34,31,-63},{28,11,-62}, + {7,0,-54},{-14,-2,-34},{-5,37,-44},{-13,14,-42}, + {-7,8,-43},{1,16,-47},{-4,22,-45},{3,30,-48}, + {8,24,-49},{15,27,-50},{12,35,-50},{4,56,-62}, + {33,60,-70},{48,38,-64},{41,7,-68},{6,-11,-63}, + {-26,-16,-42},{-17,49,-49}, +}; + +static signed char monkeyf[250][4] = { + {27,4,5,26}, {25,4,5,24}, {3,6,5,4}, {1,6,5,2}, {5,6,7,4}, + {3,6,7,2}, {5,8,7,6}, {3,8,7,4}, {7,8,9,6}, + {5,8,9,4}, {7,10,9,8}, {5,10,9,6}, {9,10,11,8}, + {7,10,11,6}, {9,12,11,10}, {7,12,11,8}, {11,6,13,12}, + {5,4,13,12}, {3,-2,13,12}, {-3,-4,13,12}, {-5,-10,13,12}, + {-11,-12,14,12}, {-13,-18,14,13}, {-19,4,5,13}, {10,12,4,4}, + {10,11,9,9}, {8,7,9,9}, {7,5,6,6}, {6,3,4,4}, + {5,1,2,2}, {4,-1,0,0}, {3,-3,-2,-2}, {22,67,68,23}, + {20,65,66,21}, {18,63,64,19}, {16,61,62,17}, {14,59,60,15}, + {12,19,48,57}, {18,19,48,47}, {18,19,48,47}, {18,19,48,47}, + {18,19,48,47}, {18,19,48,47}, {18,19,48,47}, {18,19,48,47}, + {18,19,48,47}, {18,-9,-8,47}, {18,27,45,46}, {26,55,43,44}, + {24,41,42,54}, {22,39,40,23}, {20,37,38,21}, {18,35,36,19}, + {16,33,34,17}, {14,31,32,15}, {12,39,30,13}, {11,48,45,38}, + {8,36,-19,9}, {8,-20,44,47}, {42,45,46,43}, {18,19,40,39}, + {16,17,38,37}, {14,15,36,35}, {32,44,43,33}, {12,33,32,42}, + {19,44,43,42}, {40,41,42,-27}, {8,9,39,-28}, {15,43,42,16}, + {13,43,42,14}, {11,43,42,12}, {9,-30,42,10}, {37,12,38,-32}, + {-33,37,45,46}, {-33,40,41,39}, {38,40,41,37}, {36,40,41,35}, + {34,40,41,33}, {36,39,38,37}, {35,40,39,38}, {1,2,14,21}, + {1,2,40,13}, {1,2,40,39}, {1,24,12,39}, {-34,36,38,11}, + {35,38,36,37}, {-37,8,35,37}, {-11,-12,-45,40}, {-11,-12,39,38}, + {-11,-12,37,36}, {-11,-12,35,34}, {33,34,40,41}, {33,34,38,39}, + {33,34,36,37}, {33,-52,34,35}, {33,37,36,34}, {33,35,34,34}, + {8,7,37,36}, {-32,7,35,46}, {-34,-33,45,46}, {4,-33,43,34}, + {-34,-33,41,42}, {-34,-33,39,40}, {-34,-33,37,38}, {-34,-33,35,36}, + {-34,-33,33,34}, {-34,-33,31,32}, {-34,-4,28,30}, {-5,-34,28,27}, + {-35,-44,36,27}, {26,35,36,45}, {24,25,44,45}, {25,23,44,42}, + {25,24,41,40}, {25,24,39,38}, {25,24,37,36}, {25,24,35,34}, + {25,24,33,32}, {25,24,31,30}, {15,24,29,38}, {25,24,27,26}, + {23,12,37,26}, {11,12,35,36}, {-86,-59,36,-80}, {-60,-61,36,35}, + {-62,-63,36,35}, {-64,-65,36,35}, {-66,-67,36,35}, {-68,-69,36,35}, + {-70,-71,36,35}, {-72,-73,36,35}, {-74,-75,36,35}, {42,43,53,58}, + {40,41,57,56}, {38,39,55,57}, {-81,-80,37,56}, {-83,-82,55,52}, + {-85,-84,51,49}, {-87,-86,48,49}, {47,50,51,48}, {46,48,51,49}, + {43,46,49,44}, {-92,-91,45,42}, {-23,49,50,-20}, {-94,40,48,-24}, + {-96,-22,48,49}, {-97,48,21,-90}, {-100,36,50,23}, {22,49,48,-100}, + {-101,47,46,22}, {21,45,35,25}, {33,34,44,41}, {13,14,28,24}, + {-107,26,30,-106}, {14,46,45,15}, {14,44,43,-110}, {-111,42,23,-110}, + {6,7,45,46}, {45,44,47,46}, {45,46,47,48}, {47,46,49,48}, + {17,49,47,48}, {17,36,46,48}, {35,36,44,45}, {35,36,40,43}, + {35,36,38,39}, {-4,-3,37,35}, {-123,34,33,1}, {-9,-8,-7,-6}, + {-10,-7,32,-125}, {-127,-11,-126,-126}, {-7,-6,5,31}, {4,5,33,30}, + {4,39,33,32}, {4,35,32,38}, {20,21,39,38}, {4,37,38,5}, + {-11,-10,36,3}, {-11,15,14,35}, {13,16,34,34}, {-13,14,13,13}, + {-3,1,30,29}, {-3,28,29,1}, {-2,31,28,-1}, {12,13,27,30}, + {-2,26,12,12}, {35,29,42,36}, {34,35,36,33}, {32,35,36,31}, + {30,35,36,29}, {28,35,36,27}, {26,35,36,25}, {34,39,38,35}, + {32,39,38,33}, {30,39,38,31}, {28,39,38,29}, {26,39,38,27}, + {25,31,32,38}, {-18,-17,45,44}, {-18,17,28,44}, {-24,-20,42,-23}, + {11,35,27,14}, {25,28,39,41}, {37,41,40,38}, {34,40,36,35}, + {32,40,39,33}, {30,39,31,40}, {21,29,39,22}, {-31,37,28,4}, + {-32,33,35,36}, {32,33,34,34}, {18,35,36,48}, {34,25,40,35}, + {24,25,38,39}, {24,25,36,37}, {24,25,34,35}, {24,25,32,33}, + {24,13,41,31}, {17,11,41,35}, {15,16,34,35}, {13,14,34,35}, + {11,12,34,35}, {9,10,34,35}, {7,8,34,35}, {26,25,37,36}, + {35,36,37,38}, {37,36,39,38}, {37,38,39,40}, {25,31,36,39}, + {18,34,35,30}, {17,22,30,33}, {19,29,21,20}, {16,26,29,17}, + {24,29,28,25}, {22,31,28,23}, {20,31,30,21}, {18,31,30,19}, + {16,30,17,17}, {-21,-22,35,34}, {-21,-22,33,32}, {-21,-22,31,30}, + {-21,-22,29,28}, {-21,-22,27,26}, {-28,-22,25,31}, {24,28,29,30}, + {23,24,26,27}, {23,24,25,25}, {-69,-35,-32,27}, {-70,26,25,-66}, + {-68,-67,24,-33}, +}; + +#define VERT_MARK 1 + +#define EDGE_ORIG 1 +#define EDGE_MARK 2 + +#define FACE_MARK 1 +#define FACE_NEW 2 + +void bmesh_create_grid_exec(BMesh *bm, BMOperator *op) +{ + BMOperator bmop, prevop; + BMVert *eve, *preveve; + BMEdge *e; + float vec[3], mat[4][4], phi, phid, dia = BMO_slot_float_get(op, "size"); + int a, tot = BMO_slot_int_get(op, "xsegments"), seg = BMO_slot_int_get(op, "ysegments"); + + if (tot < 2) tot = 2; + if (seg < 2) seg = 2; + + BMO_slot_mat4_get(op, "mat", mat); + + /* one segment first: the X axis */ + phi = 1.0f; + phid = 2.0f / ((float)tot - 1); + for (a = 0; a < tot; a++) { + vec[0] = dia * phi; + vec[1] = -dia; + vec[2] = 0.0f; + mul_m4_v3(mat, vec); + + eve = BM_vert_create(bm, vec, NULL); + BMO_elem_flag_enable(bm, eve, VERT_MARK); + + if (a) { + e = BM_edge_create(bm, preveve, eve, NULL, TRUE); + BMO_elem_flag_enable(bm, e, EDGE_ORIG); + } + + preveve = eve; + phi -= phid; + } + + /* extrude and translate */ + vec[0] = vec[2] = 0.0f; + vec[1] = dia * phid; + mul_mat3_m4_v3(mat, vec); + + for (a = 0; a < seg - 1; a++) { + if (a) { + BMO_op_initf(bm, &bmop, "extrude_edge_only edges=%s", &prevop, "geomout"); + BMO_op_exec(bm, &bmop); + BMO_op_finish(bm, &prevop); + + BMO_slot_buffer_flag_enable(bm, &bmop, "geomout", VERT_MARK, BM_VERT); + } + else { + BMO_op_initf(bm, &bmop, "extrude_edge_only edges=%fe", EDGE_ORIG); + BMO_op_exec(bm, &bmop); + BMO_slot_buffer_flag_enable(bm, &bmop, "geomout", VERT_MARK, BM_VERT); + } + + BMO_op_callf(bm, "translate vec=%v verts=%s", vec, &bmop, "geomout"); + prevop = bmop; + } + + if (a) + BMO_op_finish(bm, &bmop); + + BMO_slot_from_flag(bm, op, "vertout", VERT_MARK, BM_VERT); +} + +void bmesh_create_uvsphere_exec(BMesh *bm, BMOperator *op) +{ + BMOperator bmop, prevop; + BMVert *eve, *preveve; + BMEdge *e; + BMIter iter; + float vec[3], mat[4][4], cmat[3][3], phi, q[4]; + float phid, dia = BMO_slot_float_get(op, "diameter"); + int a, seg = BMO_slot_int_get(op, "segments"), tot = BMO_slot_int_get(op, "revolutions"); + + BMO_slot_mat4_get(op, "mat", mat); + + phid = 2.0f * (float)M_PI / tot; + phi = 0.25f * (float)M_PI; + + /* one segment first */ + phi = 0; + phid /= 2; + for (a = 0; a <= tot; a++) { + /* Going in this direction, then edge extruding, makes normals face outward */ + vec[0] = -dia * sinf(phi); + vec[1] = 0.0; + vec[2] = dia * cosf(phi); + eve = BM_vert_create(bm, vec, NULL); + BMO_elem_flag_enable(bm, eve, VERT_MARK); + + if (a != 0) { + e = BM_edge_create(bm, preveve, eve, NULL, FALSE); + BMO_elem_flag_enable(bm, e, EDGE_ORIG); + } + + phi+= phid; + preveve = eve; + } + + /* extrude and rotate; negative phi to make normals face outward */ + phi = -M_PI / seg; + q[0] = cosf(phi); + q[3] = sinf(phi); + q[1] = q[2] = 0.0f; + quat_to_mat3(cmat, q); + + for (a = 0; a < seg; a++) { + if (a) { + BMO_op_initf(bm, &bmop, "extrude_edge_only edges=%s", &prevop, "geomout"); + BMO_op_exec(bm, &bmop); + BMO_op_finish(bm, &prevop); + } + else { + BMO_op_initf(bm, &bmop, "extrude_edge_only edges=%fe", EDGE_ORIG); + BMO_op_exec(bm, &bmop); + } + + BMO_slot_buffer_flag_enable(bm, &bmop, "geomout", VERT_MARK, BM_VERT); + BMO_op_callf(bm, "rotate cent=%v mat=%m3 verts=%s", vec, cmat, &bmop, "geomout"); + + prevop = bmop; + } + + if (a) + BMO_op_finish(bm, &bmop); + + { + float len, len2, vec2[3]; + + len= 2*dia*sinf(phid / 2.0f); + + /* length of one segment in shortest parallen */ + vec[0]= dia*sinf(phid); + vec[1]= 0.0; + vec[2]= dia*cosf(phid); + + mul_v3_m3v3(vec2, cmat, vec); + len2= len_v3v3(vec, vec2); + + /* use shortest segment length divided by 3 as merge threshold */ + BMO_op_callf(bm, "removedoubles verts=%fv dist=%f", VERT_MARK, MIN2(len, len2) / 3.0f); + } + + /* and now do imat */ + BM_ITER(eve, &iter, bm, BM_VERTS_OF_MESH, NULL) { + if (BMO_elem_flag_test(bm, eve, VERT_MARK)) { + mul_m4_v3(mat, eve->co); + } + } + + BMO_slot_from_flag(bm, op, "vertout", VERT_MARK, BM_VERT); +} + +void bmesh_create_icosphere_exec(BMesh *bm, BMOperator *op) +{ + BMVert *eva[12]; + BMVert *v; + BMIter liter; + BMIter viter; + BMLoop *l; + float vec[3], mat[4][4] /* , phi, phid */; + float dia = BMO_slot_float_get(op, "diameter"); + int a, subdiv = BMO_slot_int_get(op, "subdivisions"); + + BMO_slot_mat4_get(op, "mat", mat); + + /* phid = 2.0f * (float)M_PI / subdiv; */ /* UNUSED */ + /* phi = 0.25f * (float)M_PI; */ /* UNUSED */ + + dia /= 200.0f; + for (a = 0; a < 12; a++) { + vec[0] = dia * icovert[a][0]; + vec[1] = dia * icovert[a][1]; + vec[2] = dia * icovert[a][2]; + eva[a] = BM_vert_create(bm, vec, NULL); + + BMO_elem_flag_enable(bm, eva[a], VERT_MARK); + } + + for (a = 0; a < 20; a++) { + BMFace *eftemp; + BMVert *v1, *v2, *v3; + + v1 = eva[icoface[a][0]]; + v2 = eva[icoface[a][1]]; + v3 = eva[icoface[a][2]]; + + eftemp = BM_face_create_quad_tri(bm, v1, v2, v3, NULL, NULL, FALSE); + + BM_ITER(l, &liter, bm, BM_LOOPS_OF_FACE, eftemp) { + BMO_elem_flag_enable(bm, l->e, EDGE_MARK); + } + + BMO_elem_flag_enable(bm, eftemp, FACE_MARK); + } + + dia *= 200.0f; + + for (a = 1; a < subdiv; a++) { + BMOperator bmop; + + BMO_op_initf(bm, &bmop, + "esubd edges=%fe smooth=%f numcuts=%i gridfill=%i beauty=%i", + EDGE_MARK, dia, 1, 1, B_SPHERE); + BMO_op_exec(bm, &bmop); + BMO_slot_buffer_flag_enable(bm, &bmop, "geomout", VERT_MARK, BM_VERT); + BMO_slot_buffer_flag_enable(bm, &bmop, "geomout", EDGE_MARK, BM_EDGE); + BMO_op_finish(bm, &bmop); + } + + /* must transform after becayse of sphere subdivision */ + BM_ITER(v, &viter, bm, BM_VERTS_OF_MESH, NULL) { + if (BMO_elem_flag_test(bm, v, VERT_MARK)) { + mul_m4_v3(mat, v->co); + } + } + + BMO_slot_from_flag(bm, op, "vertout", VERT_MARK, BM_VERT); +} + +void bmesh_create_monkey_exec(BMesh *bm, BMOperator *op) +{ + BMVert *eve; + BMVert **tv = MEM_mallocN(sizeof(*tv)*monkeynv * 2, "tv"); + float mat[4][4]; + int i; + + BMO_slot_mat4_get(op, "mat", mat); + + for (i = 0; i < monkeynv; i++) { + float v[3]; + + v[0] = (monkeyv[i][0] + 127) / 128.0, v[1] = monkeyv[i][1] / 128.0, v[2] = monkeyv[i][2] / 128.0; + + tv[i] = BM_vert_create(bm, v, NULL); + BMO_elem_flag_enable(bm, tv[i], VERT_MARK); + + tv[monkeynv + i] = (fabsf(v[0] = -v[0]) < 0.001f) ? + tv[i] : + (eve = BM_vert_create(bm, v, NULL), mul_m4_v3(mat, eve->co), eve); + + BMO_elem_flag_enable(bm, tv[monkeynv + i], VERT_MARK); + + mul_m4_v3(mat, tv[i]->co); + } + + for (i = 0; i < monkeynf; i++) { + BM_face_create_quad_tri(bm, + tv[monkeyf[i][0] + i - monkeyo], + tv[monkeyf[i][1] + i - monkeyo], + tv[monkeyf[i][2] + i - monkeyo], + (monkeyf[i][3] != monkeyf[i][2]) ? tv[monkeyf[i][3] + i - monkeyo] : NULL, + NULL, FALSE); + + BM_face_create_quad_tri(bm, + tv[monkeynv + monkeyf[i][2] + i - monkeyo], + tv[monkeynv + monkeyf[i][1] + i - monkeyo], + tv[monkeynv + monkeyf[i][0] + i - monkeyo], + (monkeyf[i][3] != monkeyf[i][2]) ? tv[monkeynv + monkeyf[i][3] + i - monkeyo]: NULL, + NULL, FALSE); + } + + MEM_freeN(tv); + + BMO_slot_from_flag(bm, op, "vertout", VERT_MARK, BM_VERT); +} + + +void bmesh_create_circle_exec(BMesh *bm, BMOperator *op) +{ + BMVert *v1, *lastv1 = NULL, *cent1, *firstv1 = NULL; + float vec[3], mat[4][4], phi, phid; + float dia = BMO_slot_float_get(op, "diameter"); + int cap_ends = BMO_slot_int_get(op, "cap_ends"), segs = BMO_slot_int_get(op, "segments"); + int cap_tris = BMO_slot_int_get(op, "cap_tris"); + int a; + + if (!segs) + return; + + BMO_slot_mat4_get(op, "mat", mat); + + phid = 2.0f * (float)M_PI / segs; + phi = .25f * (float)M_PI; + + if (cap_ends) { + vec[0] = vec[1] = 0.0f; + vec[2] = 0.0; + mul_m4_v3(mat, vec); + + cent1 = BM_vert_create(bm, vec, NULL); + } + + for (a = 0; a < segs; a++, phi += phid) { + /* Going this way ends up with normal(s) upward */ + vec[0] = -dia * sinf(phi); + vec[1] = dia * cosf(phi); + vec[2] = 0.0f; + mul_m4_v3(mat, vec); + v1 = BM_vert_create(bm, vec, NULL); + + BMO_elem_flag_enable(bm, v1, VERT_MARK); + + if (lastv1) + BM_edge_create(bm, v1, lastv1, NULL, FALSE); + + if (a && cap_ends) { + BMFace *f; + + f = BM_face_create_quad_tri(bm, cent1, lastv1, v1, NULL, NULL, FALSE); + BMO_elem_flag_enable(bm, f, FACE_NEW); + } + + if (!firstv1) + firstv1 = v1; + + lastv1 = v1; + } + + if (!a) + return; + + BM_edge_create(bm, lastv1, firstv1, NULL, FALSE); + + if (cap_ends) { + BMFace *f; + + f = BM_face_create_quad_tri(bm, cent1, v1, firstv1, NULL, NULL, FALSE); + BMO_elem_flag_enable(bm, f, FACE_NEW); + } + + if (!cap_tris) { + BMO_op_callf(bm, "dissolvefaces faces=%ff", FACE_NEW); + } + + BMO_slot_from_flag(bm, op, "vertout", VERT_MARK, BM_VERT); +} + +void bmesh_create_cone_exec(BMesh *bm, BMOperator *op) +{ + BMVert *v1, *v2, *lastv1 = NULL, *lastv2 = NULL, *cent1, *cent2, *firstv1, *firstv2; + float vec[3], mat[4][4], phi, phid; + float dia1 = BMO_slot_float_get(op, "diameter1"); + float dia2 = BMO_slot_float_get(op, "diameter2"); + float depth = BMO_slot_float_get(op, "depth"); + int cap_ends = BMO_slot_int_get(op, "cap_ends"), segs = BMO_slot_int_get(op, "segments"); + int cap_tris = BMO_slot_int_get(op, "cap_tris"); + int a; + + if (!segs) + return; + + BMO_slot_mat4_get(op, "mat", mat); + + phid = 2.0f * (float)M_PI / segs; + phi = 0.25f * (float)M_PI; + + depth *= 0.5f; + if (cap_ends) { + vec[0] = vec[1] = 0.0f; + vec[2] = -depth; + mul_m4_v3(mat, vec); + + cent1 = BM_vert_create(bm, vec, NULL); + + vec[0] = vec[1] = 0.0f; + vec[2] = depth; + mul_m4_v3(mat, vec); + + cent2 = BM_vert_create(bm, vec, NULL); + + BMO_elem_flag_enable(bm, cent1, VERT_MARK); + BMO_elem_flag_enable(bm, cent2, VERT_MARK); + } + + for (a = 0; a < segs; a++, phi += phid) { + vec[0] = dia1 * sinf(phi); + vec[1] = dia1 * cosf(phi); + vec[2] = -depth; + mul_m4_v3(mat, vec); + v1 = BM_vert_create(bm, vec, NULL); + + vec[0] = dia2 * sinf(phi); + vec[1] = dia2 * cosf(phi); + vec[2] = depth; + mul_m4_v3(mat, vec); + v2 = BM_vert_create(bm, vec, NULL); + + BMO_elem_flag_enable(bm, v1, VERT_MARK); + BMO_elem_flag_enable(bm, v2, VERT_MARK); + + if (a) { + if (cap_ends) { + BMFace *f; + + f = BM_face_create_quad_tri(bm, cent1, lastv1, v1, NULL, NULL, FALSE); + BMO_elem_flag_enable(bm, f, FACE_NEW); + f = BM_face_create_quad_tri(bm, cent2, v2, lastv2, NULL, NULL, FALSE); + BMO_elem_flag_enable(bm, f, FACE_NEW); + } + BM_face_create_quad_tri(bm, lastv1, lastv2, v2, v1, NULL, FALSE); + } + else { + firstv1 = v1; + firstv2 = v2; + } + + lastv1 = v1; + lastv2 = v2; + } + + if (!a) + return; + + if (cap_ends) { + BMFace *f; + + f = BM_face_create_quad_tri(bm, cent1, v1, firstv1, NULL, NULL, FALSE); + BMO_elem_flag_enable(bm, f, FACE_NEW); + f = BM_face_create_quad_tri(bm, cent2, firstv2, v2, NULL, NULL, FALSE); + BMO_elem_flag_enable(bm, f, FACE_NEW); + } + + if (!cap_tris) { + BMO_op_callf(bm, "dissolvefaces faces=%ff", FACE_NEW); + } + + BM_face_create_quad_tri(bm, v1, v2, firstv2, firstv1, NULL, FALSE); + + BMO_op_callf(bm, "removedoubles verts=%fv dist=%f", VERT_MARK, 0.000001); + BMO_slot_from_flag(bm, op, "vertout", VERT_MARK, BM_VERT); +} + +void bmesh_create_cube_exec(BMesh *bm, BMOperator *op) +{ + BMVert *v1, *v2, *v3, *v4, *v5, *v6, *v7, *v8; + float vec[3], mat[4][4], off = BMO_slot_float_get(op, "size") / 2.0f; + + BMO_slot_mat4_get(op, "mat", mat); + + if (!off) off = 0.5f; + + vec[0] = -off; + vec[1] = -off; + vec[2] = -off; + mul_m4_v3(mat, vec); + v1 = BM_vert_create(bm, vec, NULL); + BMO_elem_flag_enable(bm, v1, VERT_MARK); + + vec[0] = -off; + vec[1] = off; + vec[2] = -off; + mul_m4_v3(mat, vec); + v2 = BM_vert_create(bm, vec, NULL); + BMO_elem_flag_enable(bm, v2, VERT_MARK); + + vec[0] = off; + vec[1] = off; + vec[2] = -off; + mul_m4_v3(mat, vec); + v3 = BM_vert_create(bm, vec, NULL); + BMO_elem_flag_enable(bm, v3, VERT_MARK); + + vec[0] = off; + vec[1] = -off; + vec[2] = -off; + mul_m4_v3(mat, vec); + v4 = BM_vert_create(bm, vec, NULL); + BMO_elem_flag_enable(bm, v4, VERT_MARK); + + vec[0] = -off; + vec[1] = -off; + vec[2] = off; + mul_m4_v3(mat, vec); + v5 = BM_vert_create(bm, vec, NULL); + BMO_elem_flag_enable(bm, v5, VERT_MARK); + + vec[0] = -off; + vec[1] = off; + vec[2] = off; + mul_m4_v3(mat, vec); + v6 = BM_vert_create(bm, vec, NULL); + BMO_elem_flag_enable(bm, v6, VERT_MARK); + + vec[0] = off; + vec[1] = off; + vec[2] = off; + mul_m4_v3(mat, vec); + v7 = BM_vert_create(bm, vec, NULL); + BMO_elem_flag_enable(bm, v7, VERT_MARK); + + vec[0] = off; + vec[1] = -off; + vec[2] = off; + mul_m4_v3(mat, vec); + v8 = BM_vert_create(bm, vec, NULL); + BMO_elem_flag_enable(bm, v8, VERT_MARK); + + /* the four sides */ + BM_face_create_quad_tri(bm, v5, v6, v2, v1, NULL, FALSE); + BM_face_create_quad_tri(bm, v6, v7, v3, v2, NULL, FALSE); + BM_face_create_quad_tri(bm, v7, v8, v4, v3, NULL, FALSE); + BM_face_create_quad_tri(bm, v8, v5, v1, v4, NULL, FALSE); + + /* top/bottom */ + BM_face_create_quad_tri(bm, v1, v2, v3, v4, NULL, FALSE); + BM_face_create_quad_tri(bm, v8, v7, v6, v5, NULL, FALSE); + + BMO_slot_from_flag(bm, op, "vertout", VERT_MARK, BM_VERT); +} diff --git a/source/blender/bmesh/operators/bmo_removedoubles.c b/source/blender/bmesh/operators/bmo_removedoubles.c new file mode 100644 index 00000000000..2eb1cf7db3e --- /dev/null +++ b/source/blender/bmesh/operators/bmo_removedoubles.c @@ -0,0 +1,590 @@ +/* + * ***** 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_array.h" + +#include "BKE_customdata.h" + +#include "bmesh.h" +#include "bmesh_private.h" + +#include "bmesh_operators_private.h" /* own include */ + +static void remdoubles_splitface(BMFace *f, BMesh *bm, BMOperator *op) +{ + BMIter liter; + BMLoop *l; + BMVert *v2, *doub; + int split = FALSE; + + BM_ITER(l, &liter, bm, BM_LOOPS_OF_FACE, f) { + v2 = BMO_slot_map_ptr_get(bm, op, "targetmap", l->v); + /* ok: if v2 is NULL (e.g. not in the map) then it's + * a target vert, otherwise it's a doubl */ + if ((v2 && BM_vert_in_face(f, v2)) && + (v2 != l->prev->v) && + (v2 != l->next->v)) + { + doub = l->v; + split = TRUE; + break; + } + } + + if (split && doub != v2) { + BMLoop *nl; + BMFace *f2 = BM_face_split(bm, f, doub, v2, &nl, NULL); + + remdoubles_splitface(f, bm, op); + remdoubles_splitface(f2, bm, op); + } +} + +#define ELE_DEL 1 +#define EDGE_COL 2 +#define FACE_MARK 2 + +#if 0 +int remdoubles_face_overlaps(BMesh *bm, BMVert **varr, + int len, BMFace *exclude, + BMFace **overlapface) +{ + BMIter vertfaces; + BMFace *f; + int i, amount; + + if (overlapface) *overlapface = NULL; + + for (i = 0; i < len; i++) { + f = BM_iter_new(&vertfaces, bm, BM_FACES_OF_VERT, varr[i]); + while (f) { + amount = BM_verts_in_face(bm, f, varr, len); + if (amount >= len) { + if (overlapface) *overlapface = f; + return TRUE; + } + f = BM_iter_step(&vertfaces); + } + } + return FALSE; +} +#endif + +void bmesh_weldverts_exec(BMesh *bm, BMOperator *op) +{ + BMIter iter, liter; + BMVert *v, *v2; + BMEdge *e, *e2, **edges = NULL; + BLI_array_declare(edges); + BMLoop *l, *l2, **loops = NULL; + BLI_array_declare(loops); + BMFace *f, *f2; + int a, b; + + BM_ITER(v, &iter, bm, BM_VERTS_OF_MESH, NULL) { + if ((v2 = BMO_slot_map_ptr_get(bm, op, "targetmap", v))) { + BMO_elem_flag_enable(bm, v, ELE_DEL); + + /* merge the vertex flags, else we get randomly selected/unselected verts */ + BM_elem_flag_merge(v, v2); + } + } + + BM_ITER(f, &iter, bm, BM_FACES_OF_MESH, NULL) { + remdoubles_splitface(f, bm, op); + } + + BM_ITER(e, &iter, bm, BM_EDGES_OF_MESH, NULL) { + if (BMO_elem_flag_test(bm, e->v1, ELE_DEL) || BMO_elem_flag_test(bm, e->v2, ELE_DEL)) { + v = BMO_slot_map_ptr_get(bm, op, "targetmap", e->v1); + v2 = BMO_slot_map_ptr_get(bm, op, "targetmap", e->v2); + + if (!v) v = e->v1; + if (!v2) v2 = e->v2; + + if (v == v2) + BMO_elem_flag_enable(bm, e, EDGE_COL); + else if (!BM_edge_exists(v, v2)) + BM_edge_create(bm, v, v2, e, TRUE); + + BMO_elem_flag_enable(bm, e, ELE_DEL); + } + } + + /* BMESH_TODO, stop abusing face index here */ + BM_ITER(f, &iter, bm, BM_FACES_OF_MESH, NULL) { + BM_elem_index_set(f, 0); /* set_dirty! */ + BM_ITER(l, &liter, bm, BM_LOOPS_OF_FACE, f) { + if (BMO_elem_flag_test(bm, l->v, ELE_DEL)) { + BMO_elem_flag_enable(bm, f, FACE_MARK|ELE_DEL); + } + if (BMO_elem_flag_test(bm, l->e, EDGE_COL)) { + BM_elem_index_set(f, BM_elem_index_get(f) + 1); /* set_dirty! */ + } + } + } + bm->elem_index_dirty |= BM_FACE; + + BM_ITER(f, &iter, bm, BM_FACES_OF_MESH, NULL) { + if (!BMO_elem_flag_test(bm, f, FACE_MARK)) + continue; + + if (f->len - BM_elem_index_get(f) < 3) { + BMO_elem_flag_enable(bm, f, ELE_DEL); + continue; + } + + BLI_array_empty(edges); + BLI_array_empty(loops); + a = 0; + BM_ITER(l, &liter, bm, BM_LOOPS_OF_FACE, f) { + v = l->v; + v2 = l->next->v; + if (BMO_elem_flag_test(bm, v, ELE_DEL)) { + v = BMO_slot_map_ptr_get(bm, op, "targetmap", v); + } + if (BMO_elem_flag_test(bm, v2, ELE_DEL)) { + v2 = BMO_slot_map_ptr_get(bm, op, "targetmap", v2); + } + + e2 = v != v2 ? BM_edge_exists(v, v2) : NULL; + if (e2) { + for (b = 0; b < a; b++) { + if (edges[b] == e2) { + break; + } + } + if (b != a) { + continue; + } + + BLI_array_growone(edges); + BLI_array_growone(loops); + + edges[a] = e2; + loops[a] = l; + + a++; + } + } + + if (BLI_array_count(loops) < 3) + continue; + v = loops[0]->v; + v2 = loops[1]->v; + + if (BMO_elem_flag_test(bm, v, ELE_DEL)) { + v = BMO_slot_map_ptr_get(bm, op, "targetmap", v); + } + if (BMO_elem_flag_test(bm, v2, ELE_DEL)) { + v2 = BMO_slot_map_ptr_get(bm, op, "targetmap", v2); + } + + f2 = BM_face_create_ngon(bm, v, v2, edges, a, TRUE); + if (f2 && (f2 != f)) { + BM_elem_attrs_copy(bm, bm, f, f2); + + a = 0; + BM_ITER(l, &liter, bm, BM_LOOPS_OF_FACE, f2) { + l2 = loops[a]; + BM_elem_attrs_copy(bm, bm, l2, l); + + a++; + } + } + } + + BMO_op_callf(bm, "del geom=%fvef context=%i", ELE_DEL, DEL_ONLYTAGGED); + + BLI_array_free(edges); + BLI_array_free(loops); +} + +static int vergaverco(const void *e1, const void *e2) +{ + const BMVert *v1 = *(void **)e1, *v2 = *(void **)e2; + float x1 = v1->co[0] + v1->co[1] + v1->co[2]; + float x2 = v2->co[0] + v2->co[1] + v2->co[2]; + + if (x1 > x2) return 1; + else if (x1 < x2) return -1; + else return 0; +} + +#define VERT_TESTED 1 +#define VERT_DOUBLE 2 +#define VERT_TARGET 4 +#define VERT_KEEP 8 +#define VERT_MARK 16 +#define VERT_IN 32 + +#define EDGE_MARK 1 + +void bmesh_pointmerge_facedata_exec(BMesh *bm, BMOperator *op) +{ + BMOIter siter; + BMIter iter; + BMVert *v, *snapv; + BMLoop *l, *firstl = NULL; + float fac; + int i, tot; + + snapv = BMO_iter_new(&siter, bm, op, "snapv", BM_VERT); + tot = BM_vert_face_count(snapv); + + if (!tot) + return; + + fac = 1.0f / tot; + BM_ITER(l, &iter, bm, BM_LOOPS_OF_VERT, snapv) { + if (!firstl) { + firstl = l; + } + + for (i = 0; i < bm->ldata.totlayer; i++) { + if (CustomData_layer_has_math(&bm->ldata, i)) { + int type = bm->ldata.layers[i].type; + void *e1, *e2; + + e1 = CustomData_bmesh_get_layer_n(&bm->ldata, firstl->head.data, i); + e2 = CustomData_bmesh_get_layer_n(&bm->ldata, l->head.data, i); + + CustomData_data_multiply(type, e2, fac); + + if (l != firstl) + CustomData_data_add(type, e1, e2); + } + } + } + + BMO_ITER(v, &siter, bm, op, "verts", BM_VERT) { + BM_ITER(l, &iter, bm, BM_LOOPS_OF_VERT, v) { + if (l == firstl) { + continue; + } + + CustomData_bmesh_copy_data(&bm->ldata, &bm->ldata, firstl->head.data, &l->head.data); + } + } +} + +void bmesh_vert_average_facedata_exec(BMesh *bm, BMOperator *op) +{ + BMOIter siter; + BMIter iter; + BMVert *v; + BMLoop *l /* , *firstl = NULL */; + CDBlockBytes min, max; + void *block; + int i, type; + + for (i = 0; i < bm->ldata.totlayer; i++) { + if (!CustomData_layer_has_math(&bm->ldata, i)) + continue; + + type = bm->ldata.layers[i].type; + CustomData_data_initminmax(type, &min, &max); + + BMO_ITER(v, &siter, bm, op, "verts", BM_VERT) { + BM_ITER(l, &iter, bm, BM_LOOPS_OF_VERT, v) { + block = CustomData_bmesh_get_layer_n(&bm->ldata, l->head.data, i); + CustomData_data_dominmax(type, block, &min, &max); + } + } + + CustomData_data_multiply(type, &min, 0.5f); + CustomData_data_multiply(type, &max, 0.5f); + CustomData_data_add(type, &min, &max); + + BMO_ITER(v, &siter, bm, op, "verts", BM_VERT) { + BM_ITER(l, &iter, bm, BM_LOOPS_OF_VERT, v) { + block = CustomData_bmesh_get_layer_n(&bm->ldata, l->head.data, i); + CustomData_data_copy_value(type, &min, block); + } + } + } +} + +void bmesh_pointmerge_exec(BMesh *bm, BMOperator *op) +{ + BMOperator weldop; + BMOIter siter; + BMVert *v, *snapv = NULL; + float vec[3]; + + BMO_slot_vec_get(op, "mergeco", vec); + + //BMO_op_callf(bm, "collapse_uvs edges=%s", op, "edges"); + BMO_op_init(bm, &weldop, "weldverts"); + + BMO_ITER(v, &siter, bm, op, "verts", BM_VERT) { + if (!snapv) { + snapv = v; + copy_v3_v3(snapv->co, vec); + } + else { + BMO_slot_map_ptr_insert(bm, &weldop, "targetmap", v, snapv); + } + } + + BMO_op_exec(bm, &weldop); + BMO_op_finish(bm, &weldop); +} + +void bmesh_collapse_exec(BMesh *bm, BMOperator *op) +{ + BMOperator weldop; + BMWalker walker; + BMIter iter; + BMEdge *e, **edges = NULL; + BLI_array_declare(edges); + float min[3], max[3]; + int i, tot; + + BMO_op_callf(bm, "collapse_uvs edges=%s", op, "edges"); + BMO_op_init(bm, &weldop, "weldverts"); + + BMO_slot_buffer_flag_enable(bm, op, "edges", EDGE_MARK, BM_EDGE); + + BMW_init(&walker, bm, BMW_SHELL, + BMW_MASK_NOP, EDGE_MARK, BMW_MASK_NOP, BMW_MASK_NOP, + BMW_NIL_LAY); + + BM_ITER(e, &iter, bm, BM_EDGES_OF_MESH, NULL) { + if (!BMO_elem_flag_test(bm, e, EDGE_MARK)) + continue; + + e = BMW_begin(&walker, e->v1); + BLI_array_empty(edges); + + INIT_MINMAX(min, max); + for (tot = 0; e; tot++, e = BMW_step(&walker)) { + BLI_array_growone(edges); + edges[tot] = e; + + DO_MINMAX(e->v1->co, min, max); + DO_MINMAX(e->v2->co, min, max); + } + + add_v3_v3v3(min, min, max); + mul_v3_fl(min, 0.5f); + + /* snap edges to a point. for initial testing purposes anyway */ + for (i = 0; i < tot; i++) { + copy_v3_v3(edges[i]->v1->co, min); + copy_v3_v3(edges[i]->v2->co, min); + + if (edges[i]->v1 != edges[0]->v1) + BMO_slot_map_ptr_insert(bm, &weldop, "targetmap", edges[i]->v1, edges[0]->v1); + if (edges[i]->v2 != edges[0]->v1) + BMO_slot_map_ptr_insert(bm, &weldop, "targetmap", edges[i]->v2, edges[0]->v1); + } + } + + BMO_op_exec(bm, &weldop); + BMO_op_finish(bm, &weldop); + + BMW_end(&walker); + BLI_array_free(edges); +} + +/* uv collapse functio */ +static void bmesh_collapsecon_do_layer(BMesh *bm, BMOperator *op, int layer) +{ + BMIter iter, liter; + BMFace *f; + BMLoop *l, *l2; + BMWalker walker; + void **blocks = NULL; + BLI_array_declare(blocks); + CDBlockBytes min, max; + int i, tot, type = bm->ldata.layers[layer].type; + + /* clear all short flags */ + BMO_mesh_flag_disable_all(bm, op, BM_ALL, (1 << 16) - 1); + + BMO_slot_buffer_flag_enable(bm, op, "edges", EDGE_MARK, BM_EDGE); + + BMW_init(&walker, bm, BMW_LOOPDATA_ISLAND, + BMW_MASK_NOP, EDGE_MARK, BMW_MASK_NOP, BMW_MASK_NOP, + layer); + + BM_ITER(f, &iter, bm, BM_FACES_OF_MESH, NULL) { + BM_ITER(l, &liter, bm, BM_LOOPS_OF_FACE, f) { + if (BMO_elem_flag_test(bm, l->e, EDGE_MARK)) { + /* wal */ + BLI_array_empty(blocks); + tot = 0; + l2 = BMW_begin(&walker, l); + + CustomData_data_initminmax(type, &min, &max); + for (tot = 0; l2; tot++, l2 = BMW_step(&walker)) { + BLI_array_growone(blocks); + blocks[tot] = CustomData_bmesh_get_layer_n(&bm->ldata, l2->head.data, layer); + CustomData_data_dominmax(type, blocks[tot], &min, &max); + } + + if (tot) { + CustomData_data_multiply(type, &min, 0.5f); + CustomData_data_multiply(type, &max, 0.5f); + CustomData_data_add(type, &min, &max); + + /* snap CD (uv, vcol) points to their centroi */ + for (i = 0; i < tot; i++) { + CustomData_data_copy_value(type, &min, blocks[i]); + } + } + } + } + } + + BMW_end(&walker); + BLI_array_free(blocks); +} + +void bmesh_collapsecon_exec(BMesh *bm, BMOperator *op) +{ + int i; + + for (i = 0; i < bm->ldata.totlayer; i++) { + if (CustomData_layer_has_math(&bm->ldata, i)) + bmesh_collapsecon_do_layer(bm, op, i); + } +} + +void bmesh_finddoubles_common(BMesh *bm, BMOperator *op, BMOperator *optarget, const char *targetmapname) +{ + BMOIter oiter; + BMVert *v, *v2; + BMVert **verts = NULL; + BLI_array_declare(verts); + float dist, dist3; + int i, j, len, keepvert = 0; + + dist = BMO_slot_float_get(op, "dist"); + dist3 = dist * 3.0f; + + i = 0; + BMO_ITER(v, &oiter, bm, op, "verts", BM_VERT) { + BLI_array_growone(verts); + verts[i++] = v; + } + + /* Test whether keepverts arg exists and is non-empty */ + if (BMO_slot_exists(op, "keepverts")) { + keepvert = BMO_iter_new(&oiter, bm, op, "keepverts", BM_VERT) != NULL; + } + + /* sort by vertex coordinates added togethe */ + qsort(verts, BLI_array_count(verts), sizeof(void *), vergaverco); + + /* Flag keepverts */ + if (keepvert) { + BMO_slot_buffer_flag_enable(bm, op, "keepverts", VERT_KEEP, BM_VERT); + } + + len = BLI_array_count(verts); + for (i = 0; i < len; i++) { + v = verts[i]; + if (BMO_elem_flag_test(bm, v, VERT_DOUBLE)) continue; + + for (j = i + 1; j < len; j++) { + v2 = verts[j]; + + /* Compare sort values of the verts using 3x tolerance (allowing for the tolerance + * on each of the three axes). This avoids the more expensive length comparison + * for most vertex pairs. */ + if ((v2->co[0]+v2->co[1]+v2->co[2])-(v->co[0]+v->co[1]+v->co[2]) > dist3) + break; + + if (keepvert) { + if (BMO_elem_flag_test(bm, v2, VERT_KEEP) == BMO_elem_flag_test(bm, v, VERT_KEEP)) + continue; + } + + if (compare_len_v3v3(v->co, v2->co, dist)) { + + /* If one vert is marked as keep, make sure it will be the target */ + if (BMO_elem_flag_test(bm, v2, VERT_KEEP)) { + SWAP(BMVert *, v, v2); + } + + BMO_elem_flag_enable(bm, v2, VERT_DOUBLE); + BMO_elem_flag_enable(bm, v, VERT_TARGET); + + BMO_slot_map_ptr_insert(bm, optarget, targetmapname, v2, v); + } + } + } + + BLI_array_free(verts); +} + +void bmesh_removedoubles_exec(BMesh *bm, BMOperator *op) +{ + BMOperator weldop; + + BMO_op_init(bm, &weldop, "weldverts"); + bmesh_finddoubles_common(bm, op, &weldop, "targetmap"); + BMO_op_exec(bm, &weldop); + BMO_op_finish(bm, &weldop); +} + + +void bmesh_finddoubles_exec(BMesh *bm, BMOperator *op) +{ + bmesh_finddoubles_common(bm, op, op, "targetmapout"); +} + +void bmesh_automerge_exec(BMesh *bm, BMOperator *op) +{ + BMOperator findop, weldop; + BMIter viter; + BMVert *v; + + /* The "verts" input sent to this op is the set of verts that + * can be merged away into any other verts. Mark all other verts + * as VERT_KEEP. */ + BMO_slot_buffer_flag_enable(bm, op, "verts", VERT_IN, BM_VERT); + BM_ITER(v, &viter, bm, BM_VERTS_OF_MESH, NULL) { + if (!BMO_elem_flag_test(bm, v, VERT_IN)) { + BMO_elem_flag_enable(bm, v, VERT_KEEP); + } + } + + /* Search for doubles among all vertices, but only merge non-VERT_KEEP + * vertices into VERT_KEEP vertices. */ + BMO_op_initf(bm, &findop, "finddoubles verts=%av keepverts=%fv", VERT_KEEP); + BMO_slot_copy(op, &findop, "dist", "dist"); + BMO_op_exec(bm, &findop); + + /* weld the vertices */ + BMO_op_init(bm, &weldop, "weldverts"); + BMO_slot_copy(&findop, &weldop, "targetmapout", "targetmap"); + BMO_op_exec(bm, &weldop); + + BMO_op_finish(bm, &findop); + BMO_op_finish(bm, &weldop); +} 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); +} diff --git a/source/blender/bmesh/operators/bmo_subdivide.h b/source/blender/bmesh/operators/bmo_subdivide.h new file mode 100644 index 00000000000..dd5198bdc30 --- /dev/null +++ b/source/blender/bmesh/operators/bmo_subdivide.h @@ -0,0 +1,66 @@ +/* + * ***** 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 ***** + */ + +#ifndef __BMO_SUBDIVIDE_H__ +#define __BMO_SUBDIVIDE_H__ + +/** \file blender/bmesh/operators/bmo_subdivide.h + * \ingroup bmesh + */ + +typedef struct subdparams { + int numcuts; + float smooth; + float fractal; + int beauty; + int seed; + int origkey; /* shapekey holding displaced vertex coordinates for current geometry */ + BMOperator *op; + float off[3]; +} subdparams; + +typedef void (*subd_pattern_fill_fp)(BMesh *bm, BMFace *face, BMVert **verts, + const subdparams *params); + +/* + * 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 */ + subd_pattern_fill_fp connectexec; + int len; /* total number of verts, before any subdivision */ +} 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? + */ + +#endif /* __BMO_SUBDIVIDE_H__ */ diff --git a/source/blender/bmesh/operators/bmo_triangulate.c b/source/blender/bmesh/operators/bmo_triangulate.c new file mode 100644 index 00000000000..90efe0b6e44 --- /dev/null +++ b/source/blender/bmesh/operators/bmo_triangulate.c @@ -0,0 +1,219 @@ +/* + * ***** 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_scanfill.h" +#include "BLI_math.h" +#include "BLI_array.h" +#include "BLI_editVert.h" +#include "BLI_smallhash.h" + +#include "bmesh.h" +#include "bmesh_private.h" + +#include "bmesh_operators_private.h" /* own include */ + +#define EDGE_NEW 1 +#define FACE_NEW 1 + +#define ELE_NEW 1 +#define FACE_MARK 2 +#define EDGE_MARK 4 + +void triangulate_exec(BMesh *bm, BMOperator *op) +{ + BMOIter siter; + BMFace *face, **newfaces = NULL; + BLI_array_declare(newfaces); + float (*projectverts)[3] = NULL; + BLI_array_declare(projectverts); + int i, lastlen = 0 /* , count = 0 */; + + face = BMO_iter_new(&siter, bm, op, "faces", BM_FACE); + for ( ; face; face = BMO_iter_step(&siter)) { + if (lastlen < face->len) { + BLI_array_empty(projectverts); + BLI_array_empty(newfaces); + for (lastlen = 0; lastlen < face->len; lastlen++) { + BLI_array_growone(projectverts); + BLI_array_growone(projectverts); + BLI_array_growone(projectverts); + BLI_array_growone(newfaces); + } + } + + BM_face_triangulate(bm, face, projectverts, EDGE_NEW, FACE_NEW, newfaces); + + BMO_slot_map_ptr_insert(bm, op, "facemap", face, face); + for (i = 0; newfaces[i]; i++) { + BMO_slot_map_ptr_insert(bm, op, "facemap", + newfaces[i], face); + + } + } + + BMO_slot_from_flag(bm, op, "edgeout", EDGE_NEW, BM_EDGE); + BMO_slot_from_flag(bm, op, "faceout", FACE_NEW, BM_FACE); + + BLI_array_free(projectverts); + BLI_array_free(newfaces); +} + +void bmesh_beautify_fill_exec(BMesh *bm, BMOperator *op) +{ + BMOIter siter; + BMIter iter; + BMFace *f; + BMEdge *e; + int stop = 0; + + BMO_slot_buffer_flag_enable(bm, op, "constrain_edges", EDGE_MARK, BM_EDGE); + + BMO_ITER(f, &siter, bm, op, "faces", BM_FACE) { + if (f->len == 3) + BMO_elem_flag_enable(bm, f, FACE_MARK); + } + + while (!stop) { + stop = 1; + + BM_ITER(e, &iter, bm, BM_EDGES_OF_MESH, NULL) { + BMVert *v1, *v2, *v3, *v4; + + if (BM_edge_face_count(e) != 2 || BMO_elem_flag_test(bm, e, EDGE_MARK)) { + continue; + } + + if (!BMO_elem_flag_test(bm, e->l->f, FACE_MARK) || + !BMO_elem_flag_test(bm, e->l->radial_next->f, FACE_MARK)) + { + continue; + } + + v1 = e->l->prev->v; + v2 = e->l->v; + v3 = e->l->radial_next->prev->v; + v4 = e->l->next->v; + + if (is_quad_convex_v3(v1->co, v2->co, v3->co, v4->co)) { + float len1, len2, len3, len4, len5, len6, opp1, opp2, fac1, fac2; + /* testing rule: + * the area divided by the total edge lengths + */ + len1 = len_v3v3(v1->co, v2->co); + len2 = len_v3v3(v2->co, v3->co); + len3 = len_v3v3(v3->co, v4->co); + len4 = len_v3v3(v4->co, v1->co); + len5 = len_v3v3(v1->co, v3->co); + len6 = len_v3v3(v2->co, v4->co); + + opp1 = area_tri_v3(v1->co, v2->co, v3->co); + opp2 = area_tri_v3(v1->co, v3->co, v4->co); + + fac1 = opp1 / (len1 + len2 + len5) + opp2 / (len3 + len4 + len5); + + opp1 = area_tri_v3(v2->co, v3->co, v4->co); + opp2 = area_tri_v3(v2->co, v4->co, v1->co); + + fac2 = opp1 / (len2 + len3 + len6) + opp2 / (len4 + len1 + len6); + + if (fac1 > fac2) { + e = BM_edge_rotate(bm, e, 0); + if (e) { + BMO_elem_flag_enable(bm, e, ELE_NEW); + + BMO_elem_flag_enable(bm, e->l->f, FACE_MARK|ELE_NEW); + BMO_elem_flag_enable(bm, e->l->radial_next->f, FACE_MARK|ELE_NEW); + stop = 0; + } + } + } + } + } + + BMO_slot_from_flag(bm, op, "geomout", ELE_NEW, BM_EDGE|BM_FACE); +} + +void bmesh_triangle_fill_exec(BMesh *bm, BMOperator *op) +{ + BMOIter siter; + BMEdge *e; + BMOperator bmop; + EditEdge *eed; + EditVert *eve, *v1, *v2; + EditFace *efa; + SmallHash hash; + + BLI_smallhash_init(&hash); + + BLI_begin_edgefill(); + + BMO_ITER(e, &siter, bm, op, "edges", BM_EDGE) { + BMO_elem_flag_enable(bm, e, EDGE_MARK); + + if (!BLI_smallhash_haskey(&hash, (uintptr_t)e->v1)) { + eve = BLI_addfillvert(e->v1->co); + eve->tmp.p = e->v1; + BLI_smallhash_insert(&hash, (uintptr_t)e->v1, eve); + } + + if (!BLI_smallhash_haskey(&hash, (uintptr_t)e->v2)) { + eve = BLI_addfillvert(e->v2->co); + eve->tmp.p = e->v2; + BLI_smallhash_insert(&hash, (uintptr_t)e->v2, eve); + } + + v1 = BLI_smallhash_lookup(&hash, (uintptr_t)e->v1); + v2 = BLI_smallhash_lookup(&hash, (uintptr_t)e->v2); + eed = BLI_addfilledge(v1, v2); + eed->tmp.p = e; + } + + BLI_edgefill(0); + + for (efa = fillfacebase.first; efa; efa = efa->next) { + BMFace *f = BM_face_create_quad_tri(bm, + efa->v1->tmp.p, efa->v2->tmp.p, efa->v3->tmp.p, NULL, + NULL, TRUE); + BMLoop *l; + BMIter liter; + + BMO_elem_flag_enable(bm, f, ELE_NEW); + BM_ITER(l, &liter, bm, BM_LOOPS_OF_FACE, f) { + if (!BMO_elem_flag_test(bm, l->e, EDGE_MARK)) { + BMO_elem_flag_enable(bm, l->e, ELE_NEW); + } + } + } + + BLI_end_edgefill(); + BLI_smallhash_release(&hash); + + /* clean up fill */ + BMO_op_initf(bm, &bmop, "beautify_fill faces=%ff constrain_edges=%fe", ELE_NEW, EDGE_MARK); + BMO_op_exec(bm, &bmop); + BMO_slot_buffer_flag_enable(bm, &bmop, "geomout", ELE_NEW, BM_FACE|BM_EDGE); + BMO_op_finish(bm, &bmop); + + BMO_slot_from_flag(bm, op, "geomout", ELE_NEW, BM_EDGE|BM_FACE); +} diff --git a/source/blender/bmesh/operators/bmo_utils.c b/source/blender/bmesh/operators/bmo_utils.c new file mode 100644 index 00000000000..e0187476a6a --- /dev/null +++ b/source/blender/bmesh/operators/bmo_utils.c @@ -0,0 +1,1297 @@ +/* + * ***** 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 "DNA_meshdata_types.h" + +#include "BLI_math.h" +#include "BLI_array.h" +#include "BLI_heap.h" + +#include "BKE_customdata.h" + +#include "bmesh.h" + +#include "bmesh_operators_private.h" /* own include */ + +/* + * UTILS.C + * + * utility bmesh operators, e.g. transform, + * translate, rotate, scale, etc. + * + */ + +void bmesh_makevert_exec(BMesh *bm, BMOperator *op) +{ + float vec[3]; + + BMO_slot_vec_get(op, "co", vec); + + BMO_elem_flag_enable(bm, BM_vert_create(bm, vec, NULL), 1); + BMO_slot_from_flag(bm, op, "newvertout", 1, BM_VERT); +} + +void bmesh_transform_exec(BMesh *bm, BMOperator *op) +{ + BMOIter iter; + BMVert *v; + float mat[4][4]; + + BMO_slot_mat4_get(op, "mat", mat); + + BMO_ITER(v, &iter, bm, op, "verts", BM_VERT) { + mul_m4_v3(mat, v->co); + } +} + +void bmesh_translate_exec(BMesh *bm, BMOperator *op) +{ + float mat[4][4], vec[3]; + + BMO_slot_vec_get(op, "vec", vec); + + unit_m4(mat); + copy_v3_v3(mat[3], vec); + + BMO_op_callf(bm, "transform mat=%m4 verts=%s", mat, op, "verts"); +} + +void bmesh_scale_exec(BMesh *bm, BMOperator *op) +{ + float mat[3][3], vec[3]; + + BMO_slot_vec_get(op, "vec", vec); + + unit_m3(mat); + mat[0][0] = vec[0]; + mat[1][1] = vec[1]; + mat[2][2] = vec[2]; + + BMO_op_callf(bm, "transform mat=%m3 verts=%s", mat, op, "verts"); +} + +void bmesh_rotate_exec(BMesh *bm, BMOperator *op) +{ + float vec[3]; + + BMO_slot_vec_get(op, "cent", vec); + + /* there has to be a proper matrix way to do this, but + * this is how editmesh did it and I'm too tired to think + * through the math right now. */ + mul_v3_fl(vec, -1.0f); + BMO_op_callf(bm, "translate verts=%s vec=%v", op, "verts", vec); + + BMO_op_callf(bm, "transform mat=%s verts=%s", op, "mat", op, "verts"); + + mul_v3_fl(vec, -1.0f); + BMO_op_callf(bm, "translate verts=%s vec=%v", op, "verts", vec); +} + +void bmesh_reversefaces_exec(BMesh *bm, BMOperator *op) +{ + BMOIter siter; + BMFace *f; + + BMO_ITER(f, &siter, bm, op, "faces", BM_FACE) { + BM_face_normal_flip(bm, f); + } +} + +void bmesh_edgerotate_exec(BMesh *bm, BMOperator *op) +{ + BMOIter siter; + BMEdge *e, *e2; + int ccw = BMO_slot_int_get(op, "ccw"); + + BMO_ITER(e, &siter, bm, op, "edges", BM_EDGE) { + if (!(e2 = BM_edge_rotate(bm, e, ccw))) { + BMO_error_raise(bm, op, BMERR_INVALID_SELECTION, "Could not rotate edge"); + return; + } + + BMO_elem_flag_enable(bm, e2, 1); + } + + BMO_slot_from_flag(bm, op, "edgeout", 1, BM_EDGE); +} + +#define SEL_FLAG 1 +#define SEL_ORIG 2 + +static void bmesh_regionextend_extend(BMesh *bm, BMOperator *op, int usefaces) +{ + BMVert *v; + BMEdge *e; + BMIter eiter; + BMOIter siter; + + if (!usefaces) { + BMO_ITER(v, &siter, bm, op, "geom", BM_VERT) { + BM_ITER(e, &eiter, bm, BM_EDGES_OF_VERT, v) { + if (!BMO_elem_flag_test(bm, e, SEL_ORIG)) + break; + } + + if (e) { + BM_ITER(e, &eiter, bm, BM_EDGES_OF_VERT, v) { + BMO_elem_flag_enable(bm, e, SEL_FLAG); + BMO_elem_flag_enable(bm, BM_edge_other_vert(e, v), SEL_FLAG); + } + } + } + } + else { + BMIter liter, fiter; + BMFace *f, *f2; + BMLoop *l; + + BMO_ITER(f, &siter, bm, op, "geom", BM_FACE) { + BM_ITER(l, &liter, bm, BM_LOOPS_OF_FACE, f) { + BM_ITER(f2, &fiter, bm, BM_FACES_OF_EDGE, l->e) { + if (!BMO_elem_flag_test(bm, f2, SEL_ORIG)) + BMO_elem_flag_enable(bm, f2, SEL_FLAG); + } + } + } + } +} + +static void bmesh_regionextend_constrict(BMesh *bm, BMOperator *op, int usefaces) +{ + BMVert *v; + BMEdge *e; + BMIter eiter; + BMOIter siter; + + if (!usefaces) { + BMO_ITER(v, &siter, bm, op, "geom", BM_VERT) { + BM_ITER(e, &eiter, bm, BM_EDGES_OF_VERT, v) { + if (!BMO_elem_flag_test(bm, e, SEL_ORIG)) + break; + } + + if (e) { + BMO_elem_flag_enable(bm, v, SEL_FLAG); + + BM_ITER(e, &eiter, bm, BM_EDGES_OF_VERT, v) { + BMO_elem_flag_enable(bm, e, SEL_FLAG); + } + } + } + } + else { + BMIter liter, fiter; + BMFace *f, *f2; + BMLoop *l; + + BMO_ITER(f, &siter, bm, op, "geom", BM_FACE) { + BM_ITER(l, &liter, bm, BM_LOOPS_OF_FACE, f) { + BM_ITER(f2, &fiter, bm, BM_FACES_OF_EDGE, l->e) { + if (!BMO_elem_flag_test(bm, f2, SEL_ORIG)) { + BMO_elem_flag_enable(bm, f, SEL_FLAG); + break; + } + } + } + } + } +} + +void bmesh_regionextend_exec(BMesh *bm, BMOperator *op) +{ + int usefaces = BMO_slot_int_get(op, "usefaces"); + int constrict = BMO_slot_int_get(op, "constrict"); + + BMO_slot_buffer_flag_enable(bm, op, "geom", SEL_ORIG, BM_ALL); + + if (constrict) + bmesh_regionextend_constrict(bm, op, usefaces); + else + bmesh_regionextend_extend(bm, op, usefaces); + + BMO_slot_from_flag(bm, op, "geomout", SEL_FLAG, BM_ALL); +} + +/********* righthand faces implementation ****** */ + +#define FACE_VIS 1 +#define FACE_FLAG 2 +#define FACE_MARK 4 +#define FACE_FLIP 8 + +/* NOTE: these are the original righthandfaces comment in editmesh_mods.c, + * copied here for reference. */ + +/* based at a select-connected to witness loose objects */ + +/* count per edge the amount of faces + * find the ultimate left, front, upper face (not manhattan dist!!) + * also evaluate both triangle cases in quad, since these can be non-flat + * + * put normal to the outside, and set the first direction flags in edges + * + * then check the object, and set directions / direction-flags: but only for edges with 1 or 2 faces + * this is in fact the 'select connected' + * + * in case (selected) faces were not done: start over with 'find the ultimate ...' */ + +/* NOTE: this function uses recursion, which is a little unusual for a bmop + * function, but acceptable I think. */ + +/* NOTE: BM_ELEM_TAG is used on faces to tell if they are flipped. */ + +void bmesh_righthandfaces_exec(BMesh *bm, BMOperator *op) +{ + BMIter liter, liter2; + BMOIter siter; + BMFace *f, *startf, **fstack = NULL; + BLI_array_declare(fstack); + BMLoop *l, *l2; + float maxx, cent[3]; + int i, maxi, flagflip = BMO_slot_int_get(op, "doflip"); + + startf = NULL; + maxx = -1.0e10; + + BMO_slot_buffer_flag_enable(bm, op, "faces", FACE_FLAG, BM_FACE); + + /* find a starting face */ + BMO_ITER(f, &siter, bm, op, "faces", BM_FACE) { + + /* clear dirty flag */ + BM_elem_flag_disable(f, BM_ELEM_TAG); + + if (BMO_elem_flag_test(bm, f, FACE_VIS)) + continue; + + if (!startf) startf = f; + + BM_face_center_bounds_calc(bm, f, cent); + + cent[0] = cent[0]*cent[0] + cent[1]*cent[1] + cent[2]*cent[2]; + if (cent[0] > maxx) { + maxx = cent[0]; + startf = f; + } + } + + if (!startf) return; + + BM_face_center_bounds_calc(bm, startf, cent); + + /* make sure the starting face has the correct winding */ + if (dot_v3v3(cent, startf->no) < 0.0f) { + BM_face_normal_flip(bm, startf); + BMO_elem_flag_toggle(bm, startf, FACE_FLIP); + + if (flagflip) + BM_elem_flag_toggle(startf, BM_ELEM_TAG); + } + + /* now that we've found our starting face, make all connected faces + * have the same winding. this is done recursively, using a manual + * stack (if we use simple function recursion, we'd end up overloading + * the stack on large meshes). */ + + BLI_array_growone(fstack); + fstack[0] = startf; + BMO_elem_flag_enable(bm, startf, FACE_VIS); + + i = 0; + maxi = 1; + while (i >= 0) { + f = fstack[i]; + i--; + + BM_ITER(l, &liter, bm, BM_LOOPS_OF_FACE, f) { + BM_ITER(l2, &liter2, bm, BM_LOOPS_OF_LOOP, l) { + if (!BMO_elem_flag_test(bm, l2->f, FACE_FLAG) || l2 == l) + continue; + + if (!BMO_elem_flag_test(bm, l2->f, FACE_VIS)) { + BMO_elem_flag_enable(bm, l2->f, FACE_VIS); + i++; + + if (l2->v == l->v) { + BM_face_normal_flip(bm, l2->f); + + BMO_elem_flag_toggle(bm, l2->f, FACE_FLIP); + if (flagflip) + BM_elem_flag_toggle(l2->f, BM_ELEM_TAG); + } + else if (BM_elem_flag_test(l2->f, BM_ELEM_TAG) || BM_elem_flag_test(l->f, BM_ELEM_TAG)) { + if (flagflip) { + BM_elem_flag_disable(l->f, BM_ELEM_TAG); + BM_elem_flag_disable(l2->f, BM_ELEM_TAG); + } + } + + if (i == maxi) { + BLI_array_growone(fstack); + maxi++; + } + + fstack[i] = l2->f; + } + } + } + } + + BLI_array_free(fstack); + + /* check if we have faces yet to do. if so, recurse */ + BMO_ITER(f, &siter, bm, op, "faces", BM_FACE) { + if (!BMO_elem_flag_test(bm, f, FACE_VIS)) { + bmesh_righthandfaces_exec(bm, op); + break; + } + } +} + +void bmesh_vertexsmooth_exec(BMesh *bm, BMOperator *op) +{ + BMOIter siter; + BMIter iter; + BMVert *v; + BMEdge *e; + BLI_array_declare(cos); + float (*cos)[3] = NULL; + float *co, *co2, clipdist = BMO_slot_float_get(op, "clipdist"); + int i, j, clipx, clipy, clipz; + + clipx = BMO_slot_int_get(op, "mirror_clip_x"); + clipy = BMO_slot_int_get(op, "mirror_clip_y"); + clipz = BMO_slot_int_get(op, "mirror_clip_z"); + + i = 0; + BMO_ITER(v, &siter, bm, op, "verts", BM_VERT) { + BLI_array_growone(cos); + co = cos[i]; + + j = 0; + BM_ITER(e, &iter, bm, BM_EDGES_OF_VERT, v) { + co2 = BM_edge_other_vert(e, v)->co; + add_v3_v3v3(co, co, co2); + j += 1; + } + + if (!j) { + copy_v3_v3(co, v->co); + i++; + continue; + } + + mul_v3_fl(co, 1.0f / (float)j); + mid_v3_v3v3(co, co, v->co); + + if (clipx && fabsf(v->co[0]) <= clipdist) + co[0] = 0.0f; + if (clipy && fabsf(v->co[1]) <= clipdist) + co[1] = 0.0f; + if (clipz && fabsf(v->co[2]) <= clipdist) + co[2] = 0.0f; + + i++; + } + + i = 0; + BMO_ITER(v, &siter, bm, op, "verts", BM_VERT) { + copy_v3_v3(v->co, cos[i]); + i++; + } + + BLI_array_free(cos); +} + +/* + * compute the perimeter of an ngon + * + * NOTE: This should probably go to bmesh_polygon.c + */ +static float ngon_perimeter(BMesh *bm, BMFace *f) +{ + BMIter liter; + BMLoop *l; + int num_verts = 0; + float v[3], sv[3]; + float perimeter = 0.0f; + + BM_ITER(l, &liter, bm, BM_LOOPS_OF_FACE, f) { + if (num_verts == 0) { + copy_v3_v3(v, l->v->co); + copy_v3_v3(sv, l->v->co); + } + else { + perimeter += len_v3v3(v, l->v->co); + copy_v3_v3(v, l->v->co); + } + num_verts++; + } + + perimeter += len_v3v3(v, sv); + + return perimeter; +} + +/* + * compute the fake surface of an ngon + * This is done by decomposing the ngon into triangles who share the centroid of the ngon + * while this method is far from being exact, it should garantee an invariance. + * + * NOTE: This should probably go to bmesh_polygon.c + */ +static float ngon_fake_area(BMesh *bm, BMFace *f) +{ + BMIter liter; + BMLoop *l; + int num_verts = 0; + float v[3], sv[3], c[3]; + float area = 0.0f; + + BM_face_center_mean_calc(bm, f, c); + + BM_ITER(l, &liter, bm, BM_LOOPS_OF_FACE, f) { + if (num_verts == 0) { + copy_v3_v3(v, l->v->co); + copy_v3_v3(sv, l->v->co); + num_verts++; + } + else { + area += area_tri_v3(v, c, l->v->co); + copy_v3_v3(v, l->v->co); + num_verts++; + } + } + + area += area_tri_v3(v, c, sv); + + return area; +} + +/* + * extra face data (computed data) + */ +typedef struct tmp_face_ext { + BMFace *f; /* the face */ + float c[3]; /* center */ + union { + float area; /* area */ + float perim; /* perimeter */ + float d; /* 4th component of plane (the first three being the normal) */ + struct Image *t; /* image pointer */ + }; +} tmp_face_ext; + +/* + * Select similar faces, the choices are in the enum in source/blender/bmesh/bmesh_operators.h + * We select either similar faces based on material, image, area, perimeter, normal, or the coplanar faces + */ +void bmesh_similarfaces_exec(BMesh *bm, BMOperator *op) +{ + BMIter fm_iter; + BMFace *fs, *fm; + BMOIter fs_iter; + int num_sels = 0, num_total = 0, i = 0, idx = 0; + float angle = 0.0f; + tmp_face_ext *f_ext = NULL; + int *indices = NULL; + float t_no[3]; /* temporary normal */ + int type = BMO_slot_int_get(op, "type"); + float thresh = BMO_slot_float_get(op, "thresh"); + + num_total = BM_mesh_elem_count(bm, BM_FACE); + + /* + ** The first thing to do is to iterate through all the the selected items and mark them since + ** they will be in the selection anyway. + ** This will increase performance, (especially when the number of originaly selected faces is high) + ** so the overall complexity will be less than $O(mn)$ where is the total number of selected faces, + ** and n is the total number of faces + */ + BMO_ITER(fs, &fs_iter, bm, op, "faces", BM_FACE) { + if (!BMO_elem_flag_test(bm, fs, FACE_MARK)) { /* is this really needed ? */ + BMO_elem_flag_enable(bm, fs, FACE_MARK); + num_sels++; + } + } + + /* allocate memory for the selected faces indices and for all temporary faces */ + indices = (int *)MEM_callocN(sizeof(int) * num_sels, "face indices util.c"); + f_ext = (tmp_face_ext *)MEM_callocN(sizeof(tmp_face_ext) * num_total, "f_ext util.c"); + + /* loop through all the faces and fill the faces/indices structure */ + BM_ITER(fm, &fm_iter, bm, BM_FACES_OF_MESH, NULL) { + f_ext[i].f = fm; + if (BMO_elem_flag_test(bm, fm, FACE_MARK)) { + indices[idx] = i; + idx++; + } + i++; + } + + /* + ** Save us some computation burden: In case of perimeter/area/coplanar selection we compute + ** only once. + */ + if (type == SIMFACE_PERIMETER || type == SIMFACE_AREA || type == SIMFACE_COPLANAR || type == SIMFACE_IMAGE) { + for (i = 0; i < num_total; i++) { + switch (type) { + case SIMFACE_PERIMETER: + /* set the perimeter */ + f_ext[i].perim = ngon_perimeter(bm, f_ext[i].f); + break; + + case SIMFACE_COPLANAR: + /* compute the center of the polygon */ + BM_face_center_mean_calc(bm, f_ext[i].f, f_ext[i].c); + + /* normalize the polygon normal */ + copy_v3_v3(t_no, f_ext[i].f->no); + normalize_v3(t_no); + + /* compute the plane distance */ + f_ext[i].d = dot_v3v3(t_no, f_ext[i].c); + break; + + case SIMFACE_AREA: + f_ext[i].area = ngon_fake_area(bm, f_ext[i].f); + break; + + case SIMFACE_IMAGE: + f_ext[i].t = NULL; + if (CustomData_has_layer(&(bm->pdata), CD_MTEXPOLY)) { + MTexPoly *mtpoly = CustomData_bmesh_get(&bm->pdata, f_ext[i].f->head.data, CD_MTEXPOLY); + f_ext[i].t = mtpoly->tpage; + } + break; + } + } + } + + /* now select the rest (if any) */ + for (i = 0; i < num_total; i++) { + fm = f_ext[i].f; + if (!BMO_elem_flag_test(bm, fm, FACE_MARK) && !BM_elem_flag_test(fm, BM_ELEM_HIDDEN)) { + int cont = 1; + for (idx = 0; idx < num_sels && cont == 1; idx++) { + fs = f_ext[indices[idx]].f; + switch (type) { + case SIMFACE_MATERIAL: + if (fm->mat_nr == fs->mat_nr) { + BMO_elem_flag_enable(bm, fm, FACE_MARK); + cont = 0; + } + break; + + case SIMFACE_IMAGE: + if (f_ext[i].t == f_ext[indices[idx]].t) { + BMO_elem_flag_enable(bm, fm, FACE_MARK); + cont = 0; + } + break; + + case SIMFACE_NORMAL: + angle = RAD2DEGF(angle_v3v3(fs->no, fm->no)); /* if the angle between the normals -> 0 */ + if (angle / 180.0f <= thresh) { + BMO_elem_flag_enable(bm, fm, FACE_MARK); + cont = 0; + } + break; + + case SIMFACE_COPLANAR: + angle = RAD2DEGF(angle_v3v3(fs->no, fm->no)); /* angle -> 0 */ + if (angle / 180.0f <= thresh) { /* and dot product difference -> 0 */ + if (fabsf(f_ext[i].d - f_ext[indices[idx]].d) <= thresh) { + BMO_elem_flag_enable(bm, fm, FACE_MARK); + cont = 0; + } + } + break; + + case SIMFACE_AREA: + if (fabsf(f_ext[i].area - f_ext[indices[idx]].area) <= thresh) { + BMO_elem_flag_enable(bm, fm, FACE_MARK); + cont = 0; + } + break; + + case SIMFACE_PERIMETER: + if (fabsf(f_ext[i].perim - f_ext[indices[idx]].perim) <= thresh) { + BMO_elem_flag_enable(bm, fm, FACE_MARK); + cont = 0; + } + break; + } + } + } + } + + MEM_freeN(f_ext); + MEM_freeN(indices); + + /* transfer all marked faces to the output slot */ + BMO_slot_from_flag(bm, op, "faceout", FACE_MARK, BM_FACE); +} + +/****************************************************************************** +** Similar Edges +**************************************************************************** */ +#define EDGE_MARK 1 + +/* + * compute the angle of an edge (i.e. the angle between two faces) + */ +static float edge_angle(BMesh *bm, BMEdge *e) +{ + BMIter fiter; + BMFace *f, *f_prev = NULL; + + /* first edge faces, dont account for 3+ */ + + BM_ITER(f, &fiter, bm, BM_FACES_OF_EDGE, e) { + if (f_prev == NULL) { + f_prev = f; + } + else { + return angle_v3v3(f_prev->no, f->no); + } + } + + return 0.0f; +} +/* + * extra edge information + */ +typedef struct tmp_edge_ext { + BMEdge *e; + union { + float dir[3]; + float angle; /* angle between the face */ + }; + + union { + float length; /* edge length */ + int faces; /* faces count */ + }; +} tmp_edge_ext; + +/* + * select similar edges: the choices are in the enum in source/blender/bmesh/bmesh_operators.h + * choices are length, direction, face, ... + */ +void bmesh_similaredges_exec(BMesh *bm, BMOperator *op) +{ + BMOIter es_iter; /* selected edges iterator */ + BMIter e_iter; /* mesh edges iterator */ + BMEdge *es; /* selected edge */ + BMEdge *e; /* mesh edge */ + int idx = 0, i = 0 /* , f = 0 */; + int *indices = NULL; + tmp_edge_ext *e_ext = NULL; + // float *angles = NULL; + float angle; + + int num_sels = 0, num_total = 0; + int type = BMO_slot_int_get(op, "type"); + float thresh = BMO_slot_float_get(op, "thresh"); + + num_total = BM_mesh_elem_count(bm, BM_EDGE); + + /* iterate through all selected edges and mark them */ + BMO_ITER(es, &es_iter, bm, op, "edges", BM_EDGE) { + BMO_elem_flag_enable(bm, es, EDGE_MARK); + num_sels++; + } + + /* allocate memory for the selected edges indices and for all temporary edges */ + indices = (int *)MEM_callocN(sizeof(int) * num_sels, "indices util.c"); + e_ext = (tmp_edge_ext *)MEM_callocN(sizeof(tmp_edge_ext) * num_total, "e_ext util.c"); + + /* loop through all the edges and fill the edges/indices structure */ + BM_ITER(e, &e_iter, bm, BM_EDGES_OF_MESH, NULL) { + e_ext[i].e = e; + if (BMO_elem_flag_test(bm, e, EDGE_MARK)) { + indices[idx] = i; + idx++; + } + i++; + } + + /* save us some computation time by doing heavy computation once */ + if (type == SIMEDGE_LENGTH || type == SIMEDGE_FACE || type == SIMEDGE_DIR || type == SIMEDGE_FACE_ANGLE) { + for (i = 0; i < num_total; i++) { + switch (type) { + case SIMEDGE_LENGTH: /* compute the length of the edge */ + e_ext[i].length = len_v3v3(e_ext[i].e->v1->co, e_ext[i].e->v2->co); + break; + + case SIMEDGE_DIR: /* compute the direction */ + sub_v3_v3v3(e_ext[i].dir, e_ext[i].e->v1->co, e_ext[i].e->v2->co); + break; + + case SIMEDGE_FACE: /* count the faces around the edge */ + e_ext[i].faces = BM_edge_face_count(e_ext[i].e); + break; + + case SIMEDGE_FACE_ANGLE: + e_ext[i].faces = BM_edge_face_count(e_ext[i].e); + if (e_ext[i].faces == 2) + e_ext[i].angle = edge_angle(bm, e_ext[i].e); + break; + } + } + } + + /* select the edges if any */ + for (i = 0; i < num_total; i++) { + e = e_ext[i].e; + if (!BMO_elem_flag_test(bm, e, EDGE_MARK) && !BM_elem_flag_test(e, BM_ELEM_HIDDEN)) { + int cont = 1; + for (idx = 0; idx < num_sels && cont == 1; idx++) { + es = e_ext[indices[idx]].e; + switch (type) { + case SIMEDGE_LENGTH: + if (fabsf(e_ext[i].length - e_ext[indices[idx]].length) <= thresh) { + BMO_elem_flag_enable(bm, e, EDGE_MARK); + cont = 0; + } + break; + + case SIMEDGE_DIR: + /* compute the angle between the two edges */ + angle = RAD2DEGF(angle_v3v3(e_ext[i].dir, e_ext[indices[idx]].dir)); + + if (angle > 90.0f) /* use the smallest angle between the edges */ + angle = fabsf(angle - 180.0f); + + if (angle / 90.0f <= thresh) { + BMO_elem_flag_enable(bm, e, EDGE_MARK); + cont = 0; + } + break; + + case SIMEDGE_FACE: + if (e_ext[i].faces == e_ext[indices[idx]].faces) { + BMO_elem_flag_enable(bm, e, EDGE_MARK); + cont = 0; + } + break; + + case SIMEDGE_FACE_ANGLE: + if (e_ext[i].faces == 2) { + if (e_ext[indices[idx]].faces == 2) { + if (fabsf(e_ext[i].angle - e_ext[indices[idx]].angle) <= thresh) { + BMO_elem_flag_enable(bm, e, EDGE_MARK); + cont = 0; + } + } + } + else { + cont = 0; + } + break; + + case SIMEDGE_CREASE: + if (CustomData_has_layer(&bm->edata, CD_CREASE)) { + float *c1, *c2; + + c1 = CustomData_bmesh_get(&bm->edata, e->head.data, CD_CREASE); + c2 = CustomData_bmesh_get(&bm->edata, es->head.data, CD_CREASE); + + if (c1 && c2 && fabsf(*c1 - *c2) <= thresh) { + BMO_elem_flag_enable(bm, e, EDGE_MARK); + cont = 0; + } + } + break; + + case SIMEDGE_SEAM: + if (BM_elem_flag_test(e, BM_ELEM_SEAM) == BM_elem_flag_test(es, BM_ELEM_SEAM)) { + BMO_elem_flag_enable(bm, e, EDGE_MARK); + cont = 0; + } + break; + + case SIMEDGE_SHARP: + if (BM_elem_flag_test(e, BM_ELEM_SMOOTH) == BM_elem_flag_test(es, BM_ELEM_SMOOTH)) { + BMO_elem_flag_enable(bm, e, EDGE_MARK); + cont = 0; + } + break; + } + } + } + } + + MEM_freeN(e_ext); + MEM_freeN(indices); + + /* transfer all marked edges to the output slot */ + BMO_slot_from_flag(bm, op, "edgeout", EDGE_MARK, BM_EDGE); +} + +/****************************************************************************** +** Similar Vertices +**************************************************************************** */ +#define VERT_MARK 1 + +typedef struct tmp_vert_ext { + BMVert *v; + union { + int num_faces; /* adjacent faces */ + MDeformVert *dvert; /* deform vertex */ + }; +} tmp_vert_ext; + +/* + * select similar vertices: the choices are in the enum in source/blender/bmesh/bmesh_operators.h + * choices are normal, face, vertex group... + */ +void bmesh_similarverts_exec(BMesh *bm, BMOperator *op) +{ + BMOIter vs_iter; /* selected verts iterator */ + BMIter v_iter; /* mesh verts iterator */ + BMVert *vs; /* selected vertex */ + BMVert *v; /* mesh vertex */ + tmp_vert_ext *v_ext = NULL; + int *indices = NULL; + int num_total = 0, num_sels = 0, i = 0, idx = 0; + int type = BMO_slot_int_get(op, "type"); + float thresh = BMO_slot_float_get(op, "thresh"); + + num_total = BM_mesh_elem_count(bm, BM_VERT); + + /* iterate through all selected edges and mark them */ + BMO_ITER(vs, &vs_iter, bm, op, "verts", BM_VERT) { + BMO_elem_flag_enable(bm, vs, VERT_MARK); + num_sels++; + } + + /* allocate memory for the selected vertices indices and for all temporary vertices */ + indices = (int *)MEM_mallocN(sizeof(int) * num_sels, "vertex indices"); + v_ext = (tmp_vert_ext *)MEM_mallocN(sizeof(tmp_vert_ext) * num_total, "vertex extra"); + + /* loop through all the vertices and fill the vertices/indices structure */ + BM_ITER(v, &v_iter, bm, BM_VERTS_OF_MESH, NULL) { + v_ext[i].v = v; + if (BMO_elem_flag_test(bm, v, VERT_MARK)) { + indices[idx] = i; + idx++; + } + + switch (type) { + case SIMVERT_FACE: + /* calling BM_vert_face_count every time is time consumming, so call it only once per vertex */ + v_ext[i].num_faces = BM_vert_face_count(v); + break; + + case SIMVERT_VGROUP: + if (CustomData_has_layer(&(bm->vdata), CD_MDEFORMVERT)) { + v_ext[i].dvert = CustomData_bmesh_get(&bm->vdata, v_ext[i].v->head.data, CD_MDEFORMVERT); + } + else { + v_ext[i].dvert = NULL; + } + break; + } + + i++; + } + + /* select the vertices if any */ + for (i = 0; i < num_total; i++) { + v = v_ext[i].v; + if (!BMO_elem_flag_test(bm, v, VERT_MARK) && !BM_elem_flag_test(v, BM_ELEM_HIDDEN)) { + int cont = 1; + for (idx = 0; idx < num_sels && cont == 1; idx++) { + vs = v_ext[indices[idx]].v; + switch (type) { + case SIMVERT_NORMAL: + /* compare the angle between the normals */ + if (RAD2DEGF(angle_v3v3(v->no, vs->no)) / 180.0f <= thresh) { + BMO_elem_flag_enable(bm, v, VERT_MARK); + cont = 0; + } + break; + case SIMVERT_FACE: + /* number of adjacent faces */ + if (v_ext[i].num_faces == v_ext[indices[idx]].num_faces) { + BMO_elem_flag_enable(bm, v, VERT_MARK); + cont = 0; + } + break; + + case SIMVERT_VGROUP: + if (v_ext[i].dvert != NULL && v_ext[indices[idx]].dvert != NULL) { + int v1, v2; + for (v1 = 0; v1 < v_ext[i].dvert->totweight && cont == 1; v1++) { + for (v2 = 0; v2 < v_ext[indices[idx]].dvert->totweight; v2++) { + if (v_ext[i].dvert->dw[v1].def_nr == v_ext[indices[idx]].dvert->dw[v2].def_nr) { + BMO_elem_flag_enable(bm, v, VERT_MARK); + cont = 0; + break; + } + } + } + } + break; + } + } + } + } + + MEM_freeN(indices); + MEM_freeN(v_ext); + + BMO_slot_from_flag(bm, op, "vertout", VERT_MARK, BM_VERT); +} + +/****************************************************************************** +** Cycle UVs for a face +**************************************************************************** */ + +void bmesh_rotateuvs_exec(BMesh *bm, BMOperator *op) +{ + BMOIter fs_iter; /* selected faces iterator */ + BMFace *fs; /* current face */ + BMIter l_iter; /* iteration loop */ + // int n; + + int dir = BMO_slot_int_get(op, "dir"); + + BMO_ITER(fs, &fs_iter, bm, op, "faces", BM_FACE) { + if (CustomData_has_layer(&(bm->ldata), CD_MLOOPUV)) { + if (dir == DIRECTION_CW) { /* same loops direction */ + BMLoop *lf; /* current face loops */ + MLoopUV *f_luv; /* first face loop uv */ + float p_uv[2]; /* previous uvs */ + float t_uv[2]; /* tmp uvs */ + + int n = 0; + BM_ITER(lf, &l_iter, bm, BM_LOOPS_OF_FACE, fs) { + /* current loop uv is the previous loop uv */ + MLoopUV *luv = CustomData_bmesh_get(&bm->ldata, lf->head.data, CD_MLOOPUV); + if (n == 0) { + f_luv = luv; + copy_v2_v2(p_uv, luv->uv); + } + else { + copy_v2_v2(t_uv, luv->uv); + copy_v2_v2(luv->uv, p_uv); + copy_v2_v2(p_uv, t_uv); + } + n++; + } + + copy_v2_v2(f_luv->uv, p_uv); + } + else if (dir == DIRECTION_CCW) { /* counter loop direction */ + BMLoop *lf; /* current face loops */ + MLoopUV *p_luv; /* previous loop uv */ + MLoopUV *luv; + float t_uv[2]; /* current uvs */ + + int n = 0; + BM_ITER(lf, &l_iter, bm, BM_LOOPS_OF_FACE, fs) { + /* previous loop uv is the current loop uv */ + luv = CustomData_bmesh_get(&bm->ldata, lf->head.data, CD_MLOOPUV); + if (n == 0) { + p_luv = luv; + copy_v2_v2(t_uv, luv->uv); + } + else { + copy_v2_v2(p_luv->uv, luv->uv); + p_luv = luv; + } + n++; + } + + copy_v2_v2(luv->uv, t_uv); + } + } + } + +} + +/****************************************************************************** +** Reverse UVs for a face +**************************************************************************** */ + +void bmesh_reverseuvs_exec(BMesh *bm, BMOperator *op) +{ + BMOIter fs_iter; /* selected faces iterator */ + BMFace *fs; /* current face */ + BMIter l_iter; /* iteration loop */ + BLI_array_declare(uvs); + float (*uvs)[2] = NULL; + + BMO_ITER(fs, &fs_iter, bm, op, "faces", BM_FACE) { + if (CustomData_has_layer(&(bm->ldata), CD_MLOOPUV)) { + BMLoop *lf; /* current face loops */ + int i = 0; + + BLI_array_empty(uvs); + BM_ITER(lf, &l_iter, bm, BM_LOOPS_OF_FACE, fs) { + MLoopUV *luv = CustomData_bmesh_get(&bm->ldata, lf->head.data, CD_MLOOPUV); + + /* current loop uv is the previous loop uv */ + BLI_array_growone(uvs); + uvs[i][0] = luv->uv[0]; + uvs[i][1] = luv->uv[1]; + i++; + } + + /* now that we have the uvs in the array, reverse! */ + i = 0; + BM_ITER(lf, &l_iter, bm, BM_LOOPS_OF_FACE, fs) { + /* current loop uv is the previous loop uv */ + MLoopUV *luv = CustomData_bmesh_get(&bm->ldata, lf->head.data, CD_MLOOPUV); + luv->uv[0] = uvs[(fs->len - i - 1)][0]; + luv->uv[1] = uvs[(fs->len - i - 1)][1]; + i++; + } + } + } + + BLI_array_free(uvs); +} + +/****************************************************************************** +** Cycle colors for a face +**************************************************************************** */ + +void bmesh_rotatecolors_exec(BMesh *bm, BMOperator *op) +{ + BMOIter fs_iter; /* selected faces iterator */ + BMFace *fs; /* current face */ + BMIter l_iter; /* iteration loop */ + // int n; + + int dir = BMO_slot_int_get(op, "dir"); + + BMO_ITER(fs, &fs_iter, bm, op, "faces", BM_FACE) { + if (CustomData_has_layer(&(bm->ldata), CD_MLOOPCOL)) { + if (dir == DIRECTION_CW) { /* same loops direction */ + BMLoop *lf; /* current face loops */ + MLoopCol *f_lcol; /* first face loop color */ + MLoopCol p_col; /* previous color */ + MLoopCol t_col; /* tmp color */ + + int n = 0; + BM_ITER(lf, &l_iter, bm, BM_LOOPS_OF_FACE, fs) { + /* current loop color is the previous loop color */ + MLoopCol *luv = CustomData_bmesh_get(&bm->ldata, lf->head.data, CD_MLOOPCOL); + if (n == 0) { + f_lcol = luv; + p_col = *luv; + } + else { + t_col = *luv; + *luv = p_col; + p_col = t_col; + } + n++; + } + + *f_lcol = p_col; + } + else if (dir == DIRECTION_CCW) { /* counter loop direction */ + BMLoop *lf; /* current face loops */ + MLoopCol *p_lcol; /* previous loop color */ + MLoopCol *lcol; + MLoopCol t_col; /* current color */ + + int n = 0; + BM_ITER(lf, &l_iter, bm, BM_LOOPS_OF_FACE, fs) { + /* previous loop color is the current loop color */ + lcol = CustomData_bmesh_get(&bm->ldata, lf->head.data, CD_MLOOPCOL); + if (n == 0) { + p_lcol = lcol; + t_col = *lcol; + } + else { + *p_lcol = *lcol; + p_lcol = lcol; + } + n++; + } + + *lcol = t_col; + } + } + } +} + +/****************************************************************************** +** Reverse colors for a face +**************************************************************************** */ + +void bmesh_reversecolors_exec(BMesh *bm, BMOperator *op) +{ + BMOIter fs_iter; /* selected faces iterator */ + BMFace *fs; /* current face */ + BMIter l_iter; /* iteration loop */ + BLI_array_declare(cols); + MLoopCol *cols = NULL; + + BMO_ITER(fs, &fs_iter, bm, op, "faces", BM_FACE) { + if (CustomData_has_layer(&(bm->ldata), CD_MLOOPCOL)) { + BMLoop *lf; /* current face loops */ + int i = 0; + + BLI_array_empty(cols); + BM_ITER(lf, &l_iter, bm, BM_LOOPS_OF_FACE, fs) { + MLoopCol *lcol = CustomData_bmesh_get(&bm->ldata, lf->head.data, CD_MLOOPCOL); + + /* current loop uv is the previous loop color */ + BLI_array_growone(cols); + cols[i] = *lcol; + i++; + } + + /* now that we have the uvs in the array, reverse! */ + i = 0; + BM_ITER(lf, &l_iter, bm, BM_LOOPS_OF_FACE, fs) { + /* current loop uv is the previous loop color */ + MLoopCol *lcol = CustomData_bmesh_get(&bm->ldata, lf->head.data, CD_MLOOPCOL); + *lcol = cols[(fs->len - i - 1)]; + i++; + } + } + } + + BLI_array_free(cols); +} + + +/****************************************************************************** +** shortest vertex path select +**************************************************************************** */ + +typedef struct element_node { + BMVert *v; /* vertex */ + BMVert *parent; /* node parent id */ + float weight; /* node weight */ + HeapNode *hn; /* heap node */ +} element_node; + +void bmesh_vertexshortestpath_exec(BMesh *bm, BMOperator *op) +{ + BMOIter vs_iter /* , vs2_iter */; /* selected verts iterator */ + BMIter v_iter; /* mesh verts iterator */ + BMVert *vs, *sv, *ev; /* starting vertex, ending vertex */ + BMVert *v; /* mesh vertex */ + Heap *h = NULL; + + element_node *vert_list = NULL; + + int num_total = 0 /*, num_sels = 0 */, i = 0; + int type = BMO_slot_int_get(op, "type"); + + BMO_ITER(vs, &vs_iter, bm, op, "startv", BM_VERT) { + sv = vs; + } + BMO_ITER(vs, &vs_iter, bm, op, "endv", BM_VERT) { + ev = vs; + } + + num_total = BM_mesh_elem_count(bm, BM_VERT); + + /* allocate memory for the nodes */ + vert_list = (element_node *)MEM_mallocN(sizeof(element_node) * num_total, "vertex nodes"); + + /* iterate through all the mesh vertices */ + /* loop through all the vertices and fill the vertices/indices structure */ + i = 0; + BM_ITER(v, &v_iter, bm, BM_VERTS_OF_MESH, NULL) { + vert_list[i].v = v; + vert_list[i].parent = NULL; + vert_list[i].weight = FLT_MAX; + BM_elem_index_set(v, i); /* set_inline */ + i++; + } + bm->elem_index_dirty &= ~BM_VERT; + + /* + ** we now have everything we need, start Dijkstra path finding algorithm + */ + + /* set the distance/weight of the start vertex to 0 */ + vert_list[BM_elem_index_get(sv)].weight = 0.0f; + + h = BLI_heap_new(); + + for (i = 0; i < num_total; i++) { + vert_list[i].hn = BLI_heap_insert(h, vert_list[i].weight, vert_list[i].v); + } + + while (!BLI_heap_empty(h)) { + BMEdge *e; + BMIter e_i; + float v_weight; + + /* take the vertex with the lowest weight out of the heap */ + BMVert *v = (BMVert *)BLI_heap_popmin(h); + + if (vert_list[BM_elem_index_get(v)].weight == FLT_MAX) /* this means that there is no path */ + break; + + v_weight = vert_list[BM_elem_index_get(v)].weight; + + BM_ITER(e, &e_i, bm, BM_EDGES_OF_VERT, v) { + BMVert *u; + float e_weight = v_weight; + + if (type == VPATH_SELECT_EDGE_LENGTH) + e_weight += len_v3v3(e->v1->co, e->v2->co); + else e_weight += 1.0f; + + u = (e->v1 == v) ? e->v2 : e->v1; + + if (e_weight < vert_list[BM_elem_index_get(u)].weight) { /* is this path shorter ? */ + /* add it if so */ + vert_list[BM_elem_index_get(u)].parent = v; + vert_list[BM_elem_index_get(u)].weight = e_weight; + + /* we should do a heap update node function!!! :-/ */ + BLI_heap_remove(h, vert_list[BM_elem_index_get(u)].hn); + BLI_heap_insert(h, e_weight, u); + } + } + } + + /* now we trace the path (if it exists) */ + v = ev; + + while (vert_list[BM_elem_index_get(v)].parent != NULL) { + BMO_elem_flag_enable(bm, v, VERT_MARK); + v = vert_list[BM_elem_index_get(v)].parent; + } + + BLI_heap_free(h, NULL); + MEM_freeN(vert_list); + + BMO_slot_from_flag(bm, op, "vertout", VERT_MARK, BM_VERT); +} |