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.c223
1 files changed, 166 insertions, 57 deletions
diff --git a/source/blender/bmesh/tools/bmesh_bevel.c b/source/blender/bmesh/tools/bmesh_bevel.c
index bb3fe129e0b..5e68e468fb8 100644
--- a/source/blender/bmesh/tools/bmesh_bevel.c
+++ b/source/blender/bmesh/tools/bmesh_bevel.c
@@ -73,7 +73,8 @@ typedef struct EdgeHalf {
struct BoundVert *leftv; /* left boundary vert (looking along edge to end) */
struct BoundVert *rightv; /* right boundary vert, if beveled */
int seg; /* how many segments for the bevel */
- float offset; /* offset for this edge */
+ float offset_l; /* offset for this edge, on left side */
+ float offset_r; /* offset for this edge, on right side */
bool is_bev; /* is this edge beveled? */
bool is_rev; /* is e->v2 the vertex at this end? */
bool is_seam; /* is e a seam for custom loopdata (e.g., UVs)? */
@@ -128,9 +129,11 @@ typedef struct BevelParams {
MemArena *mem_arena; /* use for all allocs while bevel runs, if we need to free we can switch to mempool */
float offset; /* blender units to offset each side of a beveled edge */
+ int offset_type; /* how offset is measured; enum defined in bmesh_operators.h */
int seg; /* number of segments in beveled edge profile */
bool vertex_only; /* bevel vertices only */
bool use_weights; /* bevel amount affected by weights on edges or verts */
+ bool preserve_widths; /* should bevel prefer widths over angles, if forced to choose? */
const struct MDeformVert *dvert; /* vertex group array, maybe set if vertex_only */
int vertex_group; /* vertex group index, maybe set if vertex_only */
} BevelParams;
@@ -206,6 +209,28 @@ static EdgeHalf *find_edge_half(BevVert *bv, BMEdge *bme)
return NULL;
}
+/* find the BevVert corresponding to BMVert bmv */
+static BevVert *find_bevvert(BevelParams *bp, BMVert *bmv)
+{
+ return BLI_ghash_lookup(bp->vert_hash, bmv);
+}
+
+/* Find the EdgeHalf representing the other end of e->e.
+ * That may not have been constructed yet, in which case return NULL. */
+static EdgeHalf *find_other_end_edge_half(BevelParams *bp, EdgeHalf *e)
+{
+ BevVert *bvother;
+ EdgeHalf *eother;
+
+ bvother = find_bevvert(bp, e->is_rev ? e->e->v1 : e->e->v2);
+ if (bvother) {
+ eother = find_edge_half(bvother, e->e);
+ BLI_assert(eother != NULL);
+ return eother;
+ }
+ return NULL;
+}
+
/* Return the next EdgeHalf after from_e that is beveled.
* If from_e is NULL, find the first beveled edge. */
static EdgeHalf *next_bev(BevVert *bv, EdgeHalf *from_e)
@@ -223,12 +248,6 @@ static EdgeHalf *next_bev(BevVert *bv, EdgeHalf *from_e)
return NULL;
}
-/* find the BevVert corresponding to BMVert bmv */
-static BevVert *find_bevvert(BevelParams *bp, BMVert *bmv)
-{
- return BLI_ghash_lookup(bp->vert_hash, bmv);
-}
-
/* Return a good representative face (for materials, etc.) for faces
* created around/near BoundVert v */
static BMFace *boundvert_rep_face(BoundVert *v)
@@ -449,14 +468,12 @@ static void slide_dist(EdgeHalf *e, BMVert *v, float d, float slideco[3])
* Calculate the meeting point between the offset edges for e1 and e2, putting answer in meetco.
* e1 and e2 share vertex v and face f (may be NULL) and viewed from the normal side of
* the bevel vertex, e1 precedes e2 in CCW order.
- * If on_right is true, offset edge is on right of both edges, where e1 enters v and
- * e2 leave it. If on_right is false, then the offset edge is on the left.
+ * Offset edge is on right of both edges, where e1 enters v and e2 leave it.
* When offsets are equal, the new point is on the edge bisector, with length offset/sin(angle/2),
* but if the offsets are not equal (allowing for this, as bevel modifier has edge weights that may
* lead to different offsets) then meeting point can be found be intersecting offset lines.
*/
-static void offset_meet(EdgeHalf *e1, EdgeHalf *e2, BMVert *v, BMFace *f,
- int on_right, float meetco[3])
+static void offset_meet(EdgeHalf *e1, EdgeHalf *e2, BMVert *v, BMFace *f, float meetco[3])
{
float dir1[3], dir2[3], norm_v[3], norm_perp1[3], norm_perp2[3],
off1a[3], off1b[3], off2a[3], off2b[3], isect2[3], ang;
@@ -469,7 +486,7 @@ static void offset_meet(EdgeHalf *e1, EdgeHalf *e2, BMVert *v, BMFace *f,
if (ang < 100.0f * BEVEL_EPSILON) {
/* special case: e1 and e2 are parallel; put offset point perp to both, from v.
* need to find a suitable plane.
- * if offsets are different, we're out of luck: just use e1->offset */
+ * if offsets are different, we're out of luck: just use e1->offset_r */
if (f)
copy_v3_v3(norm_v, f->no);
else
@@ -477,26 +494,25 @@ static void offset_meet(EdgeHalf *e1, EdgeHalf *e2, BMVert *v, BMFace *f,
cross_v3_v3v3(norm_perp1, dir1, norm_v);
normalize_v3(norm_perp1);
copy_v3_v3(off1a, v->co);
- madd_v3_v3fl(off1a, norm_perp1, e1->offset);
+ madd_v3_v3fl(off1a, norm_perp1, e1->offset_r);
copy_v3_v3(meetco, off1a);
}
else if (fabsf(ang - (float)M_PI) < 100.0f * BEVEL_EPSILON) {
/* special case e1 and e2 are antiparallel, so bevel is into
* a zero-area face. Just make the offset point on the
* common line, at offset distance from v. */
- slide_dist(e2, v, e2->offset, meetco);
+ slide_dist(e2, v, e2->offset_l, meetco);
}
else {
- /* get normal to plane where meet point should be */
+ /* Get normal to plane where meet point should be,
+ * using cross product instead of f->no in case f is non-planar.
+ * If e1-v-e2 is a reflex angle (viewed from vertex normal side), need to flip*/
cross_v3_v3v3(norm_v, dir2, dir1);
normalize_v3(norm_v);
- if (!on_right)
+ if (dot_v3v3(norm_v, v->no) < 0.0f)
negate_v3(norm_v);
/* get vectors perp to each edge, perp to norm_v, and pointing into face */
- if (f) {
- copy_v3_v3(norm_v, f->no);
- }
cross_v3_v3v3(norm_perp1, dir1, norm_v);
cross_v3_v3v3(norm_perp2, dir2, norm_v);
normalize_v3(norm_perp1);
@@ -504,10 +520,10 @@ static void offset_meet(EdgeHalf *e1, EdgeHalf *e2, BMVert *v, BMFace *f,
/* get points that are offset distances from each line, then another point on each line */
copy_v3_v3(off1a, v->co);
- madd_v3_v3fl(off1a, norm_perp1, e1->offset);
+ madd_v3_v3fl(off1a, norm_perp1, e1->offset_r);
add_v3_v3v3(off1b, off1a, dir1);
copy_v3_v3(off2a, v->co);
- madd_v3_v3fl(off2a, norm_perp2, e2->offset);
+ madd_v3_v3fl(off2a, norm_perp2, e2->offset_l);
add_v3_v3v3(off2b, off2a, dir2);
/* intersect the lines; by construction they should be on the same plane and not parallel */
@@ -520,19 +536,40 @@ static void offset_meet(EdgeHalf *e1, EdgeHalf *e2, BMVert *v, BMFace *f,
}
}
+/* Like offset_in_two planes, but for the case where we prefer to solve the problem
+ * of not meeting at the same point by choosing to change the bevel offset on one
+ * of the appropriate side of either e1 or e2, in order that the lines can meet on emid. */
+static void offset_on_edge_between(BevelParams *bp, EdgeHalf *e1, EdgeHalf *e2, EdgeHalf *emid,
+ BMVert *v, float meetco[3])
+{
+ BLI_assert(e1->is_bev && e2->is_bev && !emid->is_bev);
+
+ /* If have to change offset of e1 or e2, which one?
+ * Prefer the one whose other end hasn't been constructed yet.
+ * Following will choose to change e2 if both have already been constructed. */
+ if (find_other_end_edge_half(bp, e1)) {
+ offset_meet(e1, emid, v, e1->fnext, meetco);
+ /* now e2's left offset is probably different */
+ e2->offset_l = dist_to_line_v3(meetco, v->co, BM_edge_other_vert(e2->e, v)->co);
+ }
+ else {
+ offset_meet(emid, e2, v, emid->fnext, meetco);
+ /* now e1's right offset is probably different */
+ e1->offset_r = dist_to_line_v3(meetco, v->co, BM_edge_other_vert(e1->e, v)->co);
+ }
+}
+
/* Like offset_meet, but with a mid edge between them that is used
* to calculate the planes in which to run the offset lines.
- * They may not meet exactly: the offsets for the edges may be different
- * or both the planes and the lines may be angled so that they can't meet.
+ * They may not meet exactly: the lines may be angled so that they can't meet,
+ * probably because one or both faces is non-planar.
* In that case, pick a close point on emid, which should be the dividing
- * edge between the two planes.
- * TODO: should have a global 'offset consistency' prepass to adjust offset
- * widths so that all edges have the same offset at both ends. */
-static void offset_in_two_planes(EdgeHalf *e1, EdgeHalf *e2, EdgeHalf *emid,
+ * edge between the two planes. */
+static void offset_in_two_planes(BevelParams *bp, EdgeHalf *e1, EdgeHalf *e2, EdgeHalf *emid,
BMVert *v, float meetco[3])
{
float dir1[3], dir2[3], dirmid[3], norm_perp1[3], norm_perp2[3],
- off1a[3], off1b[3], off2a[3], off2b[3], isect2[3], co[3],
+ off1a[3], off1b[3], off2a[3], off2b[3], isect2[3],
f1no[3], f2no[3], ang;
int iret;
@@ -545,6 +582,14 @@ static void offset_in_two_planes(EdgeHalf *e1, EdgeHalf *e2, EdgeHalf *emid,
/* calculate face normals at corner in case faces are nonplanar */
cross_v3_v3v3(f1no, dirmid, dir1);
cross_v3_v3v3(f2no, dirmid, dir2);
+
+ /* if e1-v-emid or emid-v-e2 are reflex angles, need to flip corner normals */
+ if (dot_v3v3(f1no, v->no) < 0.0f)
+ negate_v3(f1no);
+ if (dot_v3v3(f2no, v->no) < 0.0f)
+ negate_v3(f2no);
+
+ /* get vectors perpendicular to e1 and e2, pointing into the proper faces */
cross_v3_v3v3(norm_perp1, dir1, f1no);
normalize_v3(norm_perp1);
cross_v3_v3v3(norm_perp2, dir2, f2no);
@@ -552,19 +597,19 @@ static void offset_in_two_planes(EdgeHalf *e1, EdgeHalf *e2, EdgeHalf *emid,
/* get points that are offset distances from each line, then another point on each line */
copy_v3_v3(off1a, v->co);
- madd_v3_v3fl(off1a, norm_perp1, e1->offset);
+ madd_v3_v3fl(off1a, norm_perp1, e1->offset_r);
sub_v3_v3v3(off1b, off1a, dir1);
copy_v3_v3(off2a, v->co);
- madd_v3_v3fl(off2a, norm_perp2, e2->offset);
+ madd_v3_v3fl(off2a, norm_perp2, e2->offset_l);
add_v3_v3v3(off2b, off2a, dir2);
ang = angle_v3v3(dir1, dir2);
if (ang < 100.0f * BEVEL_EPSILON) {
- /* lines are parallel; off1a is a good meet point */
- copy_v3_v3(meetco, off1a);
+ /* lines are parallel; put intersection on emid */
+ offset_on_edge_between(bp, e1, e2, emid, v, meetco);
}
else if (fabsf(ang - (float)M_PI) < 100.0f * BEVEL_EPSILON) {
- slide_dist(e2, v, e2->offset, meetco);
+ slide_dist(e2, v, e2->offset_l, meetco);
}
else {
iret = isect_line_line_v3(off1a, off1b, off2a, off2b, meetco, isect2);
@@ -573,11 +618,10 @@ static void offset_in_two_planes(EdgeHalf *e1, EdgeHalf *e2, EdgeHalf *emid,
copy_v3_v3(meetco, off1a);
}
else if (iret == 2) {
- /* lines are not coplanar; meetco and isect2 are nearest to first and second lines */
+ /* lines are not coplanar and don't meet; meetco and isect2 are nearest to first and second lines */
if (len_v3v3(meetco, isect2) > 100.0f * BEVEL_EPSILON) {
- /* offset lines don't meet: project average onto emid; this is not ideal (see TODO above) */
- mid_v3_v3v3(co, meetco, isect2);
- closest_to_line_v3(meetco, co, v->co, BM_edge_other_vert(emid->e, v)->co);
+ /* offset lines don't meet so can't preserve widths; fallback on sliding along edge between */
+ offset_on_edge_between(bp, e1, e2, emid, v, meetco);
}
}
/* else iret == 1 and the lines are coplanar so meetco has the intersection */
@@ -612,7 +656,7 @@ static void offset_in_plane(EdgeHalf *e, const float plane_no[3], int left, floa
cross_v3_v3v3(fdir, no, dir);
normalize_v3(fdir);
copy_v3_v3(r, v->co);
- madd_v3_v3fl(r, fdir, e->offset);
+ madd_v3_v3fl(r, fdir, left? e->offset_l : e->offset_r);
}
/* Calculate the point on e where line (co_a, co_b) comes closest to and return it in projco */
@@ -818,7 +862,7 @@ static void build_boundary(BevelParams *bp, BevVert *bv)
v->efirst = v->elast = e;
e->rightv = v;
/* make artifical extra point along unbeveled edge, and form triangle */
- slide_dist(e->next, bv->v, e->offset, co);
+ slide_dist(e->next, bv->v, e->offset_l, co);
v = add_new_bound_vert(mem_arena, vm, co);
v->efirst = v->elast = e->next;
e->next->leftv = e->next->rightv = v;
@@ -828,14 +872,14 @@ static void build_boundary(BevelParams *bp, BevVert *bv)
return;
}
- lastd = bp->vertex_only ? bv->offset : e->offset;
+ lastd = bp->vertex_only ? bv->offset : e->offset_l;
vm->boundstart = NULL;
do {
if (e->is_bev) {
/* handle only left side of beveled edge e here: next iteration should do right side */
if (e->prev->is_bev) {
BLI_assert(e->prev != e); /* see: wire edge special case */
- offset_meet(e->prev, e, bv->v, e->fprev, TRUE, co);
+ offset_meet(e->prev, e, bv->v, e->fprev, co);
v = add_new_bound_vert(mem_arena, vm, co);
v->efirst = e->prev;
v->elast = v->ebev = e;
@@ -847,7 +891,10 @@ static void build_boundary(BevelParams *bp, BevVert *bv)
if (e->prev->prev->is_bev) {
BLI_assert(e->prev->prev != e); /* see: edgecount 2, selcount 1 case */
/* find meet point between e->prev->prev and e and attach e->prev there */
- offset_in_two_planes(e->prev->prev, e, e->prev, bv->v, co);
+ if (bp->preserve_widths)
+ offset_in_two_planes(bp, e->prev->prev, e, e->prev, bv->v, co);
+ else
+ offset_on_edge_between(bp, e->prev->prev, e, e->prev, bv->v, co);
v = add_new_bound_vert(mem_arena, vm, co);
v->efirst = e->prev->prev;
v->elast = v->ebev = e;
@@ -857,7 +904,7 @@ static void build_boundary(BevelParams *bp, BevVert *bv)
}
else {
/* neither e->prev nor e->prev->prev are beveled: make on-edge on e->prev */
- offset_meet(e->prev, e, bv->v, e->fprev, TRUE, co);
+ offset_meet(e->prev, e, bv->v, e->fprev, co);
v = add_new_bound_vert(mem_arena, vm, co);
v->efirst = e->prev;
v->elast = v->ebev = e;
@@ -875,7 +922,7 @@ static void build_boundary(BevelParams *bp, BevVert *bv)
}
else if (e->prev->is_bev) {
/* on-edge meet between e->prev and e */
- offset_meet(e->prev, e, bv->v, e->fprev, TRUE, co);
+ offset_meet(e->prev, e, bv->v, e->fprev, co);
v = add_new_bound_vert(mem_arena, vm, co);
v->efirst = e->prev;
v->elast = e;
@@ -1921,6 +1968,19 @@ static void build_vmesh(BevelParams *bp, BMesh *bm, BevVert *bv)
}
}
+/* Return the angle between the two faces adjacent to e.
+ * If there are not two, return 0. */
+static float edge_face_angle(EdgeHalf *e)
+{
+ if (e->fprev && e->fnext) {
+ /* angle between faces is supplement of angle between face normals */
+ return (float)M_PI - angle_normalized_v3v3(e->fprev->no, e->fnext->no);
+ }
+ else {
+ return 0.0f;
+ }
+}
+
/* take care, this flag isn't cleared before use, it just so happens that its not set */
#define BM_BEVEL_EDGE_TAG_ENABLE(bme) BM_ELEM_API_FLAG_ENABLE( (bme), _FLAG_OVERLAP)
#define BM_BEVEL_EDGE_TAG_DISABLE(bme) BM_ELEM_API_FLAG_DISABLE( (bme), _FLAG_OVERLAP)
@@ -1936,8 +1996,8 @@ static void bevel_vert_construct(BMesh *bm, BevelParams *bp, BMVert *v)
BMEdge *bme2, *unflagged_bme, *first_bme;
BMFace *f;
BMIter iter, iter2;
- EdgeHalf *e;
- float weight;
+ EdgeHalf *e, *eother;
+ float weight, z;
int i, found_shared_face, ccw_test_sum;
int nsel = 0;
int ntot = 0;
@@ -2053,16 +2113,6 @@ static void bevel_vert_construct(BMesh *bm, BevelParams *bp, BMVert *v)
e->seg = 0;
}
e->is_rev = (bme->v2 == v);
- if (e->is_bev) {
- e->offset = bp->offset;
- if (bp->use_weights) {
- weight = BM_elem_float_data_get(&bm->edata, bme, CD_BWEIGHT);
- e->offset *= weight;
- }
- }
- else {
- e->offset = 0.0f;
- }
}
/* find wrap-around shared face */
BM_ITER_ELEM (f, &iter2, bme, BM_FACES_OF_EDGE) {
@@ -2098,6 +2148,63 @@ static void bevel_vert_construct(BMesh *bm, BevelParams *bp, BMVert *v)
for (i = 0, e = bv->edges; i < ntot; i++, e++) {
e->next = &bv->edges[(i + 1) % ntot];
e->prev = &bv->edges[(i + ntot - 1) % ntot];
+
+ /* set offsets */
+ if (e->is_bev) {
+ /* Convert distance as specified by user into offsets along
+ * faces on left side and right side of this edgehalf.
+ * Except for percent method, offset will be same on each side.
+ *
+ * First check to see if other end has had construction made,
+ * because offset may have been forced to another number
+ * (but for percent method all 4 widths can be different). */
+
+ eother = find_other_end_edge_half(bp, e);
+ if (eother && bp->offset_type != BEVEL_AMT_PERCENT) {
+ e->offset_l = eother->offset_r;
+ e->offset_r = eother->offset_l;
+ }
+ else {
+ switch (bp->offset_type) {
+ case BEVEL_AMT_OFFSET:
+ e->offset_l = bp->offset;
+ break;
+ case BEVEL_AMT_WIDTH:
+ z = fabsf(2.0f * sinf(edge_face_angle(e) / 2.0f));
+ if (z < BEVEL_EPSILON)
+ e->offset_l = 0.01f * bp->offset; /* undefined behavior, so tiny bevel */
+ else
+ e->offset_l = bp->offset / z;
+ break;
+ case BEVEL_AMT_DEPTH:
+ z = fabsf(cosf(edge_face_angle(e) / 2.0f));
+ if (z < BEVEL_EPSILON)
+ e->offset_l = 0.01f * bp->offset; /* undefined behavior, so tiny bevel */
+ else
+ e->offset_l = bp->offset / z;
+ break;
+ case BEVEL_AMT_PERCENT:
+ e->offset_l = BM_edge_calc_length(e->prev->e) * bp->offset / 100.0f;
+ e->offset_r = BM_edge_calc_length(e->next->e) * bp->offset / 100.0f;
+ break;
+ default:
+ BLI_assert(!"bad bevel offset kind");
+ e->offset_l = bp->offset;
+ break;
+ }
+ if (bp->offset_type != BEVEL_AMT_PERCENT)
+ e->offset_r = e->offset_l;
+ if (bp->use_weights) {
+ weight = BM_elem_float_data_get(&bm->edata, e->e, CD_BWEIGHT);
+ e->offset_l *= weight;
+ e->offset_r *= weight;
+ }
+ }
+ }
+ else {
+ e->offset_l = e->offset_r = 0.0f;
+ }
+
BM_BEVEL_EDGE_TAG_DISABLE(e->e);
if (e->fprev && e->fnext)
e->is_seam = !contig_ldata_across_edge(bm, e->e, e->fprev, e->fnext);
@@ -2370,7 +2477,7 @@ static float bevel_limit_offset(BMesh *bm, BevelParams *bp)
*
* \warning all tagged edges _must_ be manifold.
*/
-void BM_mesh_bevel(BMesh *bm, const float offset, const float segments,
+void BM_mesh_bevel(BMesh *bm, const float offset, const int offset_type, const float segments,
const bool vertex_only, const bool use_weights, const bool limit_offset,
const struct MDeformVert *dvert, const int vertex_group)
{
@@ -2380,9 +2487,11 @@ void BM_mesh_bevel(BMesh *bm, const float offset, const float segments,
BevelParams bp = {NULL};
bp.offset = offset;
+ bp.offset_type = offset_type;
bp.seg = segments;
bp.vertex_only = vertex_only;
bp.use_weights = use_weights;
+ bp.preserve_widths = false;
bp.dvert = dvert;
bp.vertex_group = vertex_group;