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/tools | |
parent | d6deca4e9d6bc7faff98644286571c7934324a9d (diff) |
copying bmesh dir on its own from bmesh branch
Diffstat (limited to 'source/blender/bmesh/tools')
-rw-r--r-- | source/blender/bmesh/tools/BME_bevel.c | 1011 | ||||
-rw-r--r-- | source/blender/bmesh/tools/BME_dupe_ops.c | 322 | ||||
-rw-r--r-- | source/blender/bmesh/tools/BME_duplicate.c | 310 | ||||
-rw-r--r-- | source/blender/bmesh/tools/BME_weld.c | 341 |
4 files changed, 1984 insertions, 0 deletions
diff --git a/source/blender/bmesh/tools/BME_bevel.c b/source/blender/bmesh/tools/BME_bevel.c new file mode 100644 index 00000000000..b20b7316eb4 --- /dev/null +++ b/source/blender/bmesh/tools/BME_bevel.c @@ -0,0 +1,1011 @@ +/* + * ***** 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. + * + * The Original Code is Copyright (C) 2004 Blender Foundation. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): Geoffrey Bantle and Levi Schooley. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#include <math.h> + +#include "MEM_guardedalloc.h" + +#include "DNA_listBase.h" +#include "DNA_meshdata_types.h" +#include "DNA_mesh_types.h" + +#include "BKE_utildefines.h" +#include "BKE_tessmesh.h" +#include "BKE_bmesh.h" +#include "BLI_math.h" +#include "BLI_blenlib.h" +#include "BLI_ghash.h" + +#include "bmesh.h" +#include "bmesh_private.h" + +/* ------- Bevel code starts here -------- */ + +BME_TransData_Head *BME_init_transdata(int bufsize) +{ + BME_TransData_Head *td; + + td = MEM_callocN(sizeof(BME_TransData_Head), "BM transdata header"); + td->gh = BLI_ghash_new(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp, "BME_init_transdata gh"); + td->ma = BLI_memarena_new(bufsize, "BME_TransData arena"); + BLI_memarena_use_calloc(td->ma); + + return td; +} + +void BME_free_transdata(BME_TransData_Head *td) +{ + BLI_ghash_free(td->gh, NULL, NULL); + BLI_memarena_free(td->ma); + MEM_freeN(td); +} + +BME_TransData *BME_assign_transdata( + BME_TransData_Head *td, BMesh *bm, BMVert *v, + float *co, float *org, float *vec, float *loc, + float factor, float weight, float maxfactor, float *max) +{ + BME_TransData *vtd; + int is_new = 0; + + if (v == NULL) { + return NULL; + } + + if ((vtd = BLI_ghash_lookup(td->gh, v)) == NULL && bm != NULL) { + vtd = BLI_memarena_alloc(td->ma, sizeof(*vtd)); + BLI_ghash_insert(td->gh, v, vtd); + td->len++; + is_new = 1; + } + + vtd->bm = bm; + vtd->v = v; + + if (co != NULL) { + copy_v3_v3(vtd->co, co); + } + + if (org == NULL && is_new) { + copy_v3_v3(vtd->org, v->co); /* default */ + } + else if (org != NULL) { + copy_v3_v3(vtd->org, org); + } + + if (vec != NULL) { + copy_v3_v3(vtd->vec, vec); + normalize_v3(vtd->vec); + } + + vtd->loc = loc; + + vtd->factor = factor; + vtd->weight = weight; + vtd->maxfactor = maxfactor; + vtd->max = max; + + return vtd; +} + +BME_TransData *BME_get_transdata(BME_TransData_Head *td, BMVert *v) +{ + BME_TransData *vtd; + vtd = BLI_ghash_lookup(td->gh, v); + return vtd; +} + +/* a hack (?) to use the transdata memarena to allocate floats for use with the max limits */ +float *BME_new_transdata_float(BME_TransData_Head *td) +{ + return BLI_memarena_alloc(td->ma, sizeof(float)); +} + +/* BM_disk_dissolve is a real mess, and crashes bevel if called instead of this. + * The drawback, though, is that this code doesn't merge customdata. */ +static int BME_Bevel_Dissolve_Disk(BMesh *bm, BMVert *v) +{ + BMIter iter; + BMEdge *e, *elast; + BMLoop *l1, *l2; + + if (!BM_vert_is_manifold(bm, v)) { + return 0; + } + + BM_ITER(e, &iter, bm, BM_EDGES_OF_VERT, v) { + if (BM_edge_face_count(e) != 2) { + return 0; + } + } + + if (BM_vert_edge_count(v) > 2) { + while (BM_vert_edge_count(v) > 2) { + e = v->e; + l1 = e->l; + l2 = l1->radial_next; + bmesh_jfke(bm, l1->f, l2->f, e); + } + + e = v->e; + elast = bmesh_disk_nextedge(e, v); + bmesh_jekv(bm, e, v); + + l1 = elast->l; + l2 = l1->radial_next; + bmesh_jfke(bm, l1->f, l2->f, elast); + } + + return 1; +} + +static int BME_bevel_is_split_vert(BMesh *bm, BMLoop *l) +{ + /* look for verts that have already been added to the edge when + * beveling other polys; this can be determined by testing the + * vert and the edges around it for originality + */ + if ( !BMO_elem_flag_test(bm, l->v, BME_BEVEL_ORIG) && + BMO_elem_flag_test(bm, l->e, BME_BEVEL_ORIG) && + BMO_elem_flag_test(bm, l->prev->e, BME_BEVEL_ORIG)) + { + return 1; + } + return 0; +} + +/* get a vector, vec, that points from v1->co to wherever makes sense to + * the bevel operation as a whole based on the relationship between v1 and v2 + * (won't necessarily be a vec from v1->co to v2->co, though it probably will be); + * the return value is -1 for failure, 0 if we used vert co's, and 1 if we used transform origins */ +static int BME_bevel_get_vec(float *vec, BMVert *v1, BMVert *v2, BME_TransData_Head *td) +{ + BME_TransData *vtd1, *vtd2; + + vtd1 = BME_get_transdata(td, v1); + vtd2 = BME_get_transdata(td, v2); + if (!vtd1 || !vtd2) { + //printf("BME_bevel_get_vec() got called without proper BME_TransData\n"); + return -1; + } + + /* compare the transform origins to see if we can use the vert co's; + * if they belong to different origins, then we will use the origins to determine + * the vector */ + if (compare_v3v3(vtd1->org, vtd2->org, 0.000001f)) { + sub_v3_v3v3(vec, v2->co, v1->co); + if (len_v3(vec) < 0.000001f) { + zero_v3(vec); + } + return 0; + } + else { + sub_v3_v3v3(vec, vtd2->org, vtd1->org); + if (len_v3(vec) < 0.000001f) { + zero_v3(vec); + } + return 1; + } +} + +/* "Projects" a vector perpendicular to vec2 against vec1, such that + * the projected vec1 + vec2 has a min distance of 1 from the "edge" defined by vec2. + * note: the direction, is_forward, is used in conjunction with up_vec to determine + * whether this is a convex or concave corner. If it is a concave corner, it will + * be projected "backwards." If vec1 is before vec2, is_forward should be 0 (we are projecting backwards). + * vec1 is the vector to project onto (expected to be normalized) + * vec2 is the direction of projection (pointing away from vec1) + * up_vec is used for orientation (expected to be normalized) + * returns the length of the projected vector that lies along vec1 */ +static float BME_bevel_project_vec(float *vec1, float *vec2, float *up_vec, int is_forward, BME_TransData_Head *UNUSED(td)) +{ + float factor, vec3[3], tmp[3], c1, c2; + + cross_v3_v3v3(tmp, vec1, vec2); + normalize_v3(tmp); + factor = dot_v3v3(up_vec, tmp); + if ((factor > 0 && is_forward) || (factor < 0 && !is_forward)) { + cross_v3_v3v3(vec3, vec2, tmp); /* hmm, maybe up_vec should be used instead of tmp */ + } + else { + cross_v3_v3v3(vec3, tmp, vec2); /* hmm, maybe up_vec should be used instead of tmp */ + } + normalize_v3(vec3); + c1 = dot_v3v3(vec3, vec1); + c2 = dot_v3v3(vec1, vec1); + if (fabsf(c1) < 0.000001f || fabsf(c2) < 0.000001f) { + factor = 0.0f; + } + else { + factor = c2 / c1; + } + + return factor; +} + +/* BME_bevel_split_edge() is the main math work-house; its responsibilities are: + * using the vert and the loop passed, get or make the split vert, set its coordinates + * and transform properties, and set the max limits. + * Finally, return the split vert. */ +static BMVert *BME_bevel_split_edge(BMesh *bm, BMVert *v, BMVert *v1, BMLoop *l, float *up_vec, float value, BME_TransData_Head *td) +{ + BME_TransData *vtd, *vtd1, *vtd2; + BMVert *sv, *v2, *v3, *ov; + BMLoop *lv1, *lv2; + BMEdge *ne, *e1, *e2; + float maxfactor, scale, len, dis, vec1[3], vec2[3], t_up_vec[3]; + int is_edge, forward, is_split_vert; + + if (l == NULL) { + /* what you call operator overloading in C :) + * I wanted to use the same function for both wire edges and poly loops + * so... here we walk around edges to find the needed verts */ + forward = 1; + is_split_vert = 0; + if (v->e == NULL) { + //printf("We can't split a loose vert's edge!\n"); + return NULL; + } + e1 = v->e; /* we just use the first two edges */ + e2 = bmesh_disk_nextedge(v->e, v); + if (e1 == e2) { + //printf("You need at least two edges to use BME_bevel_split_edge()\n"); + return NULL; + } + v2 = BM_edge_other_vert(e1, v); + v3 = BM_edge_other_vert(e2, v); + if (v1 != v2 && v1 != v3) { + //printf("Error: more than 2 edges in v's disk cycle, or v1 does not share an edge with v\n"); + return NULL; + } + if (v1 == v2) { + v2 = v3; + } + else { + e1 = e2; + } + ov = BM_edge_other_vert(e1, v); + sv = BM_edge_split(bm, v, e1, &ne, 0); + //BME_data_interp_from_verts(bm, v, ov, sv, 0.25); /* this is technically wrong.. */ + //BME_data_interp_from_faceverts(bm, v, ov, sv, 0.25); + //BME_data_interp_from_faceverts(bm, ov, v, sv, 0.25); + BME_assign_transdata(td, bm, sv, sv->co, sv->co, NULL, sv->co, 0, -1, -1, NULL); /* quick default */ + BMO_elem_flag_enable(bm, sv, BME_BEVEL_BEVEL); + BMO_elem_flag_enable(bm, ne, BME_BEVEL_ORIG); /* mark edge as original, even though it isn't */ + BME_bevel_get_vec(vec1, v1, v, td); + BME_bevel_get_vec(vec2, v2, v, td); + cross_v3_v3v3(t_up_vec, vec1, vec2); + normalize_v3(t_up_vec); + up_vec = t_up_vec; + } + else { + /* establish loop direction */ + if (l->v == v) { + forward = 1; + lv1 = l->next; + lv2 = l->prev; + v1 = l->next->v; + v2 = l->prev->v; + } + else if (l->next->v == v) { + forward = 0; + lv1 = l; + lv2 = l->next->next; + v1 = l->v; + v2 = l->next->next->v; + } + else { + //printf("ERROR: BME_bevel_split_edge() - v must be adjacent to l\n"); + return NULL; + } + + if (BME_bevel_is_split_vert(bm, lv1)) { + is_split_vert = 1; + sv = v1; + v1 = forward ? l->next->next->v : l->prev->v; + } + else { + is_split_vert = 0; + ov = BM_edge_other_vert(l->e, v); + sv = BM_edge_split(bm, v, l->e, &ne, 0); + //BME_data_interp_from_verts(bm, v, ov, sv, 0.25); /* this is technically wrong.. */ + //BME_data_interp_from_faceverts(bm, v, ov, sv, 0.25); + //BME_data_interp_from_faceverts(bm, ov, v, sv, 0.25); + BME_assign_transdata(td, bm, sv, sv->co, sv->co, NULL, sv->co, 0, -1, -1, NULL); /* quick default */ + BMO_elem_flag_enable(bm, sv, BME_BEVEL_BEVEL); + BMO_elem_flag_enable(bm, ne, BME_BEVEL_ORIG); /* mark edge as original, even though it isn't */ + } + + if (BME_bevel_is_split_vert(bm, lv2)) { + v2 = forward ? lv2->prev->v : lv2->next->v; + } + } + + is_edge = BME_bevel_get_vec(vec1, v, v1, td); /* get the vector we will be projecting onto */ + BME_bevel_get_vec(vec2, v, v2, td); /* get the vector we will be projecting parallel to */ + len = len_v3(vec1); + normalize_v3(vec1); + + vtd = BME_get_transdata(td, sv); + vtd1 = BME_get_transdata(td, v); + vtd2 = BME_get_transdata(td, v1); + + if (vtd1->loc == NULL) { + /* this is a vert with data only for calculating initial weights */ + if (vtd1->weight < 0) { + vtd1->weight = 0; + } + scale = vtd1->weight / vtd1->factor; + if (!vtd1->max) { + vtd1->max = BME_new_transdata_float(td); + *vtd1->max = -1; + } + } + else { + scale = vtd1->weight; + } + vtd->max = vtd1->max; + + if (is_edge && vtd1->loc != NULL) { + maxfactor = vtd1->maxfactor; + } + else { + maxfactor = scale * BME_bevel_project_vec(vec1, vec2, up_vec, forward, td); + if (vtd->maxfactor > 0 && vtd->maxfactor < maxfactor) { + maxfactor = vtd->maxfactor; + } + } + + dis = BMO_elem_flag_test(bm, v1, BME_BEVEL_ORIG) ? len / 3 : len / 2; + if (is_edge || dis > maxfactor * value) { + dis = maxfactor * value; + } + madd_v3_v3v3fl(sv->co, v->co, vec1, dis); + sub_v3_v3v3(vec1, sv->co, vtd1->org); + dis = len_v3(vec1); + normalize_v3(vec1); + BME_assign_transdata(td, bm, sv, vtd1->org, vtd1->org, vec1, sv->co, dis, scale, maxfactor, vtd->max); + + return sv; +} + +#if 0 /* UNUSED */ +static float BME_bevel_set_max(BMVert *v1, BMVert *v2, float value, BME_TransData_Head *td) +{ + BME_TransData *vtd1, *vtd2; + float max, fac1, fac2, vec1[3], vec2[3], vec3[3]; + + BME_bevel_get_vec(vec1, v1, v2, td); + vtd1 = BME_get_transdata(td, v1); + vtd2 = BME_get_transdata(td, v2); + + if (vtd1->loc == NULL) { + fac1 = 0; + } + else { + copy_v3_v3(vec2, vtd1->vec); + mul_v3_fl(vec2, vtd1->factor); + if (dot_v3v3(vec1, vec1)) { + project_v3_v3v3(vec2, vec2, vec1); + fac1 = len_v3(vec2) / value; + } + else { + fac1 = 0; + } + } + + if (vtd2->loc == NULL) { + fac2 = 0; + } + else { + copy_v3_v3(vec3, vtd2->vec); + mul_v3_fl(vec3, vtd2->factor); + if (dot_v3v3(vec1, vec1)) { + project_v3_v3v3(vec2, vec3, vec1); + fac2 = len_v3(vec2) / value; + } + else { + fac2 = 0; + } + } + + if (fac1 || fac2) { + max = len_v3(vec1) / (fac1 + fac2); + if (vtd1->max && (*vtd1->max < 0 || max < *vtd1->max)) { + *vtd1->max = max; + } + if (vtd2->max && (*vtd2->max < 0 || max < *vtd2->max)) { + *vtd2->max = max; + } + } + else { + max = -1; + } + + return max; +} +#endif + +#if 0 /* UNUSED */ +static BMVert *BME_bevel_wire(BMesh *bm, BMVert *v, float value, int res, int UNUSED(options), BME_TransData_Head *td) +{ + BMVert *ov1, *ov2, *v1, *v2; + + ov1 = BM_edge_other_vert(v->e, v); + ov2 = BM_edge_other_vert(bmesh_disk_nextedge(v->e, v), v); + + /* split the edges */ + v1 = BME_bevel_split_edge(bm, v, ov1, NULL, NULL, value, td); + BMO_elem_flag_enable(bm, v1, BME_BEVEL_NONMAN); + v2 = BME_bevel_split_edge(bm, v, ov2, NULL, NULL, value, td); + BMO_elem_flag_enable(bm, v2, BME_BEVEL_NONMAN); + + if (value > 0.5) { + BME_bevel_set_max(v1, ov1, value, td); + BME_bevel_set_max(v2, ov2, value, td); + } + + /* remove the original vert */ + if (res) { + /* bmesh_jekv; */ + + //void BM_vert_collapse_faces(BMesh *bm, BMEdge *ke, BMVert *kv, float fac, int calcnorm){ + //hrm, why is there a fac here? it just removes a vert + BM_vert_collapse_edges(bm, v->e, v); + } + + return v1; +} +#endif + +static BMLoop *BME_bevel_edge(BMesh *bm, BMLoop *l, float value, int UNUSED(options), float *up_vec, BME_TransData_Head *td) +{ + BMVert *v1, *v2, *kv; + BMLoop *kl = NULL, *nl; + BMEdge *e, *ke, *se; + BMFace *f, *jf; + + f = l->f; + e = l->e; + + /* sanity check */ + if ( !BMO_elem_flag_test(bm, l->e, BME_BEVEL_BEVEL) && + (BMO_elem_flag_test(bm, l->v, BME_BEVEL_BEVEL) || BMO_elem_flag_test(bm, l->next->v, BME_BEVEL_BEVEL))) + { + return l; + } + + /* checks and operations for prev edge */ + /* first, check to see if this edge was inset previously */ + if ( !BMO_elem_flag_test(bm, l->prev->e, BME_BEVEL_ORIG) && + !BMO_elem_flag_test(bm, l->v, BME_BEVEL_NONMAN)) + { + kl = l->prev->radial_next; + kl = (kl->v == l->v) ? kl->prev : kl->next; + kv = l->v; + } + else { + kv = NULL; + } + /* get/make the first vert to be used in SFME */ + if (BMO_elem_flag_test(bm, l->v, BME_BEVEL_NONMAN)) { + v1 = l->v; + } + else { /* we'll need to split the previous edge */ + v1 = BME_bevel_split_edge(bm, l->v, NULL, l->prev, up_vec, value, td); + } + /* if we need to clean up geometry... */ + if (kv) { + se = l->next->e; + jf = NULL; + if (kl->v == kv) { + BM_face_split(bm, kl->f, kl->prev->v, kl->next->v, &nl, kl->prev->e); + ke = kl->e; + /* BMESH-TODO: jfke doesn't handle customdata */ + jf = bmesh_jfke(bm, kl->prev->radial_next->f, kl->f, kl->prev->e); + BM_vert_collapse_edges(bm, ke, kv); + } + else { + BM_face_split(bm, kl->f, kl->next->next->v, kl->v, &nl, kl->next->e); + ke = kl->e; + /* BMESH-TODO: jfke doesn't handle customdata */ + jf = bmesh_jfke(bm, kl->next->radial_next->f, kl->f, kl->next->e); + BM_vert_collapse_edges(bm, ke, kv); + } + /* find saved loop pointer */ + l = se->l; + while (l->f != jf) { + l = bmesh_radial_nextloop(l); + BLI_assert(l != se->l); + } + l = l->prev; + } + + /* checks and operations for the next edge */ + /* first, check to see if this edge was inset previously */ + if ( !BMO_elem_flag_test(bm, l->next->e, BME_BEVEL_ORIG) && + !BMO_elem_flag_test(bm, l->next->v, BME_BEVEL_NONMAN)) + { + kl = l->next->radial_next; + kl = (kl->v == l->next->v) ? kl->prev : kl->next; + kv = l->next->v; + } + else { + kv = NULL; + } + /* get/make the second vert to be used in SFME */ + if (BMO_elem_flag_test(bm, l->next->v, BME_BEVEL_NONMAN)) { + v2 = l->next->v; + } + else { /* we'll need to split the next edge */ + v2 = BME_bevel_split_edge(bm, l->next->v, NULL, l->next, up_vec, value, td); + } + /* if we need to clean up geometry... */ + if (kv) { + se = l->e; + jf = NULL; + if (kl->v == kv) { + BM_face_split(bm, kl->f, kl->prev->v, kl->next->v, &nl, kl->prev->e); + ke = kl->e; + /* BMESH-TODO: jfke doesn't handle customdata */ + jf = bmesh_jfke(bm, kl->prev->radial_next->f, kl->f, kl->prev->e); + BM_vert_collapse_edges(bm, ke, kv); + } + else { + BM_face_split(bm, kl->f, kl->next->next->v, kl->v, &nl, kl->next->e); + ke = kl->e; + /* BMESH-TODO: jfke doesn't handle customdata */ + jf = bmesh_jfke(bm, kl->next->radial_next->f, kl->f, kl->next->e); + BM_vert_collapse_edges(bm, ke, kv); + } + /* find saved loop pointer */ + l = se->l; + while (l->f != jf) { + l = bmesh_radial_nextloop(l); + BLI_assert(l != se->l); + } + } + + if (!BMO_elem_flag_test(bm, v1, BME_BEVEL_NONMAN) || !BMO_elem_flag_test(bm, v2, BME_BEVEL_NONMAN)) { + BM_face_split(bm, f, v2, v1, &l, e); + BMO_elem_flag_enable(bm, l->e, BME_BEVEL_BEVEL); + l = l->radial_next; + } + + if (l->f != f){ + //printf("Whoops! You got something out of order in BME_bevel_edge()!\n"); + } + + return l; +} + +static BMLoop *BME_bevel_vert(BMesh *bm, BMLoop *l, float value, int UNUSED(options), float *up_vec, BME_TransData_Head *td) +{ + BMVert *v1, *v2; + BMFace *f; + + /* get/make the first vert to be used in SFME */ + /* may need to split the previous edge */ + v1 = BME_bevel_split_edge(bm, l->v, NULL, l->prev, up_vec, value, td); + + /* get/make the second vert to be used in SFME */ + /* may need to split this edge (so move l) */ + l = l->prev; + v2 = BME_bevel_split_edge(bm, l->next->v, NULL, l->next, up_vec, value, td); + l = l->next->next; + + /* "cut off" this corner */ + f = BM_face_split(bm, l->f, v2, v1, NULL, l->e); + + return l; +} + +/* + * BME_bevel_poly + * + * Polygon inset tool: + * + * Insets a polygon/face based on the flagss of its vertices + * and edges. Used by the bevel tool only, for now. + * The parameter "value" is the distance to inset (should be negative). + * The parameter "options" is not currently used. + * + * Returns - + * A BMFace pointer to the resulting inner face. + */ +static BMFace *BME_bevel_poly(BMesh *bm, BMFace *f, float value, int options, BME_TransData_Head *td) +{ + BMLoop *l/*, *o */; + BME_TransData *vtd1, *vtd2; + float up_vec[3], vec1[3], vec2[3], vec3[3], fac1, fac2, max = -1; + int len, i; + BMIter iter; + + zero_v3(up_vec); + + /* find a good normal for this face (there's better ways, I'm sure) */ + BM_ITER(l, &iter, bm, BM_LOOPS_OF_FACE, f) { + BME_bevel_get_vec(vec1, l->v, l->next->v, td); + BME_bevel_get_vec(vec2, l->prev->v, l->v, td); + cross_v3_v3v3(vec3, vec2, vec1); + add_v3_v3(up_vec, vec3); + } + normalize_v3(up_vec); + + /* Can't use a BM_LOOPS_OF_FACE iterator here, because the loops are being modified + * and so the end condition will never hi */ + for (l = BM_FACE_FIRST_LOOP(f)->prev, i = 0, len = f->len; i < len; i++, l = l->next) { + if (BMO_elem_flag_test(bm, l->e, BME_BEVEL_BEVEL) && BMO_elem_flag_test(bm, l->e, BME_BEVEL_ORIG)) { + max = 1.0f; + l = BME_bevel_edge(bm, l, value, options, up_vec, td); + } + else if ( BMO_elem_flag_test(bm, l->v, BME_BEVEL_BEVEL) && + BMO_elem_flag_test(bm, l->v, BME_BEVEL_ORIG) && + !BMO_elem_flag_test(bm, l->prev->e, BME_BEVEL_BEVEL)) + { + max = 1.0f; + l = BME_bevel_vert(bm, l, value, options, up_vec, td); + } + } + + f = l->f; + + /* max pass */ + if (value > 0.5f && max > 0) { + max = -1; + BM_ITER(l, &iter, bm, BM_LOOPS_OF_FACE, f) { + if (BMO_elem_flag_test(bm, l->e, BME_BEVEL_BEVEL) || BMO_elem_flag_test(bm, l->e, BME_BEVEL_ORIG)) { + BME_bevel_get_vec(vec1, l->v, l->next->v, td); + vtd1 = BME_get_transdata(td, l->v); + vtd2 = BME_get_transdata(td, l->next->v); + if (vtd1->loc == NULL) { + fac1 = 0; + } + else { + copy_v3_v3(vec2, vtd1->vec); + mul_v3_fl(vec2, vtd1->factor); + if (dot_v3v3(vec1, vec1)) { + project_v3_v3v3(vec2, vec2, vec1); + fac1 = len_v3(vec2) / value; + } + else { + fac1 = 0; + } + } + if (vtd2->loc == NULL) { + fac2 = 0; + } + else { + copy_v3_v3(vec3, vtd2->vec); + mul_v3_fl(vec3, vtd2->factor); + if (dot_v3v3(vec1, vec1)) { + project_v3_v3v3(vec2, vec3, vec1); + fac2 = len_v3(vec2) / value; + } + else { + fac2 = 0; + } + } + if (fac1 || fac2) { + max = len_v3(vec1)/(fac1 + fac2); + if (vtd1->max && (*vtd1->max < 0 || max < *vtd1->max)) { + *vtd1->max = max; + } + if (vtd2->max && (*vtd2->max < 0 || max < *vtd2->max)) { + *vtd2->max = max; + } + } + } + } + } + + /* return l->f; */ + return NULL; +} + +static void BME_bevel_add_vweight(BME_TransData_Head *td, BMesh *bm, BMVert *v, float weight, float factor, int options) +{ + BME_TransData *vtd; + + if (BMO_elem_flag_test(bm, v, BME_BEVEL_NONMAN)) return; + BMO_elem_flag_enable(bm, v, BME_BEVEL_BEVEL); + if ((vtd = BME_get_transdata(td, v))) { + if (options & BME_BEVEL_EMIN) { + vtd->factor = 1.0; + if (vtd->weight < 0 || weight < vtd->weight) { + vtd->weight = weight; + } + } + else if (options & BME_BEVEL_EMAX) { + vtd->factor = 1.0; + if (weight > vtd->weight) { + vtd->weight = weight; + } + } + else if (vtd->weight < 0) { + vtd->factor = factor; + vtd->weight = weight; + } + else { + vtd->factor += factor; /* increment number of edges with weights (will be averaged) */ + vtd->weight += weight; /* accumulate all the weights */ + } + } + else { + /* we'll use vtd->loc == NULL to mark that this vert is not moving */ + vtd = BME_assign_transdata(td, bm, v, v->co, NULL, NULL, NULL, factor, weight, -1, NULL); + } +} + +static void bevel_init_verts(BMesh *bm, int options, BME_TransData_Head *td) +{ + BMVert *v; + BMIter iter; + float weight; + BM_ITER(v, &iter, bm, BM_VERTS_OF_MESH, NULL) { + weight = 0.0; + if (!BMO_elem_flag_test(bm, v, BME_BEVEL_NONMAN)) { + /* modifiers should not use selection */ + if(options & BME_BEVEL_SELECT){ + if(BM_elem_flag_test(v, BM_ELEM_SELECT)) weight = 1.0; + } + /* bevel weight NYI */ + else if(options & BME_BEVEL_WEIGHT) + weight = BM_elem_float_data_get(&bm->vdata, v, CD_BWEIGHT); + else + weight = 1.0; + if(weight > 0.0){ + BMO_elem_flag_enable(bm, v, BME_BEVEL_BEVEL); + BME_assign_transdata(td, bm, v, v->co, v->co, NULL, NULL, 1.0, weight, -1, NULL); + } + } + } +} + +static void bevel_init_edges(BMesh *bm, int options, BME_TransData_Head *td) +{ + BMEdge *e; + int count; + float weight; + BMIter iter; + BM_ITER(e, &iter, bm, BM_EDGES_OF_MESH, NULL) { + weight = 0.0; + if (!BMO_elem_flag_test(bm, e, BME_BEVEL_NONMAN)) { + if(options & BME_BEVEL_SELECT){ + if(BM_elem_flag_test(e, BM_ELEM_SELECT)) weight = 1.0; + } + else if(options & BME_BEVEL_WEIGHT) { + weight = BM_elem_float_data_get(&bm->edata, e, CD_BWEIGHT); + } + else { + weight = 1.0; + } + if(weight > 0.0){ + BMO_elem_flag_enable(bm, e, BME_BEVEL_BEVEL); + BMO_elem_flag_enable(bm, e->v1, BME_BEVEL_BEVEL); + BMO_elem_flag_enable(bm, e->v2, BME_BEVEL_BEVEL); + BME_bevel_add_vweight(td, bm, e->v1, weight, 1.0, options); + BME_bevel_add_vweight(td, bm, e->v2, weight, 1.0, options); + } + } + } + + /* clean up edges with 2 faces that share more than one edg */ + BM_ITER(e, &iter, bm, BM_EDGES_OF_MESH, NULL) { + if(BMO_elem_flag_test(bm, e, BME_BEVEL_BEVEL)) { + count = BM_face_share_edges(e->l->f, e->l->radial_next->f); + if(count > 1) BMO_elem_flag_disable(bm, e, BME_BEVEL_BEVEL); + } + } +} + +static BMesh *BME_bevel_initialize(BMesh *bm, int options, int UNUSED(defgrp_index), float UNUSED(angle), BME_TransData_Head *td) +{ + BMVert *v/*, *v2 */; + BMEdge *e/*, *curedg */; + BMFace *f; + BMIter iter; + int /* wire, */ len; + + /* tag non-manifold geometr */ + BM_ITER(v, &iter, bm, BM_VERTS_OF_MESH, NULL) { + BMO_elem_flag_enable(bm, v, BME_BEVEL_ORIG); + if(v->e){ + BME_assign_transdata(td, bm, v, v->co, v->co, NULL, NULL, 0, -1, -1, NULL); + if (!BM_vert_is_manifold(bm, v)) + BMO_elem_flag_enable(bm, v, BME_BEVEL_NONMAN); + /* test wire ver */ + len = BM_vert_edge_count(v); + if (len == 2 && BM_vert_is_wire(bm, v)) + BMO_elem_flag_disable(bm, v, BME_BEVEL_NONMAN); + } + else + BMO_elem_flag_enable(bm, v, BME_BEVEL_NONMAN); + } + + BM_ITER(e, &iter, bm, BM_EDGES_OF_MESH, NULL) { + BMO_elem_flag_enable(bm, e, BME_BEVEL_ORIG); + if (!BM_edge_is_manifold(bm, e)) { + BMO_elem_flag_enable(bm, e->v1, BME_BEVEL_NONMAN); + BMO_elem_flag_enable(bm, e->v2, BME_BEVEL_NONMAN); + BMO_elem_flag_enable(bm, e, BME_BEVEL_NONMAN); + } + if(BMO_elem_flag_test(bm, e->v1, BME_BEVEL_NONMAN) || BMO_elem_flag_test(bm, e->v2, BME_BEVEL_NONMAN)) BMO_elem_flag_enable(bm, e, BME_BEVEL_NONMAN); + } + + BM_ITER(f, &iter, bm, BM_FACES_OF_MESH, NULL) + BMO_elem_flag_enable(bm, f, BME_BEVEL_ORIG); + if(options & BME_BEVEL_VERT) bevel_init_verts(bm, options, td); + else bevel_init_edges(bm, options, td); + return bm; + +} + +#if 0 + +static BMesh *BME_bevel_reinitialize(BMesh *bm) +{ + BMVert *v; + BMEdge *e; + BMFace *f; + BMIter iter; + + BM_ITER(v, &iter, bm, BM_VERTS_OF_MESH, NULL) { + BMO_elem_flag_enable(bm, v, BME_BEVEL_ORIG); + } + BM_ITER(e, &iter, bm, BM_EDGES_OF_MESH, NULL) { + BMO_elem_flag_enable(bm, e, BME_BEVEL_ORIG); + } + BM_ITER(f, &iter, bm, BM_FACES_OF_MESH, NULL) { + BMO_elem_flag_enable(bm, f, BME_BEVEL_ORIG); + } + return bm; + +} + +#endif + +/** + * BME_bevel_mesh + * + * Mesh beveling tool: + * + * Bevels an entire mesh. It currently uses the flags of + * its vertices and edges to track topological changes. + * The parameter "value" is the distance to inset (should be negative). + * The parameter "options" is not currently used. + * + * Returns - + * A BMesh pointer to the BM passed as a parameter. + */ + +static BMesh *BME_bevel_mesh(BMesh *bm, float value, int UNUSED(res), int options, int UNUSED(defgrp_index), BME_TransData_Head *td) +{ + BMVert *v; + BMEdge *e, *curedge; + BMLoop *l, *l2; + BMFace *f; + BMIter iter; + + /* unsigned int i, len; */ + + /* bevel poly */ + BM_ITER(f, &iter, bm, BM_FACES_OF_MESH, NULL) { + if(BMO_elem_flag_test(bm, f, BME_BEVEL_ORIG)) { + BME_bevel_poly(bm, f, value, options, td); + } + } + + /* get rid of beveled edge */ + BM_ITER(e, &iter, bm, BM_EDGES_OF_MESH, NULL) { + if(BMO_elem_flag_test(bm, e, BME_BEVEL_BEVEL) && BMO_elem_flag_test(bm, e, BME_BEVEL_ORIG)) { + BM_faces_join_pair(bm, e->l->f, e->l->radial_next->f, e); + } + } + + /* link up corners and cli */ + BM_ITER(v, &iter, bm, BM_VERTS_OF_MESH, NULL) { + if(BMO_elem_flag_test(bm, v, BME_BEVEL_ORIG) && BMO_elem_flag_test(bm, v, BME_BEVEL_BEVEL)) { + curedge = v->e; + do{ + l = curedge->l; + l2 = l->radial_next; + if(l->v != v) l = l->next; + if(l2->v != v) l2 = l2->next; + if(l->f->len > 3) + BM_face_split(bm, l->f, l->next->v, l->prev->v, &l, l->e); /* clip this corner off */ + if(l2->f->len > 3) + BM_face_split(bm, l2->f, l2->next->v, l2->prev->v, &l, l2->e); /* clip this corner off */ + curedge = bmesh_disk_nextedge(curedge, v); + } while(curedge != v->e); + BME_Bevel_Dissolve_Disk(bm, v); + } + } + + /* Debug print, remov */ + BM_ITER(f, &iter, bm, BM_FACES_OF_MESH, NULL) { + if(f->len == 2){ + printf("warning"); + } + } + + return bm; +} + +BMesh *BME_bevel(BMEditMesh *em, float value, int res, int options, int defgrp_index, float angle, BME_TransData_Head **rtd) +{ + BMesh *bm = em->bm; + BMVert *v; + BMIter iter; + + BME_TransData_Head *td; + BME_TransData *vtd; + int i; + double fac = 1, d; + + td = BME_init_transdata(BLI_MEMARENA_STD_BUFSIZE); + /* recursion math courtesy of Martin Poirier (theeth) */ + for (i = 0; i < res - 1; i++) { + if (i == 0) fac += 1.0f / 3.0f; else fac += 1.0f / (3 * i * 2.0f); + } + d = 1.0f / fac; + + for (i = 0; i < res || (res == 0 && i == 0); i++) { + BMO_push(bm, NULL); + BME_bevel_initialize(bm, options, defgrp_index, angle, td); + //if (i != 0) BME_bevel_reinitialize(bm); + bmesh_begin_edit(bm, 0); + BME_bevel_mesh(bm, (float)d, res, options, defgrp_index, td); + bmesh_end_edit(bm, 0); + d /= (i == 0) ? 3.0 : 2.0; + BMO_pop(bm); + } + + BMEdit_RecalcTesselation(em); + + /* interactive preview? */ + if (rtd) { + *rtd = td; + return bm; + } + + /* otherwise apply transforms */ + BM_ITER(v, &iter, bm, BM_VERTS_OF_MESH, NULL) { + if ( (vtd = BME_get_transdata(td, v)) ) { + if (vtd->max && (*vtd->max > 0 && value > *vtd->max)) { + d = *vtd->max; + } + else { + d = value; + } + madd_v3_v3v3fl(v->co, vtd->org, vtd->vec, vtd->factor * d); + } + } + + BME_free_transdata(td); + return bm; +} diff --git a/source/blender/bmesh/tools/BME_dupe_ops.c b/source/blender/bmesh/tools/BME_dupe_ops.c new file mode 100644 index 00000000000..fb357390ecc --- /dev/null +++ b/source/blender/bmesh/tools/BME_dupe_ops.c @@ -0,0 +1,322 @@ +#if 0 + +/* + * BME_DUPLICATE.C + * + * This file contains functions for duplicating, copying, and splitting + * elements from a bmesh. + * + */ + +/* + * COPY VERTEX + * + * Copy an existing vertex from one bmesh to another. + * +*/ + +static BMVert *copy_vertex(BMMesh *source_mesh, BMVert *source_vertex, BMMesh *target_mesh, GHash *vhash) +{ + BMVert *target_vertex = NULL; + + /*create a new vertex*/ + target_vertex = BM_vert_create(target, source_vertex->co, NULL); + + /*insert new vertex into the vert hash*/ + BLI_ghash_insert(vhash, source_vertex, target_vertex); + + /*copy custom data in this function since we cannot be assured that byte layout is same between meshes*/ + CustomData_bmesh_copy_data(&source_mesh->vdata, &target_mesh->vdata, source_vertex->data, &target_vertex->data); + + /*Copy Markings*/ + if(BM_Is_Selected((BMHeader*)source_vertex)) BM_vert_select_set(target_mesh, target_vertex, TRUE); + if(BM_Is_Hidden((BMHeader*)source_vertex)) BM_Mark_Hidden((BMHeader*)target_vertex, 1); + + BMO_elem_flag_enable(target_mesh, (BMHeader*)target_vertex, DUPE_NEW); + + return target_vertex; +} + +/* + * COPY EDGE + * + * Copy an existing edge from one bmesh to another. + * +*/ + +static BMEdge *copy_edge(BMMesh *source_mesh, BMEdge *source_edge, BMMesh *target_mesh, GHash *vhash, GHash *ehash) +{ + BMEdge *target_edge = NULL; + BMVert *target_vert1, *target_vert2; + + /*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 edge*/ + target_edge = BM_edge_create(target_mesh, target_vert1, target_vert2, NULL, FALSE); + + /*insert new edge into the edge hash*/ + BLI_ghash_insert(ehash, source_edge, target_edge); + + /*copy custom data in this function since we cannot be assured that byte layout is same between meshes*/ + CustomData_bmesh_copy_data(&source_mesh->edata, &target_mesh->edata, source_edge->data, &target_edge->data); + + /*copy flags*/ + if(BM_Is_Selected((BMHeader*) source_edge)) BM_edge_select_set(target_mesh, target_edge, TRUE); + if(BM_Is_Hidden((BMHeader*) source_edge)) BM_Mark_Hidden(target_mesh, target_edge, 1); + if(BM_Is_Sharp((BMHeader*) source_edge)) BM_Mark_Sharp(target_edge, 1); + if(BM_Is_Seam((BMHeader*) source_edge)) BM_Mark_Seam(target_edge, 1); + if(BM_Is_Fgon((BMHeader*) source_edge)) BM_Mark_Fgon(target_edge, 1); + + BMO_elem_flag_enable(target_mesh, (BMHeader*)target_edge, DUPE_NEW); + + return target_edge; +} + +/* + * COPY FACE + * + * Copy an existing face from one bmesh to another. + * +*/ + +static BMFace *copy_face(BMMesh *source_mesh, BMFace *source_face, BMMesh *target_mesh, BMEdge **edar, GHash *verthash, GHash *ehash) +{ + BMEdge *target_edge; + BMVert *target_vert1, *target_vert2; + BMLoop *source_loop, *target_loop; + BMFace *target_face = NULL; + int i; + + /*lookup the first and second verts*/ + target_vert1 = BLI_ghash_lookup(vhash, source_face->lbase->v); + target_vert2 = BLI_ghash_lookup(vhash, source_face->lbase->next->v); + + /*lookup edges*/ + i = 0; + source_loop = source_face->lbase; + do{ + edar[i] = BLI_ghash_lookup(ehash, source_loop->e); + i++; + source_loop = source_loop->next; + }while(source_loop != source_face->lbase); + + /*create new face*/ + target_face = BM_face_create_ngon(target_mesh, target_vert1, target_vert2, edar, source_face->len, 0); + + /*we copy custom data by hand, we cannot assume that customdata byte layout will be exactly the same....*/ + CustomData_bmesh_copy_data(&source_mesh->pdata, &target_mesh->pdata, source_face->data, &target_face->data); + + /*copy flags*/ + if(BM_Is_Selected((BMHeader*)source_face)) BM_Select_face(target, target_face, TRUE); + if(BM_Is_Hidden((BMHeader*)source_face)) BM_Mark_Hidden((BMHeader*)target_face, 1); + + /*mark the face for output*/ + BMO_elem_flag_enable(target_mesh, (BMHeader*)target_face, DUPE_NEW); + + /*copy per-loop custom data*/ + source_loop = source_face->lbase; + target_loop = target_face->lbase; + do{ + CustomData_bmesh_copy_data(&source_mesh->ldata, &target_mesh->ldata, source_loop->data, &target_loop->data); + source_loop = source_loop->next; + target_loop = target_loop->next; + }while(source_loop != source_face->lbase); + + return target_face; +} + +/* + * COPY MESH + * + * Internal Copy function. +*/ + +/*local flag defines*/ + +#define DUPE_INPUT 1 /*input from operator*/ +#define DUPE_NEW 2 +#define DUPE_DONE 3 + +static void copy_mesh(BMMesh *source, BMMesh *target) +{ + + BMVert *v = NULL; + BMEdge *e = NULL, **edar = NULL; + BMLoop *l = NULL; + BMFace *f = NULL; + + BMIter verts; + BMIter edges; + BMIter faces; + BMIter loops; + + GHash *vhash; + GHash *ehash; + + int maxlength = 0, flag; + + /*initialize pointer hashes*/ + vhash = BLI_ghash_new(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp); + ehash = BLI_ghash_new(BLI_ghashutil_ptrrhash, BLI_ghashutil_ptrcmp); + + /*initialize edge pointer array*/ + for(f = BM_iter_new(&faces, source, BM_FACES, source, 0, NULL); f; f = BM_iter_step(&faces)){ + if(f->len > maxlength) maxlength = f->len; + } + edar = MEM_callocN(sizeof(BMEdge*) * maxlength, "BM copy mesh edge pointer array"); + + + /*first we dupe all flagged faces and their elements from source*/ + for(f = BM_iter_new(&faces, source, BM_FACES, source, 0, NULL); f; f= BM_iter_step(&faces)){ + if(BMO_elem_flag_test(source, (BMHeader*)f, DUPE_INPUT)){ + /*vertex pass*/ + for(v = BM_iter_new(&verts, source, BM_VERT_OF_FACE, f, 0, NULL); v; v = BM_iter_step(&verts)){ + if(!BMO_elem_flag_test(source, (BMHeader*)v, DUPE_DONE)){ + copy_vertex(source,v, target, vhash); + BMO_elem_flag_enable(source, (BMHeader*)v, DUPE_DONE); + } + } + + /*edge pass*/ + for(e = BM_iter_new(&edges, source, BM_EDGE_OF_FACE, f, 0, NULL); e; e = BMeshIter_step(&edges)){ + if(!BMO_elem_flag_test(source, (BMHeader*)e, DUPE_DONE)){ + copy_edge(source, e, target, vhash, ehash); + BMO_elem_flag_enable(source, (BMHeader*)e, DUPE_DONE); + } + } + copy_face(source, f, target, edar, vhash, ehash); + BMO_elem_flag_enable(source, (BMHeader*)f, DUPE_DONE); + } + } + + /*now we dupe all the edges*/ + for(e = BM_iter_new(&edges, source, BM_EDGES, source, 0, NULL); e; e = BM_iter_step(&edges)){ + if(BMO_elem_flag_test(source, (BMHeader*)e, DUPE_INPUT) && (!BMO_elem_flag_test(source, (BMHeader*)e, DUPE_DONE))){ + /*make sure that verts are copied*/ + if(!BMO_elem_flag_test(source, (BMHeader*)e->v1, DUPE_DONE){ + copy_vertex(source, e->v1, target, vhash); + BMO_elem_flag_enable(source, (BMHeader*)e->v1, DUPE_DONE); + } + if(!BMO_elem_flag_test(source, (BMHeader*)e->v2, DUPE_DONE){ + copy_vertex(source, e->v2, target, vhash); + BMO_elem_flag_enable(source, (BMHeader*)e->v2, DUPE_DONE); + } + /*now copy the actual edge*/ + copy_edge(source, e, target, vhash, ehash); + BMO_elem_flag_enable(source, (BMHeader*)e, DUPE_DONE); + } + } + + /*finally dupe all loose vertices*/ + for(v = BM_iter_new(&verts, source, BM_VERTS, source, 0, NULL); v; v = BM_iter_step(&verts)){ + if(BMO_elem_flag_test(source, (BMHeader*)v, DUPE_INPUT) && (!BMO_elem_flag_test(source, (BMHeader*)v, DUPE_DONE))){ + copy_vertex(source, v, target, vhash); + BMO_elem_flag_enable(source, (BMHeader*)v, DUPE_DONE); + } + } + + /*free pointer hashes*/ + BLI_ghash_free(vhash, NULL, NULL); + BLI_ghash_free(ehash, NULL, NULL); + + /*free edge pointer array*/ + if(edar) + MEM_freeN(edar); +} +/* +BMMesh *bmesh_make_mesh_from_mesh(BMMesh *bm, int allocsize[4]) +{ + BMMesh *target = NULL; + target = bmesh_make_mesh(allocsize); + + + CustomData_copy(&bm->vdata, &target->vdata, CD_MASK_BMESH, CD_CALLOC, 0); + CustomData_copy(&bm->edata, &target->edata, CD_MASK_BMESH, CD_CALLOC, 0); + CustomData_copy(&bm->ldata, &target->ldata, CD_MASK_BMESH, CD_CALLOC, 0); + CustomData_copy(&bm->pdata, &target->pdata, CD_MASK_BMESH, CD_CALLOC, 0); + + + CustomData_bmesh_init_pool(&target->vdata, allocsize[0]); + CustomData_bmesh_init_pool(&target->edata, allocsize[1]); + CustomData_bmesh_init_pool(&target->ldata, allocsize[2]); + CustomData_bmesh_init_pool(&target->pdata, allocsize[3]); + + bmesh_begin_edit(bm); + bmesh_begin_edit(target); + + bmesh_copy_mesh(bm, target, 0); + + bmesh_end_edit(bm); + bmesh_end_edit(target); + + return target; + +} +*/ + +void dupeop_exec(BMMesh *bm, BMOperator *op) +{ + BMOperator *dupeop = op; + BMOpSlot *vinput, *einput, *finput, *vnew, *enew, *fnew; + int i; + + vinput = BMO_Get_Slot(dupeop, BMOP_DUPE_VINPUT); + einput = BMO_Get_Slot(dupeop, BMOP_DUPE_EINPUT); + finput = BMO_Get_Slot(dupeop, BMOP_DUPE_FINPUT); + + /*go through vinput, einput, and finput and flag elements with private flags*/ + BMO_slot_buffer_flag_enable(bm, dupeop, BMOP_DUPE_VINPUT, DUPE_INPUT); + BMO_slot_buffer_flag_enable(bm, dupeop, BMOP_DUPE_EINPUT, DUPE_INPUT); + BMO_slot_buffer_flag_enable(bm, dupeop, BMOP_DUPE_FINPUT, DUPE_INPUT); + + /*use the internal copy function*/ + copy_mesh(bm, bm); + + /*Output*/ + /*First copy the input buffers to output buffers - original data*/ + BMO_Copy_Opslot_Buffer_Alloc(dupeop, vinput, BMO_Get_Slot(dupeop, BMOP_DUPE_VORIGINAL)); + BMO_Copy_Opslot_Buffer_Alloc(dupeop, einput, BMO_Get_Slot(dupeop, BMOP_DUPE_EORIGINAL)); + BMO_Copy_Opslot_Buffer_Alloc(dupeop, finput, BMO_Get_Slot(dupeop, BMOP_DUPE_FORIGINAL)); + + /*Now alloc the new output buffers*/ + BMO_slot_from_flag(bm, dupeop, BMOP_DUPE_VNEW, DUPE_NEW, BMESH_VERT); + BMO_slot_from_flag(bm, dupeop, BMOP_DUPE_ENEW, DUPE_NEW, BMESH_EDGE); + BMO_slot_from_flag(bm, dupeop, BMOP_DUPE_FNEW, DUPE_NEW, BMESH_FACE); +} + +void splitop_exec(BMMesh *bm, BMOperator *op) +{ + BMOperator *splitop = op; + BMOperator dupeop; + BMOperator delop; + + /*initialize our sub-operators*/ + BMO_op_init(&dupeop, BMOP_DUPE); + BMO_op_init(&delop, BMOP_DEL); + + BMO_Connect(BMO_Get_Slot(splitop, BMOP_SPLIT_VINPUT),BMO_Get_Slot(&dupeop, BMOP_DUPE_VINPUT)); + BMO_Connect(BMO_Get_Slot(splitop, BMOP_SPLIT_EINPUT),BMO_Get_Slot(&dupeop, BMOP_DUPE_EINPUT)); + BMO_Connect(BMO_Get_Slot(splitop, BMOP_SPLIT_FINPUT),BMO_Get_Slot(&dupeop, BMOP_DUPE_FINPUT)); + + BMO_op_exec(&dupeop); + + /*connect outputs of dupe to delete*/ + BMO_Connect(BMO_Get_Slot(&dupeop, BMOP_DUPE_VORIGINAL), BMO_Get_Slot(&delop, BMOP_DEL_VINPUT)); + BMO_Connect(BMO_Get_Slot(&dupeop, BMOP_DUPE_EORIGINAL), BMO_Get_Slot(&delop, BMOP_DEL_EINPUT)); + BMO_Connect(BMO_Get_Slot(&dupeop, BMOP_DUPE_FORIGINAL), BMO_Get_Slot(&delop, BMOP_DEL_FINPUT)); + + BMO_op_exec(&delop); + + /*now we make our outputs by copying the dupe outputs*/ + BMO_Copy_Buffer_Alloc(BMO_Get_Slot(&dupeop, BMOP_DUPE_VNEW), BMO_Get_Slot(splitop, BMOP_SPLIT_VOUTPUT)); + BMO_Copy_Buffer_Alloc(BMO_Get_Slot(&dupeop, BMOP_DUPE_ENEW), BMO_Get_Slot(splitop, BMOP_SPLIT_EOUTPUT)); + BMO_Copy_Buffer_Alloc(BMO_Get_Slot(&dupeop, BMOP_DUPE_FNEW), BMO_Get_Slot(splitop, BMOP_SPLIT_FOUTPUT)); + + /*cleanup*/ + BMO_op_finish(&dupeop); + BMO_op_finish(&delop); +} + +#endif diff --git a/source/blender/bmesh/tools/BME_duplicate.c b/source/blender/bmesh/tools/BME_duplicate.c new file mode 100644 index 00000000000..11151de9ed7 --- /dev/null +++ b/source/blender/bmesh/tools/BME_duplicate.c @@ -0,0 +1,310 @@ +#if 0 + +/* + * BME_DUPLICATE.C + * + * This file contains functions for duplicating, copying, and splitting + * elements from a bmesh. + * + */ + +/* + * BMESH COPY VERTEX + * + * Copy an existing vertex from one bmesh to another. + * +*/ + +static BMVert *bmesh_copy_vertex(BMMesh *source_mesh, BMVert *source_vertex, BMMesh *target_mesh, GHash *vhash) +{ + BMVert *target_vertex = NULL; + + /*create a new vertex*/ + target_vertex = bmesh_make_vert(target, source_vertex->co, NULL); + + /*insert new vertex into the vert hash*/ + BLI_ghash_insert(vhash, source_vertex, target_vertex); + + /*copy custom data in this function since we cannot be assured that byte layout is same between meshes*/ + CustomData_bmesh_copy_data(&source_mesh->vdata, &target_mesh->vdata, source_vertex->data, &target_vertex->data); + + /*copy flags*/ + if(bmesh_test_flag(source_vertex, BMESH_SELECT)) bmesh_set_flag(target_vertex, BMESH_SELECT); + if(bmesh_test_flag(source_vertex, BMESH_HIDDEN)) bmesh_set_flag(target_vertex, BMESH_HIDDEN); + + return target_vertex; +} + +/* + * BMESH COPY EDGE + * + * Copy an existing edge from one bmesh to another. + * +*/ + +static BMEdge *bmesh_copy_edge(BMMesh *source_mesh, BMEdge *source_edge, BMMesh *target_mesh, GHash *vhash, GHash *ehash) +{ + BMEdge *target_edge = NULL; + BMVert *target_vert1, *target_vert2; + + /*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 edge*/ + target_edge = bmesh_make_edge(target_mesh, target_vert1, target_vert2, NULL, 0); + + /*insert new edge into the edge hash*/ + BLI_ghash_insert(ehash, source_edge, target_edge); + + /*copy custom data in this function since we cannot be assured that byte layout is same between meshes*/ + CustomData_bmesh_copy_data(&source_mesh->edata, &target_mesh->edata, source_edge->data, &target_edge->data); + + /*copy flags*/ + if(bmesh_test_flag(source_edge, BMESH_SELECT)) bmesh_set_flag(target_edge, BMESH_SELECT); + if(bmesh_test_flag(source_edge, BMESH_HIDDEN)) bmesh_set_flag(target_edge, BMESH_SELECT); + if(bmesh_test_flag(source_edge, BMESH_SHARP)) bmesh_set_flag(target_edge, BMESH_SHARP); + if(bmesh_test_flag(source_edge, BMESH_SEAM)) bmesh_set_flag(target_edge, BMESH_SEAM); + if(bmesh_test_flag(source_edge, BMESH_FGON)) bmesh_set_flag(target_edge, BMESH_FGON); + + return target_edge; +} + +/* + * BMESH COPY FACE + * + * Copy an existing face from one bmesh to another. + * +*/ + +static BMFace *bmesh_copy_face(BMMesh *source_mesh, BMFace *source_face, BMMesh *target_mesh, BMEdge **edar, GHash *verthash, GHash *ehash) +{ + BMEdge *target_edge; + BMVert *target_vert1, *target_vert2; + BMLoop *source_loop, *target_loop; + BMFace *target_face = NULL; + int i; + + + /*lookup the first and second verts*/ + target_vert1 = BLI_ghash_lookup(vhash, source_face->lbase->v); + target_vert2 = BLI_ghash_lookup(vhash, source_face->lbase->next->v); + + /*lookup edges*/ + i = 0; + source_loop = source_face->lbase; + do{ + edar[i] = BLI_ghash_lookup(ehash, source_loop->e); + i++; + source_loop = source_loop->next; + }while(source_loop != source_face->lbase); + + /*create new face*/ + target_face = bmesh_make_ngon(target_mesh, target_vert1, target_vert2, edar, source_face->len, 0); + + /*we copy custom data by hand, we cannot assume that customdata byte layout will be exactly the same....*/ + CustomData_bmesh_copy_data(&source_mesh->pdata, &target_mesh->pdata, source_face->data, &target_face->data); + + /*copy flags*/ + if(bmesh_test_flag(source_face, BMESH_SELECT)) bmesh_set_flag(target_face, BMESH_SELECT); + if(bmesh_test_flag(source_face, BMESH_HIDDEN)) bmesh_set_flag(target_face, BMESH_HIDDEN); + + /*mark the face as dirty for normal and tesselation calcs*/ + bmesh_set_flag(target_face, BMESH_DIRTY); + + /*copy per-loop custom data*/ + source_loop = source_face->lbase; + target_loop = target_face->lbase; + do{ + CustomData_bmesh_copy_data(&source_mesh->ldata, &target_mesh->ldata, source_loop->data, &target_loop->data); + source_loop = source_loop->next; + target_loop = target_loop->next; + }while(source_loop != source_face->lbase); + + return target_face; +} + +/* + * BMESH COPY MESH + * + * Internal Copy function. copies flagged elements from + * source to target, which may in fact be the same mesh. + * Note that if __flag is 0, all elements will be copied. + * +*/ + +static void bmesh_copy_mesh(BMMesh *source, BMMesh *target, int __flag) +{ + + BMVert *v; + BMEdge *e, **edar; + BMLoop *l; + BMFace *f; + + BMIter verts; + BMIter edges; + BMIter faces; + BMIter loops; + + GHash *vhash; + GHash *ehash; + + int maxlength = 0, flag; + + /*initialize pointer hashes*/ + vhash = BLI_ghash_new(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp); + ehash = BLI_ghash_new(BLI_ghashutil_ptrrhash, BLI_ghashutil_ptrcmp); + + /*initialize edge pointer array*/ + for(f = BMeshIter_init(faces, BM_FACES, source, 0); f; f = BMeshIter_step(faces)){ + if(f->len > maxlength) maxlength = f->len; + } + edar = MEM_callocN(sizeof(BMEdge*) * maxlength, "BM copy mesh edge pointer array"); + + /*begin modelling loop for target*/ + bmesh_begin_edit(target); + + /*we make special exception for __flag == 0... we copy all*/ + if(!__flag){ + flag = BMESH_DUPE; + for(v = BMeshIter_init(verts, BM_VERTS, source, 0); v; v = BMeshIter_step(verts)) bmesh_set_flag(v, BMESH_DUPE); + for(e = BMeshIter_init(verts, BM_EDGES, source, 0); e; e = BMeshIter_step(edges)) bmesh_set_flag(e, BMESH_DUPE); + for(f = BMeshIter_init(faces, BM_FACES, source, 0); f; f = BMeshIter_step(faces)) bmesh_set_flag(f, BMESH_DUPE); + } else{ + flag = __flag; + } + + /*first we dupe all flagged faces and their elements from source*/ + for(f = BMeshIter_init(faces, BM_FACES, source, 0); f; f= BMeshIter_step(faces)){ + if(bmesh_test_flag(f, flag)){ + /*vertex pass*/ + for(l = BMeshIter_init(loops, BMESH_LOOP_OF_MESH, f, 0); l; l = BMeshIter_step(loops)){ + if(!bmesh_test_flag(l->v, BMESH_DUPED)){ + bmesh_copy_vertex(source,l->v, target, vhash); + bmesh_set_flag(l->v, BMESH_DUPED); + } + } + + /*edge pass*/ + for(l = BMeshIter_init(loops, BMESH_LOOP_OF_MESH, f, 0); l; l = BMeshIter_step(loops)){ + if(!bmesh_test_flag(l->e, BMESH_DUPED)){ + bmesh_copy_edge(source, l->e, target, vhash, ehash); + bmesh_set_flag(l->e, BMESH_DUPED); + } + } + bmesh_copy_face(source, f, target, edar, vhash, ehash); + bmesh_set_flag(f, BMESH_DUPED); + } + } + + /*now we dupe all the edges*/ + for(e = BMeshIter_init(edges, BM_EDGES, source, 0); e; e = BMeshIter_step(edges)){ + if(bmesh_test_flag(e, flag) && (!bmesh_test_flag(e, BMESH_DUPED))){ + /*make sure that verts are copied*/ + if(!bmesh_test_flag(e->v1, BMESH_DUPED)){ + bmesh_copy_vertex(source, e->v1, target, vhash); + bmesh_set_flag(e->v1, BMESH_DUPED); + } + if(!bmesh_test_flag(e->v2, BMESH_DUPED)){ + bmesh_copy_vertex(source, e->v2, target, vhash); + bmesh_set_flag(e->v2, BMESH_DUPED); + } + /*now copy the actual edge*/ + bmesh_copy_edge(source, e, target, vhash, ehash); + bmesh_set_flag(e, BMESH_DUPED); + } + } + + /*finally dupe all loose vertices*/ + for(v = BMeshIter_init(verts, BM_VERTS, bm, 0); v; v = BMeshIter_step(verts)){ + if(bmesh_test_flag(v, flag) && (!bmesh_test_flag(v, BMESH_DUPED))){ + bmesh_copy_vertex(source, v, target, vhash); + bmesh_set_flag(v, BMESH_DUPED); + } + } + + /*finish*/ + bmesh_end_edit(target, BMESH_CALC_NORM | BMESH_CALC_TESS); + + /*free pointer hashes*/ + BLI_ghash_free(vhash, NULL, NULL); + BLI_ghash_free(ehash, NULL, NULL); + + /*free edge pointer array*/ + MEM_freeN(edar); +} + +/* + * BMESH MAKE MESH FROM MESH + * + * Creates a new mesh by duplicating an existing one. + * +*/ + +BMMesh *bmesh_make_mesh_from_mesh(BMMesh *bm, int allocsize[4]) +{ + BMMesh *target = NULL; + target = bmesh_make_mesh(allocsize); + + /*copy custom data layout*/ + CustomData_copy(&bm->vdata, &target->vdata, CD_MASK_BMESH, CD_CALLOC, 0); + CustomData_copy(&bm->edata, &target->edata, CD_MASK_BMESH, CD_CALLOC, 0); + CustomData_copy(&bm->ldata, &target->ldata, CD_MASK_BMESH, CD_CALLOC, 0); + CustomData_copy(&bm->pdata, &target->pdata, CD_MASK_BMESH, CD_CALLOC, 0); + + /*initialize memory pools*/ + CustomData_bmesh_init_pool(&target->vdata, allocsize[0]); + CustomData_bmesh_init_pool(&target->edata, allocsize[1]); + CustomData_bmesh_init_pool(&target->ldata, allocsize[2]); + CustomData_bmesh_init_pool(&target->pdata, allocsize[3]); + + bmesh_begin_edit(bm); + bmesh_begin_edit(target); + + bmesh_copy_mesh(bm, target, 0); /* copy all elements */ + + bmesh_end_edit(bm); + bmesh_end_edit(target); + + return target; + +} + +/* + * BMESH SPLIT MESH + * + * Copies flagged elements then deletes them. + * +*/ + +void bmesh_split_mesh(BMMesh *bm, int flag) +{ + BMVert *v; + BMEdge *e; + BMFace *f; + + BMIter verts; + BMIter edges; + BMIter faces; + + bmesh_begin_edit(bm); + bmesh_copy_mesh(bm, bm, flag); + + /*mark verts for deletion*/ + for(v = BMeshIter_init(verts, BM_VERTS, bm, 0); v; v = BMeshIter_step(verts)){ + if(bmesh_test_flag(v, flag)) bmesh_delete_vert(bm, v); + } + /*mark edges for deletion*/ + for(e = BMeshIter_init(edges, BM_EDGES, bm, 0); e; e = BMeshIter_step(edges)){ + if(bmesh_test_flag(e, flag)) bmesh_delete_edge(bm, e); + + } + /*mark faces for deletion*/ + for(f = BMeshIter_init(faces, BM_FACES, bm, 0); f; f= BMeshIter_step(faces)){ + if(bmesh_tes t_flag(f, flag)) bmesh_delete_face(bm, f); + + } + bmesh_end_edit(bm); +} + +#endif diff --git a/source/blender/bmesh/tools/BME_weld.c b/source/blender/bmesh/tools/BME_weld.c new file mode 100644 index 00000000000..fe1a340d94a --- /dev/null +++ b/source/blender/bmesh/tools/BME_weld.c @@ -0,0 +1,341 @@ +#if + +/* + * BME_WELD.C + * + * This file contains functions for welding + * elements in a mesh togather (remove doubles, + * collapse, ect). + * + * TODO: + * -Rewrite this to fit into the new API + * -Seperate out find doubles code and put it in + * BME_queries.c + * +*/ + + +/********* qsort routines *********/ + + +typedef struct xvertsort { + float x; + BMVert *v1; +} xvertsort; + +static int vergxco(const void *v1, const void *v2) +{ + const xvertsort *x1=v1, *x2=v2; + + if( x1->x > x2->x ) return 1; + else if( x1->x < x2->x) return -1; + return 0; +} + +struct facesort { + unsigned long x; + struct BMFace *f; +}; + + +static int vergface(const void *v1, const void *v2) +{ + const struct facesort *x1=v1, *x2=v2; + + if( x1->x > x2->x ) return 1; + else if( x1->x < x2->x) return -1; + return 0; +} + + + +/*break this into two functions.... 'find doubles' and 'remove doubles'?*/ + +static void BME_remove_doubles__splitface(BME_Mesh *bm,BMFace *f,GHash *vhash) +{ + BMVert *doub=NULL, *target=NULL; + BME_Loop *l; + BMFace *f2=NULL; + int split=0; + + l=f->loopbase; + do{ + if(l->v->tflag1 == 2){ + target = BLI_ghash_lookup(vhash,l->v); + if((BME_vert_in_face(target,f)) && (target != l->next->v) && (target != l->prev->v)){ + doub = l->v; + split = 1; + break; + } + } + + l= l->next; + }while(l!= f->loopbase); + + if(split){ + f2 = BME_SFME(bm,f,doub,target,NULL); + BME_remove_doubles__splitface(bm,f,vhash); + BME_remove_doubles__splitface(bm,f2,vhash); + } +} + +int BME_remove_doubles(BME_Mesh *bm, float limit) +{ + + /* all verts with (flag & 'flag') are being evaluated */ + BMVert *v, *v2, *target; + BMEdge *e, **edar, *ne; + BME_Loop *l; + BMFace *f, *nf; + xvertsort *sortblock, *sb, *sb1; + struct GHash *vhash; + struct facesort *fsortblock, *vsb, *vsb1; + int a, b, test, amount=0, found; + float dist; + + /*Build a hash table of doubles to thier target vert/edge.*/ + vhash = BLI_ghash_new(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp); + + /*count amount of selected vertices*/ + for(v=BME_first(bm,BME_VERT);v;v=BME_next(bm,BME_VERT,v)){ + if(BME_SELECTED(v))amount++; + } + + /*qsort vertices based upon average of coordinate. We test this way first.*/ + sb= sortblock= MEM_mallocN(sizeof(xvertsort)*amount,"sortremovedoub"); + + for(v=BME_first(bm,BME_VERT);v;v=BME_next(bm,BME_VERT,v)){ + if(BME_SELECTED(v)){ + sb->x = v->co[0]+v->co[1]+v->co[2]; + sb->v1 = v; + sb++; + } + } + qsort(sortblock, amount, sizeof(xvertsort), vergxco); + + /* test for doubles */ + sb= sortblock; + for(a=0; a<amount; a++) { + v= sb->v1; + if(!(v->tflag1)) { //have we tested yet? + sb1= sb+1; + for(b=a+1; b<amount; b++) { + /* first test: simple distance. Simple way to discard*/ + dist= sb1->x - sb->x; + if(dist > limit) break; + + /* second test: have we already done this vertex? + (eh this should be swapped, simple equality test should be cheaper than math above... small savings + though) */ + v2= sb1->v1; + if(!(v2->tflag1)) { + dist= fabsf(v2->co[0]-v->co[0]); + if(dist<=limit) { + dist= fabsf(v2->co[1]-v->co[1]); + if(dist<=limit) { + dist= fabsf(v2->co[2]-v->co[2]); + if(dist<=limit) { + /*v2 is a double of v. We want to throw out v1 and relink everything to v*/ + BLI_ghash_insert(vhash,v2, v); + v->tflag1 = 1; //mark this vertex as a target + v->tflag2++; //increase user count for this vert. + v2->tflag1 = 2; //mark this vertex as a double. + BME_VISIT(v2); //mark for delete + } + } + } + } + sb1++; + } + } + sb++; + } + MEM_freeN(sortblock); + + + /*todo... figure out what this is for... + for(eve = em->verts.first; eve; eve=eve->next) + if((eve->f & flag) && (eve->f & 128)) + EM_data_interp_from_verts(eve, eve->tmp.v, eve->tmp.v, 0.5f); + */ + + /*We cannot collapse a vertex onto another vertex if they share a face and are not connected via a collapsable edge. + so to deal with this we simply find these offending vertices and split the faces. Note that this is not optimal, but works. + */ + + + for(f=BME_first(bm,BME_POLY);f;f=BME_next(bm,BME_POLY,f)){ + if(!(BME_NEWELEM(f))){ + BME_remove_doubles__splitface(bm,f,vhash); + } + } + + for(e=BME_first(bm,BME_EDGE);e;e=BME_next(bm,BME_EDGE,e)){ + /*If either vertices of this edge are a double, we must mark it for removal and we create a new one.*/ + if(e->v1->tflag1 == 2 || e->v2->tflag1 == 2){ + v = v2 = NULL; + /*For each vertex in the edge, test to find out what it should equal now.*/ + if(e->v1->tflag1 == 2) v= BLI_ghash_lookup(vhash,e->v1); + else v = e->v1; + if(e->v2->tflag1 == 2) v2 = BLI_ghash_lookup(vhash,e->v2); + else v2 = e->v2; + + /*small optimization, test to see if the edge needs to be rebuilt at all*/ + if((e->v1 != v) || (e->v2 != v2)){ /*will this always be true of collapsed edges?*/ + if(v == v2) e->tflag1 = 2; /*mark as a collapsed edge*/ + else if(!BME_disk_existedge(v,v2)) ne = BME_ME(bm,v,v2); + BME_VISIT(e); /*mark for delete*/ + } + } + } + + + /* need to remove double edges as well. To do this we decide on one edge to keep, + * and if its inserted into hash then we need to remove all other + * edges incident upon and relink.*/ + /* + * REBUILD FACES + * + * Loop through double faces and if they have vertices that have been flagged, they need to be rebuilt. + * We do this by looking up the face rebuild faces. + * loop through original face, for each loop, if the edge it is attached to is marked for delete and has no + * other edge in the hash edge, then we know to skip that loop on face recreation. Simple. + */ + + /*1st loop through, just marking elements*/ + for(f=BME_first(bm,BME_POLY);f;f=BME_next(bm,BME_POLY,f)){ //insert bit here about double edges, mark with a flag (e->tflag2) so that we can nuke it later. + l = f->loopbase; + do{ + if(l->v->tflag1 == 2) f->tflag1 = 1; //double, mark for rebuild + if(l->e->tflag1 != 2) f->tflag2++; //count number of edges in the new face. + l=l->next; + }while(l!=f->loopbase); + } + + /*now go through and create new faces*/ + for(f=BME_first(bm,BME_POLY);f;f=BME_next(bm,BME_POLY,f)){ + if(f->tflag1 && f->tflag2 < 3) BME_VISIT(f); //mark for delete + else if (f->tflag1 == 1){ /*is the face marked for rebuild*/ + edar = MEM_callocN(sizeof(BMEdge *)*f->tflag2,"Remove doubles face creation array."); + a=0; + l = f->loopbase; + do{ + v = l->v; + v2 = l->next->v; + if(l->v->tflag1 == 2) v = BLI_ghash_lookup(vhash,l->v); + if(l->next->v->tflag1 == 2) v2 = BLI_ghash_lookup(vhash,l->next->v); + ne = BME_disk_existedge(v,v2); //use BME_disk_next_edgeflag here or something to find the edge that is marked as 'target'. + //add in call here to edge doubles hash array... then bobs your uncle. + if(ne){ + edar[a] = ne; + a++; + } + l=l->next; + }while(l!=f->loopbase); + + if(BME_vert_in_edge(edar[1],edar[0]->v2)){ + v = edar[0]->v1; + v2 = edar[0]->v2; + } + else{ + v = edar[0]->v2; + v2 = edar[0]->v1; + } + + nf = BME_MF(bm,v,v2,edar,f->tflag2); + + /*copy per loop data here*/ + if(nf){ + BME_VISIT(f); //mark for delete + } + MEM_freeN(edar); + } + } + + /*count amount of removed vert doubles*/ + a = 0; + for(v=BME_first(bm,BME_VERT);v;v=BME_next(bm,BME_VERT,v)){ + if(v->tflag1 == 2) a++; + } + + /*free memory and return amount removed*/ + remove_tagged_polys(bm); + remove_tagged_edges(bm); + remove_tagged_verts(bm); + BLI_ghash_free(vhash,NULL, NULL); + BME_selectmode_flush(bm); + return a; +} + +static void BME_MeshWalk__collapsefunc(void *userData, BMEdge *applyedge) +{ + int index; + GHash *collected = userData; + index = BLI_ghash_size(collected); + if(!BLI_ghash_lookup(collected,applyedge->v1)){ + BLI_ghash_insert(collected,index,applyedge->v1); + index++; + } + if(!BLI_ghash_lookup(collected,applyedge->v2)){ + BLI_ghash_insert(collected,index,applyedge->v2); + } +} + +void BME_collapse_edges(BME_Mesh *bm) +{ + + BMVert *v, *cvert; + GHash *collected; + float min[3], max[3], cent[3]; + int size, i=0, j, num=0; + + for(v=BME_first(bm,BME_VERT);v;v=BME_next(bm,BME_VERT,v)){ + if(!(BME_ISVISITED(v)) && v->edge){ + /*initiate hash table*/ + collected = BLI_ghash_new(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp); + /*do the walking.*/ + BME_MeshWalk(bm,v,BME_MeshWalk__collapsefunc,collected,BME_RESTRICTSELECT); + /*now loop through the hash table twice, once to calculate bounding box, second time to do the actual collapse*/ + size = BLI_ghash_size(collected); + /*initial values*/ + copy_v3_v3(min,v->co); + copy_v3_v3(max,v->co); + cent[0] = cent[1] = cent[2]=0; + for(i=0; i<size; i++){ + cvert = BLI_ghash_lookup(collected,i); + cent[0] = cent[0] + cvert->co[0]; + cent[1] = cent[1] + cvert->co[1]; + cent[2] = cent[2] + cvert->co[2]; + } + + cent[0] = cent[0] / size; + cent[1] = cent[1] / size; + cent[2] = cent[2] / size; + + for(i=0; i<size; i++){ + cvert = BLI_ghash_lookup(collected,i); + copy_v3_v3(cvert->co,cent); + num++; + } + /*free the hash table*/ + BLI_ghash_free(collected,NULL, NULL); + } + } + /*if any collapsed, call remove doubles*/ + if(num){ + //need to change selection mode here, OR do something else? Or does tool change selection mode? + //selectgrep + //first clear flags + BMEdge *e; + BMFace *f; + BME_clear_flag_all(bm,BME_VISITED); + for(v=BME_first(bm,BME_VERT); v; v=BME_next(bm,BME_VERT,v)) v->tflag1 = v->tflag2 = 0; + for(e=BME_first(bm,BME_EDGE); e; e=BME_next(bm,BME_EDGE,e)) e->tflag1 = e->tflag2 = 0; + for(f=BME_first(bm,BME_POLY); f; f=BME_next(bm,BME_POLY,f)) f->tflag1 = f->tflag2 = 0; + /*now call remove doubles*/ + BME_remove_doubles(bm,0.0000001); + } + BME_selectmode_flush(bm); +} + +#endif |