Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'source/blender/bmesh/tools/bmesh_bevel.c')
-rw-r--r--source/blender/bmesh/tools/bmesh_bevel.c313
1 files changed, 263 insertions, 50 deletions
diff --git a/source/blender/bmesh/tools/bmesh_bevel.c b/source/blender/bmesh/tools/bmesh_bevel.c
index 74841dc2756..51a0fa4b2cc 100644
--- a/source/blender/bmesh/tools/bmesh_bevel.c
+++ b/source/blender/bmesh/tools/bmesh_bevel.c
@@ -205,13 +205,36 @@ static int bev_debug_flags = 0;
#define DEBUG_OLD_PROJ_TO_PERP_PLANE (bev_debug_flags & 2)
#define DEBUG_OLD_FLAT_MID (bev_debug_flags & 4)
+/* this flag values will get set on geom we want to return in 'out' slots for edges and verts */
+#define EDGE_OUT 4
+#define VERT_OUT 8
+
+/* If we're called from the modifier, tool flags aren't available, but don't need output geometry */
+static void flag_out_edge(BMesh *bm, BMEdge *bme)
+{
+ if (bm->use_toolflags)
+ BMO_edge_flag_enable(bm, bme, EDGE_OUT);
+}
+
+static void flag_out_vert(BMesh *bm, BMVert *bmv)
+{
+ if (bm->use_toolflags)
+ BMO_vert_flag_enable(bm, bmv, VERT_OUT);
+}
+
+static void disable_flag_out_edge(BMesh *bm, BMEdge *bme)
+{
+ if (bm->use_toolflags)
+ BMO_edge_flag_disable(bm, bme, EDGE_OUT);
+}
+
/* Are d1 and d2 parallel or nearly so? */
static bool nearly_parallel(const float d1[3], const float d2[3])
{
float ang;
ang = angle_v3v3(d1, d2);
- return (fabsf(ang) < BEVEL_EPSILON_ANG) || (fabsf(ang - M_PI) < BEVEL_EPSILON_ANG);
+ return (fabsf(ang) < BEVEL_EPSILON_ANG) || (fabsf(ang - (float)M_PI) < BEVEL_EPSILON_ANG);
}
/* Make a new BoundVert of the given kind, insert it at the end of the circular linked
@@ -262,6 +285,7 @@ static void create_mesh_bmvert(BMesh *bm, VMesh *vm, int i, int j, int k, BMVert
NewVert *nv = mesh_vert(vm, i, j, k);
nv->v = BM_vert_create(bm, nv->co, eg, BM_CREATE_NOP);
BM_elem_flag_disable(nv->v, BM_ELEM_TAG);
+ flag_out_vert(bm, nv->v);
}
static void copy_mesh_vert(
@@ -504,9 +528,12 @@ static BMFace *bev_create_ngon(
}
/* not essential for bevels own internal logic,
- * this is done so the operator can select newly created faces */
+ * this is done so the operator can select newly created geometry */
if (f) {
BM_elem_flag_enable(f, BM_ELEM_TAG);
+ BM_ITER_ELEM(bme, &iter, f, BM_EDGES_OF_FACE) {
+ flag_out_edge(bm, bme);
+ }
}
if (mat_nr >= 0)
@@ -3213,6 +3240,7 @@ static void bevel_build_trifan(BevelParams *bp, BMesh *bm, BevVert *bv)
BMFace *f_new;
BLI_assert(v_fan == l_fan->v);
f_new = BM_face_split(bm, f, l_fan, l_fan->next->next, &l_new, NULL, false);
+ flag_out_edge(bm, l_new->e);
if (f_new->len > f->len) {
f = f_new;
@@ -3259,6 +3287,7 @@ static void bevel_build_quadstrip(BevelParams *bp, BMesh *bm, BevVert *bv)
else {
BM_face_split(bm, f, l_a, l_b, &l_new, NULL, false);
f = l_new->f;
+ flag_out_edge(bm, l_new->e);
/* walk around the new face to get the next verts to split */
l_a = l_new->prev;
@@ -3278,7 +3307,7 @@ static void bevel_vert_two_edges(BevelParams *bp, BMesh *bm, BevVert *bv)
{
VMesh *vm = bv->vmesh;
BMVert *v1, *v2;
- BMEdge *e_eg;
+ BMEdge *e_eg, *bme;
Profile *pro;
float co[3];
BoundVert *bndv;
@@ -3320,7 +3349,9 @@ static void bevel_vert_two_edges(BevelParams *bp, BMesh *bm, BevVert *bv)
v1 = mesh_vert(vm, 0, 0, k)->v;
v2 = mesh_vert(vm, 0, 0, k + 1)->v;
BLI_assert(v1 != NULL && v2 != NULL);
- BM_edge_create(bm, v1, v2, e_eg, BM_CREATE_NO_DOUBLE);
+ bme = BM_edge_create(bm, v1, v2, e_eg, BM_CREATE_NO_DOUBLE);
+ if (bme)
+ flag_out_edge(bm, bme);
}
}
}
@@ -3901,7 +3932,7 @@ static BevVert *bevel_vert_construct(BMesh *bm, BevelParams *bp, BMVert *v)
/* Face f has at least one beveled vertex. Rebuild f */
static bool bev_rebuild_polygon(BMesh *bm, BevelParams *bp, BMFace *f)
{
- BMIter liter;
+ BMIter liter, eiter, fiter;
BMLoop *l, *lprev;
BevVert *bv;
BoundVert *v, *vstart, *vend;
@@ -3909,10 +3940,10 @@ static bool bev_rebuild_polygon(BMesh *bm, BevelParams *bp, BMFace *f)
VMesh *vm;
int i, k, n;
bool do_rebuild = false;
- bool go_ccw, corner3special;
+ bool go_ccw, corner3special, keep;
BMVert *bmv;
BMEdge *bme, *bme_new, *bme_prev;
- BMFace *f_new;
+ BMFace *f_new, *f_other;
BMVert **vv = NULL;
BMVert **vv_fix = NULL;
BMEdge **ee = NULL;
@@ -4050,9 +4081,21 @@ static bool bev_rebuild_polygon(BMesh *bm, BevelParams *bp, BMFace *f)
}
}
- /* don't select newly created boundary faces... */
+ /* don't select newly or return created boundary faces... */
if (f_new) {
BM_elem_flag_disable(f_new, BM_ELEM_TAG);
+ /* Also don't want new edges that aren't part of a new bevel face */
+ BM_ITER_ELEM(bme, &eiter, f_new, BM_EDGES_OF_FACE) {
+ keep = false;
+ BM_ITER_ELEM(f_other, &fiter, bme, BM_FACES_OF_EDGE) {
+ if (BM_elem_flag_test(f_other, BM_ELEM_TAG)) {
+ keep = true;
+ break;
+ }
+ }
+ if (!keep)
+ disable_flag_out_edge(bm, bme);
+ }
}
}
@@ -4134,8 +4177,9 @@ static void bevel_reattach_wires(BMesh *bm, BevelParams *bp, BMVert *v)
}
}
} while ((bndv = bndv->next) != bv->vmesh->boundstart);
- if (vclosest)
+ if (vclosest) {
BM_edge_create(bm, vclosest, votherclosest, e, BM_CREATE_NO_DOUBLE);
+ }
}
}
@@ -4487,61 +4531,206 @@ static void set_profile_spacing(BevelParams *bp)
}
/*
- * Calculate and return an offset that is the lesser of the current
+ * Assume we have a situation like:
+ *
+ * a d
+ * \ /
+ * A \ / C
+ * \ th1 th2/
+ * b---------c
+ * B
+ *
+ * where edges are A, B, and C,
+ * following a face around vertices a, b, c, d;
+ * th1 is angle abc and th2 is angle bcd;
+ * and the argument EdgeHalf eb is B, going from b to c.
+ * In general case, edge offset specs for A, B, C have
+ * the form ka*t, kb*t, kc*t where ka, kb, kc are some factors
+ * (may be 0) and t is the current bp->offset.
+ * We want to calculate t at which the clone of B parallel
+ * to it collapses. This can be calculated using trig.
+ * Another case of geometry collision that can happen is
+ * When B slides along A because A is unbeveled.
+ * Then it might collide with a. Similarly for B sliding along C.
+ */
+static float geometry_collide_offset(BevelParams *bp, EdgeHalf *eb)
+{
+ EdgeHalf *ea, *ec, *ebother;
+ BevVert *bvc;
+ BMLoop *lb;
+ BMVert *va, *vb, *vc, *vd;
+ float ka, kb, kc, g, h, t, den, no_collide_offset, th1, th2, sin1, sin2, tan1, tan2, limit;
+
+ limit = no_collide_offset = bp->offset + 1e6;
+ if (bp->offset == 0.0f)
+ return no_collide_offset;
+ kb = eb->offset_l_spec;
+ ea = eb->next; /* note: this is in direction b --> a */
+ ka = ea->offset_r_spec;
+ if (eb->is_rev) {
+ vc = eb->e->v1;
+ vb = eb->e->v2;
+ }
+ else {
+ vb = eb->e->v1;
+ vc = eb->e->v2;
+ }
+ va = ea->is_rev ? ea->e->v1 : ea->e->v2;
+ bvc = NULL;
+ ebother = find_other_end_edge_half(bp, eb, &bvc);
+ if (ebother != NULL) {
+ ec = ebother->prev; /* note: this is in direction c --> d*/
+ vc = bvc->v;
+ kc = ec->offset_l_spec;
+ vd = ec->is_rev ? ec->e->v1 : ec->e->v2;
+ }
+ else {
+ /* No bevvert for w, so C can't be beveled */
+ kc = 0.0f;
+ ec = NULL;
+ /* Find an edge from c that has same face */
+ lb = BM_face_edge_share_loop(eb->fnext, eb->e);
+ if (!lb) {
+ return no_collide_offset;
+ }
+ if (lb->next->v == vc)
+ vd = lb->next->next->v;
+ else if (lb->v == vc)
+ vd = lb->prev->v;
+ else {
+ return no_collide_offset;
+ }
+ }
+ if (ea->e == eb->e || (ec && ec->e == eb->e))
+ return no_collide_offset;
+ ka = ka / bp->offset;
+ kb = kb / bp->offset;
+ kc = kc / bp->offset;
+ th1 = angle_v3v3v3(va->co, vb->co, vc->co);
+ th2 = angle_v3v3v3(vb->co, vc->co, vd->co);
+
+ /* First calculate offset at which edge B collapses, which happens
+ * when advancing clones of A, B, C all meet at a point.
+ * This only happens if at least two of those three edges have non-zero k's */
+ sin1 = sinf(th1);
+ sin2 = sinf(th2);
+ if ((ka > 0.0f) + (kb > 0.0f) + (kc > 0.0f) >= 2) {
+ tan1 = tanf(th1);
+ tan2 = tanf(th2);
+ g = tan1 * tan2;
+ h = sin1 * sin2;
+ den = g * (ka * sin2 + kc * sin1) + kb * h * (tan1 + tan2);
+ if (den != 0.0f) {
+ t = BM_edge_calc_length(eb->e);
+ t *= g * h / den;
+ if (t >= 0.0f)
+ limit = t;
+ }
+ }
+
+ /* Now check edge slide cases */
+ if (kb > 0.0f && ka == 0.0f /*&& bvb->selcount == 1 && bvb->edgecount > 2*/) {
+ t = BM_edge_calc_length(ea->e);
+ t *= sin1 / kb;
+ if (t >= 0.0f && t < limit)
+ limit = t;
+ }
+ if (kb > 0.0f && kc == 0.0f /* && bvc && ec && bvc->selcount == 1 && bvc->edgecount > 2 */) {
+ t = BM_edge_calc_length(ec->e);
+ t *= sin2 / kb;
+ if (t >= 0.0f && t < limit)
+ limit = t;
+ }
+ return limit;
+}
+
+/*
+ * We have an edge A between vertices a and b,
+ * where EdgeHalf ea is the half of A that starts at a.
+ * For vertex-only bevels, the new vertices slide from a at a rate ka*t
+ * and from b at a rate kb*t.
+ * We want to calculate the t at which the two meet.
+ */
+static float vertex_collide_offset(BevelParams *bp, EdgeHalf *ea)
+{
+ float limit, ka, kb, no_collide_offset, la, kab;
+ EdgeHalf *eb;
+
+ limit = no_collide_offset = bp->offset + 1e6;
+ if (bp->offset == 0.0f)
+ return no_collide_offset;
+ ka = ea->offset_l_spec / bp->offset;
+ eb = find_other_end_edge_half(bp, ea, NULL);
+ kb = eb ? eb->offset_l_spec / bp->offset : 0.0f;
+ kab = ka + kb;
+ la = BM_edge_calc_length(ea->e);
+ if (kab <= 0.0f)
+ return no_collide_offset;
+ limit = la / kab;
+ return limit;
+}
+
+/*
+ * Calculate an offset that is the lesser of the current
* bp.offset and the maximum possible offset before geometry
* collisions happen.
- * Currently this is a quick and dirty estimate of the max
- * possible: half the minimum edge length of any vertex involved
- * in a bevel. This is usually conservative.
- * The correct calculation is quite complicated.
- * TODO: implement this correctly.
+ * If the offset changes as a result of this, adjust the
+ * current edge offset specs to reflect this clamping,
+ * and store the new offset in bp.offset.
*/
-static float bevel_limit_offset(BMesh *bm, BevelParams *bp)
+static void bevel_limit_offset(BevelParams *bp)
{
- BMVert *v;
- BMEdge *e;
- BMIter v_iter, e_iter;
- float limited_offset, half_elen;
- bool vbeveled;
+ BevVert *bv;
+ EdgeHalf *eh;
+ GHashIterator giter;
+ float limited_offset, offset_factor, collision_offset;
+ int i;
limited_offset = bp->offset;
- if (bp->offset_type == BEVEL_AMT_PERCENT) {
- if (limited_offset > 50.0f)
- limited_offset = 50.0f;
- return limited_offset;
- }
- BM_ITER_MESH (v, &v_iter, bm, BM_VERTS_OF_MESH) {
- if (BM_elem_flag_test(v, BM_ELEM_TAG)) {
+ GHASH_ITER(giter, bp->vert_hash) {
+ bv = BLI_ghashIterator_getValue(&giter);
+ for (i = 0; i < bv->edgecount; i++) {
+ eh = &bv->edges[i];
if (bp->vertex_only) {
- vbeveled = true;
+ collision_offset = vertex_collide_offset(bp, eh);
+ if (collision_offset < limited_offset)
+ limited_offset = collision_offset;
}
else {
- vbeveled = false;
- BM_ITER_ELEM (e, &e_iter, v, BM_EDGES_OF_VERT) {
- if (BM_elem_flag_test(BM_edge_other_vert(e, v), BM_ELEM_TAG)) {
- vbeveled = true;
- break;
- }
- }
+ collision_offset = geometry_collide_offset(bp, eh);
+ if (collision_offset < limited_offset)
+ limited_offset = collision_offset;
}
- if (vbeveled) {
- BM_ITER_ELEM (e, &e_iter, v, BM_EDGES_OF_VERT) {
- half_elen = 0.5f * BM_edge_calc_length(e);
- if (half_elen < limited_offset)
- limited_offset = half_elen;
- }
+ }
+ }
+
+ if (limited_offset < bp->offset) {
+ /* All current offset specs have some number times bp->offset,
+ * so we can just multiply them all by the reduction factor
+ * of the offset to have the effect of recalculating the specs
+ * with the new limited_offset.
+ */
+ offset_factor = limited_offset / bp->offset;
+ GHASH_ITER(giter, bp->vert_hash) {
+ bv = BLI_ghashIterator_getValue(&giter);
+ for (i = 0; i < bv->edgecount; i++) {
+ eh = &bv->edges[i];
+ eh->offset_l_spec *= offset_factor;
+ eh->offset_r_spec *= offset_factor;
+ eh->offset_l *= offset_factor;
+ eh->offset_r *= offset_factor;
}
}
+ bp->offset = limited_offset;
}
- return limited_offset;
}
/**
* - Currently only bevels BM_ELEM_TAG'd verts and edges.
*
- * - Newly created faces are BM_ELEM_TAG'd too,
- * the caller needs to ensure this is cleared before calling
- * if its going to use this face tag.
+ * - Newly created faces, edges, and verts are BM_ELEM_TAG'd too,
+ * the caller needs to ensure these are cleared before calling
+ * if its going to use this tag.
*
* - If limit_offset is set, adjusts offset down if necessary
* to avoid geometry collisions.
@@ -4560,6 +4749,7 @@ void BM_mesh_bevel(
BMEdge *e;
BevVert *bv;
BevelParams bp = {NULL};
+ GHashIterator giter;
bp.offset = offset;
bp.offset_type = offset_type;
@@ -4583,24 +4773,33 @@ void BM_mesh_bevel(
BLI_memarena_use_calloc(bp.mem_arena);
set_profile_spacing(&bp);
- if (limit_offset)
- bp.offset = bevel_limit_offset(bm, &bp);
-
/* Analyze input vertices, sorting edges and assigning initial new vertex positions */
BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) {
if (BM_elem_flag_test(v, BM_ELEM_TAG)) {
bv = bevel_vert_construct(bm, &bp, v);
- if (bv)
+ if (!limit_offset && bv)
build_boundary(&bp, bv, true);
}
}
+ /* Perhaps clamp offset to avoid geometry colliisions */
+ if (limit_offset) {
+ bevel_limit_offset(&bp);
+
+ /* Assign initial new vertex positions */
+ GHASH_ITER(giter, bp.vert_hash) {
+ bv = BLI_ghashIterator_getValue(&giter);
+ build_boundary(&bp, bv, true);
+ }
+ }
+
/* Perhaps do a pass to try to even out widths */
if (!bp.vertex_only) {
adjust_offsets(&bp);
}
/* Build the meshes around vertices, now that positions are final */
+ /* Note: could use GHASH_ITER over bp.vert_hash when backward compatibility no longer matters */
BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) {
if (BM_elem_flag_test(v, BM_ELEM_TAG)) {
bv = find_bevvert(&bp, v);
@@ -4633,6 +4832,20 @@ void BM_mesh_bevel(
}
}
+ /* When called from operator (as opposed to modifier), bm->use_toolflags
+ * will be set, and we to transfer the oflags to BM_ELEM_TAGs */
+ if (bm->use_toolflags) {
+ BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) {
+ if (BMO_vert_flag_test(bm, v, VERT_OUT))
+ BM_elem_flag_enable(v, BM_ELEM_TAG);
+ }
+ BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) {
+ if (BMO_edge_flag_test(bm, e, EDGE_OUT)) {
+ BM_elem_flag_enable(e, BM_ELEM_TAG);
+ }
+ }
+ }
+
/* primary free */
BLI_ghash_free(bp.vert_hash, NULL, NULL);
BLI_memarena_free(bp.mem_arena);