Fix T71329: Bevel: Don't drop offsets to 'in plane' faces
offset_meet creates offset lines that can't be directly intersected, so the average of the points on each offset line is 'dropped' onto the faces around the beveled vertex, which can depend on where the loop starts. This fix skips faces with the same normals as the "in plane" faces from build_boundary. Reviewed By: howardt Differential Revision: https://developer.blender.org/D6521
This commit is contained in:
@@ -994,23 +994,31 @@ static bool point_between_edges(float co[3], BMVert *v, BMFace *f, EdgeHalf *e1,
|
|||||||
return (ang11 - ang1co > -BEVEL_EPSILON_ANG);
|
return (ang11 - ang1co > -BEVEL_EPSILON_ANG);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/**
|
||||||
* Calculate the meeting point between the offset edges for e1 and e2, putting answer in meetco.
|
* 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
|
* 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.
|
* the bevel vertex, e1 precedes e2 in CCW order.
|
||||||
* Except: if edges_between is true, there are edges between e1 and e2 in CCW order so they
|
|
||||||
* don't share a common face. We want the meeting point to be on an existing face so it
|
|
||||||
* should be dropped onto one of the intermediate faces, if possible.
|
|
||||||
* Offset edge is on right of both edges, where e1 enters v and e2 leave it.
|
* 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),
|
* 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
|
* but if the offsets are not equal (we allow for because the bevel modifier has edge weights that
|
||||||
* lead to different offsets) then meeting point can be found be intersecting offset lines.
|
* may lead to different offsets) then the meeting point can be found by intersecting offset lines.
|
||||||
* If making the meeting point significantly changes the left or right offset from the user spec,
|
* If making the meeting point significantly changes the left or right offset from the user spec,
|
||||||
* record the change in offset_l (or offset_r); later we can tell that a change has happened
|
* record the change in offset_l (or offset_r); later we can tell that a change has happened
|
||||||
* because the offset will differ from its original value in offset_l_spec (or offset_r_spec).
|
* because the offset will differ from its original value in offset_l_spec (or offset_r_spec).
|
||||||
|
*
|
||||||
|
* \param edges_between If this is true, there are edges between e1 and e2 in CCW order so they
|
||||||
|
* don't share a common face. We want the meeting point to be on an existing face so it
|
||||||
|
* should be dropped onto one of the intermediate faces, if possible.
|
||||||
|
* \param e_in_plane If we need to drop from the calculated offset lines to one of the faces,
|
||||||
|
* we don't want to drop onto the 'in plane' face, so if this is not null skip this edge's faces.
|
||||||
*/
|
*/
|
||||||
static void offset_meet(
|
static void offset_meet(EdgeHalf *e1,
|
||||||
EdgeHalf *e1, EdgeHalf *e2, BMVert *v, BMFace *f, bool edges_between, float meetco[3])
|
EdgeHalf *e2,
|
||||||
|
BMVert *v,
|
||||||
|
BMFace *f,
|
||||||
|
bool edges_between,
|
||||||
|
float meetco[3],
|
||||||
|
const EdgeHalf *e_in_plane)
|
||||||
{
|
{
|
||||||
float dir1[3], dir2[3], dir1n[3], dir2p[3], norm_v[3], norm_v1[3], norm_v2[3];
|
float dir1[3], dir2[3], dir1n[3], dir2p[3], norm_v[3], norm_v1[3], norm_v2[3];
|
||||||
float norm_perp1[3], norm_perp2[3], off1a[3], off1b[3], off2a[3], off2b[3];
|
float norm_perp1[3], norm_perp2[3], off1a[3], off1b[3], off2a[3], off2b[3];
|
||||||
@@ -1018,10 +1026,10 @@ static void offset_meet(
|
|||||||
float ang, d;
|
float ang, d;
|
||||||
BMVert *closer_v;
|
BMVert *closer_v;
|
||||||
EdgeHalf *e, *e1next, *e2prev;
|
EdgeHalf *e, *e1next, *e2prev;
|
||||||
BMFace *ff;
|
BMFace *fnext;
|
||||||
int isect_kind;
|
int isect_kind;
|
||||||
|
|
||||||
/* get direction vectors for two offset lines */
|
/* Get direction vectors for two offset lines. */
|
||||||
sub_v3_v3v3(dir1, v->co, BM_edge_other_vert(e1->e, v)->co);
|
sub_v3_v3v3(dir1, v->co, BM_edge_other_vert(e1->e, v)->co);
|
||||||
sub_v3_v3v3(dir2, BM_edge_other_vert(e2->e, v)->co, v->co);
|
sub_v3_v3v3(dir2, BM_edge_other_vert(e2->e, v)->co, v->co);
|
||||||
|
|
||||||
@@ -1032,20 +1040,20 @@ static void offset_meet(
|
|||||||
sub_v3_v3v3(dir2p, v->co, BM_edge_other_vert(e2prev->e, v)->co);
|
sub_v3_v3v3(dir2p, v->co, BM_edge_other_vert(e2prev->e, v)->co);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
/* shup up 'maybe unused' warnings */
|
/* Shut up 'maybe unused' warnings. */
|
||||||
zero_v3(dir1n);
|
zero_v3(dir1n);
|
||||||
zero_v3(dir2p);
|
zero_v3(dir2p);
|
||||||
}
|
}
|
||||||
|
|
||||||
ang = angle_v3v3(dir1, dir2);
|
ang = angle_v3v3(dir1, dir2);
|
||||||
if (ang < BEVEL_EPSILON_ANG) {
|
if (ang < BEVEL_EPSILON_ANG) {
|
||||||
/* special case: e1 and e2 are parallel; put offset point perp to both, from v.
|
/* Special case: e1 and e2 are parallel; put offset point perp to both, from v.
|
||||||
* need to find a suitable plane.
|
* need to find a suitable plane.
|
||||||
* this code used to just use offset and dir1, but that makes for visible errors
|
* This code used to just use offset and dir1, but that makes for visible errors
|
||||||
* on a circle with > 200 sides, which trips this "nearly perp" code (see T61214).
|
* on a circle with > 200 sides, which trips this "nearly perp" code (see T61214).
|
||||||
* so use the average of the two, and the offset formula for angle bisector.
|
* so use the average of the two, and the offset formula for angle bisector.
|
||||||
* if offsets are different, we're out of luck:
|
* If offsets are different, we're out of luck:
|
||||||
* use the max of the two (so get consistent looking results if the same situation
|
* Use the max of the two (so get consistent looking results if the same situation
|
||||||
* arises elsewhere in the object but with opposite roles for e1 and e2 */
|
* arises elsewhere in the object but with opposite roles for e1 and e2 */
|
||||||
if (f) {
|
if (f) {
|
||||||
copy_v3_v3(norm_v, f->no);
|
copy_v3_v3(norm_v, f->no);
|
||||||
@@ -1063,23 +1071,21 @@ static void offset_meet(
|
|||||||
copy_v3_v3(meetco, off1a);
|
copy_v3_v3(meetco, off1a);
|
||||||
}
|
}
|
||||||
else if (fabsf(ang - (float)M_PI) < BEVEL_EPSILON_ANG) {
|
else if (fabsf(ang - (float)M_PI) < BEVEL_EPSILON_ANG) {
|
||||||
/* special case e1 and e2 are antiparallel, so bevel is into
|
/* Special case: e1 and e2 are antiparallel, so bevel is into a zero-area face.
|
||||||
* a zero-area face. Just make the offset point on the
|
* Just make the offset point on the common line, at offset distance from v. */
|
||||||
* common line, at offset distance from v. */
|
|
||||||
d = max_ff(e1->offset_r, e2->offset_l);
|
d = max_ff(e1->offset_r, e2->offset_l);
|
||||||
slide_dist(e2, v, d, meetco);
|
slide_dist(e2, v, d, meetco);
|
||||||
}
|
}
|
||||||
else {
|
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
|
||||||
* using cross product instead of f->no in case f is non-planar.
|
* in case f is non-planar.
|
||||||
* Except: sometimes locally there can be a small angle
|
* Except: sometimes locally there can be a small angle between dir1 and dir2 that leads
|
||||||
* between dir1 and dir2 that leads to a normal that is actually almost
|
* to a normal that is actually almost perpendicular to the face normal;
|
||||||
* perpendicular to the face normal; in this case it looks wrong to use
|
* in this case it looks wrong to use the local (cross-product) normal,
|
||||||
* the local (cross-product) normal, so use the face normal if the angle
|
* so use the face normal if the angle between dir1 and dir2 is smallish.
|
||||||
* between dir1 and dir2 is smallish.
|
|
||||||
* If e1-v-e2 is a reflex angle (viewed from vertex normal side), need to flip.
|
* If e1-v-e2 is a reflex angle (viewed from vertex normal side), need to flip.
|
||||||
* Use f->no to figure out which side to look at angle from, as even if
|
* Use f->no to figure out which side to look at angle from, as even if f is non-planar,
|
||||||
* f is non-planar, will be more accurate than vertex normal */
|
* will be more accurate than vertex normal. */
|
||||||
if (f && ang < BEVEL_SMALL_ANG) {
|
if (f && ang < BEVEL_SMALL_ANG) {
|
||||||
copy_v3_v3(norm_v1, f->no);
|
copy_v3_v3(norm_v1, f->no);
|
||||||
copy_v3_v3(norm_v2, f->no);
|
copy_v3_v3(norm_v2, f->no);
|
||||||
@@ -1093,7 +1099,7 @@ static void offset_meet(
|
|||||||
copy_v3_v3(norm_v2, norm_v1);
|
copy_v3_v3(norm_v2, norm_v1);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
/* separate faces; get face norms at corners for each separately */
|
/* Separate faces; get face norms at corners for each separately. */
|
||||||
cross_v3_v3v3(norm_v1, dir1n, dir1);
|
cross_v3_v3v3(norm_v1, dir1n, dir1);
|
||||||
normalize_v3(norm_v1);
|
normalize_v3(norm_v1);
|
||||||
f = e1->fnext;
|
f = e1->fnext;
|
||||||
@@ -1108,13 +1114,13 @@ static void offset_meet(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* get vectors perp to each edge, perp to norm_v, and pointing into face */
|
/* Get vectors perp to each edge, perp to norm_v, and pointing into face. */
|
||||||
cross_v3_v3v3(norm_perp1, dir1, norm_v1);
|
cross_v3_v3v3(norm_perp1, dir1, norm_v1);
|
||||||
cross_v3_v3v3(norm_perp2, dir2, norm_v2);
|
cross_v3_v3v3(norm_perp2, dir2, norm_v2);
|
||||||
normalize_v3(norm_perp1);
|
normalize_v3(norm_perp1);
|
||||||
normalize_v3(norm_perp2);
|
normalize_v3(norm_perp2);
|
||||||
|
|
||||||
/* get points that are offset distances from each line, then another point on each line */
|
/* Get points that are offset distances from each line, then another point on each line. */
|
||||||
copy_v3_v3(off1a, v->co);
|
copy_v3_v3(off1a, v->co);
|
||||||
madd_v3_v3fl(off1a, norm_perp1, e1->offset_r);
|
madd_v3_v3fl(off1a, norm_perp1, e1->offset_r);
|
||||||
add_v3_v3v3(off1b, off1a, dir1);
|
add_v3_v3v3(off1b, off1a, dir1);
|
||||||
@@ -1122,18 +1128,17 @@ static void offset_meet(
|
|||||||
madd_v3_v3fl(off2a, norm_perp2, e2->offset_l);
|
madd_v3_v3fl(off2a, norm_perp2, e2->offset_l);
|
||||||
add_v3_v3v3(off2b, off2a, dir2);
|
add_v3_v3v3(off2b, off2a, dir2);
|
||||||
|
|
||||||
/* intersect the lines */
|
/* Intersect the offset lines. */
|
||||||
isect_kind = isect_line_line_v3(off1a, off1b, off2a, off2b, meetco, isect2);
|
isect_kind = isect_line_line_v3(off1a, off1b, off2a, off2b, meetco, isect2);
|
||||||
if (isect_kind == 0) {
|
if (isect_kind == 0) {
|
||||||
/* lines are collinear: we already tested for this, but this used a different epsilon */
|
/* Lines are collinear: we already tested for this, but this used a different epsilon. */
|
||||||
copy_v3_v3(meetco, off1a); /* just to do something */
|
copy_v3_v3(meetco, off1a); /* Just to do something. */
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
/* The lines intersect, but is it at a reasonable place?
|
/* The lines intersect, but is it at a reasonable place?
|
||||||
* One problem to check: if one of the offsets is 0, then don't
|
* One problem to check: if one of the offsets is 0, then don't want an intersection
|
||||||
* want an intersection that is outside that edge itself.
|
* that is outside that edge itself. This can happen if angle between them is > 180 degrees,
|
||||||
* This can happen if angle between them is > 180 degrees,
|
* or if the offset amount is > the edge length. */
|
||||||
* or if the offset amount is > the edge length*/
|
|
||||||
if (e1->offset_r == 0.0f && is_outside_edge(e1, meetco, &closer_v)) {
|
if (e1->offset_r == 0.0f && is_outside_edge(e1, meetco, &closer_v)) {
|
||||||
copy_v3_v3(meetco, closer_v->co);
|
copy_v3_v3(meetco, closer_v->co);
|
||||||
}
|
}
|
||||||
@@ -1141,19 +1146,26 @@ static void offset_meet(
|
|||||||
copy_v3_v3(meetco, closer_v->co);
|
copy_v3_v3(meetco, closer_v->co);
|
||||||
}
|
}
|
||||||
if (edges_between && e1->offset_r > 0.0f && e2->offset_l > 0.0f) {
|
if (edges_between && e1->offset_r > 0.0f && e2->offset_l > 0.0f) {
|
||||||
/* Try to drop meetco to a face between e1 and e2 */
|
/* Try to drop meetco to a face between e1 and e2. */
|
||||||
if (isect_kind == 2) {
|
if (isect_kind == 2) {
|
||||||
/* lines didn't meet in 3d: get average of meetco and isect2 */
|
/* Lines didn't meet in 3d: get average of meetco and isect2. */
|
||||||
mid_v3_v3v3(meetco, meetco, isect2);
|
mid_v3_v3v3(meetco, meetco, isect2);
|
||||||
}
|
}
|
||||||
for (e = e1; e != e2; e = e->next) {
|
for (e = e1; e != e2; e = e->next) {
|
||||||
ff = e->fnext;
|
fnext = e->fnext;
|
||||||
if (!ff) {
|
if (!fnext) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
plane_from_point_normal_v3(plane, v->co, ff->no);
|
plane_from_point_normal_v3(plane, v->co, fnext->no);
|
||||||
closest_to_plane_normalized_v3(dropco, plane, meetco);
|
closest_to_plane_normalized_v3(dropco, plane, meetco);
|
||||||
if (point_between_edges(dropco, v, ff, e, e->next)) {
|
/* Don't drop to the faces next to the in plane edge. */
|
||||||
|
if (e_in_plane) {
|
||||||
|
ang = angle_v3v3(fnext->no, e_in_plane->fnext->no);
|
||||||
|
if ((fabsf(ang) < BEVEL_SMALL_ANG) || (fabsf(ang - (float)M_PI) < BEVEL_SMALL_ANG)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (point_between_edges(dropco, v, fnext, e, e->next)) {
|
||||||
copy_v3_v3(meetco, dropco);
|
copy_v3_v3(meetco, dropco);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -1163,8 +1175,7 @@ static void offset_meet(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Chosen so that 1/sin(BEVEL_GOOD_ANGLE) is about 4,
|
/* Chosen so 1/sin(BEVEL_GOOD_ANGLE) is about 4, giving that expansion factor to bevel width. */
|
||||||
* giving that expansion factor to bevel width. */
|
|
||||||
#define BEVEL_GOOD_ANGLE 0.25f
|
#define BEVEL_GOOD_ANGLE 0.25f
|
||||||
|
|
||||||
/* Calculate the meeting point between e1 and e2 (one of which should have zero offsets),
|
/* Calculate the meeting point between e1 and e2 (one of which should have zero offsets),
|
||||||
@@ -1182,7 +1193,7 @@ static bool offset_meet_edge(
|
|||||||
normalize_v3(dir1);
|
normalize_v3(dir1);
|
||||||
normalize_v3(dir2);
|
normalize_v3(dir2);
|
||||||
|
|
||||||
/* find angle from dir1 to dir2 as viewed from vertex normal side */
|
/* Find angle from dir1 to dir2 as viewed from vertex normal side. */
|
||||||
ang = angle_normalized_v3v3(dir1, dir2);
|
ang = angle_normalized_v3v3(dir1, dir2);
|
||||||
if (fabsf(ang) < BEVEL_GOOD_ANGLE) {
|
if (fabsf(ang) < BEVEL_GOOD_ANGLE) {
|
||||||
if (r_angle) {
|
if (r_angle) {
|
||||||
@@ -1192,7 +1203,7 @@ static bool offset_meet_edge(
|
|||||||
}
|
}
|
||||||
cross_v3_v3v3(fno, dir1, dir2);
|
cross_v3_v3v3(fno, dir1, dir2);
|
||||||
if (dot_v3v3(fno, v->no) < 0.0f) {
|
if (dot_v3v3(fno, v->no) < 0.0f) {
|
||||||
ang = 2.0f * (float)M_PI - ang; /* angle is reflex */
|
ang = 2.0f * (float)M_PI - ang; /* Angle is reflex. */
|
||||||
if (r_angle) {
|
if (r_angle) {
|
||||||
*r_angle = ang;
|
*r_angle = ang;
|
||||||
}
|
}
|
||||||
@@ -1219,7 +1230,7 @@ static bool offset_meet_edge(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Return true if it will look good to put the meeting point where offset_on_edge_between
|
/* Return true if it will look good to put the meeting point where offset_on_edge_between
|
||||||
* would put it. This means that neither side sees a reflex angle */
|
* would put it. This means that neither side sees a reflex angle. */
|
||||||
static bool good_offset_on_edge_between(EdgeHalf *e1, EdgeHalf *e2, EdgeHalf *emid, BMVert *v)
|
static bool good_offset_on_edge_between(EdgeHalf *e1, EdgeHalf *e2, EdgeHalf *emid, BMVert *v)
|
||||||
{
|
{
|
||||||
float ang;
|
float ang;
|
||||||
@@ -1248,7 +1259,7 @@ static bool offset_on_edge_between(
|
|||||||
if (ok1 && ok2) {
|
if (ok1 && ok2) {
|
||||||
mid_v3_v3v3(meetco, meet1, meet2);
|
mid_v3_v3v3(meetco, meet1, meet2);
|
||||||
if (r_sinratio) {
|
if (r_sinratio) {
|
||||||
/* ang1 should not be 0, but be paranoid */
|
/* ang1 should not be 0, but be paranoid. */
|
||||||
*r_sinratio = (ang1 == 0.0f) ? 1.0f : sinf(ang2) / sinf(ang1);
|
*r_sinratio = (ang1 == 0.0f) ? 1.0f : sinf(ang2) / sinf(ang1);
|
||||||
}
|
}
|
||||||
retval = true;
|
retval = true;
|
||||||
@@ -1261,7 +1272,7 @@ static bool offset_on_edge_between(
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
/* Neither offset line met emid.
|
/* Neither offset line met emid.
|
||||||
* This should only happen if all three lines are on top of each other */
|
* This should only happen if all three lines are on top of each other. */
|
||||||
slide_dist(emid, v, e1->offset_r, meetco);
|
slide_dist(emid, v, e1->offset_r, meetco);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1269,8 +1280,7 @@ static bool offset_on_edge_between(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Offset by e->offset in plane with normal plane_no, on left if left==true,
|
/* Offset by e->offset in plane with normal plane_no, on left if left==true,
|
||||||
* else on right. If no is NULL, choose an arbitrary plane different
|
* else on right. If no is NULL, choose an arbitrary plane different from eh's direction. */
|
||||||
* from eh's direction. */
|
|
||||||
static void offset_in_plane(EdgeHalf *e, const float plane_no[3], bool left, float r[3])
|
static void offset_in_plane(EdgeHalf *e, const float plane_no[3], bool left, float r[3])
|
||||||
{
|
{
|
||||||
float dir[3], no[3], fdir[3];
|
float dir[3], no[3], fdir[3];
|
||||||
@@ -1303,7 +1313,7 @@ static void offset_in_plane(EdgeHalf *e, const float plane_no[3], bool left, flo
|
|||||||
madd_v3_v3fl(r, fdir, left ? e->offset_l : e->offset_r);
|
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 */
|
/* Calculate the point on e where line (co_a, co_b) comes closest to and return it in projco. */
|
||||||
static void project_to_edge(BMEdge *e, const float co_a[3], const float co_b[3], float projco[3])
|
static void project_to_edge(BMEdge *e, const float co_a[3], const float co_b[3], float projco[3])
|
||||||
{
|
{
|
||||||
float otherco[3];
|
float otherco[3];
|
||||||
@@ -1317,56 +1327,55 @@ static void project_to_edge(BMEdge *e, const float co_a[3], const float co_b[3],
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* If there is a bndv->ebev edge, find the mid control point if necessary.
|
/* If there is a bndv->ebev edge, find the mid control point if necessary.
|
||||||
* It is the closest point on the beveled edge to the line segment between
|
* It is the closest point on the beveled edge to the line segment between bndv and bndv->next. */
|
||||||
* bndv and bndv->next. */
|
|
||||||
static void set_profile_params(BevelParams *bp, BevVert *bv, BoundVert *bndv)
|
static void set_profile_params(BevelParams *bp, BevVert *bv, BoundVert *bndv)
|
||||||
{
|
{
|
||||||
EdgeHalf *e;
|
EdgeHalf *e;
|
||||||
Profile *pro;
|
Profile *pro;
|
||||||
float co1[3], co2[3], co3[3], d1[3], d2[3];
|
float start[3], end[3], co3[3], d1[3], d2[3];
|
||||||
bool do_linear_interp;
|
bool do_linear_interp;
|
||||||
|
|
||||||
copy_v3_v3(co1, bndv->nv.co);
|
copy_v3_v3(start, bndv->nv.co);
|
||||||
copy_v3_v3(co2, bndv->next->nv.co);
|
copy_v3_v3(end, bndv->next->nv.co);
|
||||||
pro = &bndv->profile;
|
pro = &bndv->profile;
|
||||||
e = bndv->ebev;
|
e = bndv->ebev;
|
||||||
do_linear_interp = true;
|
do_linear_interp = true;
|
||||||
if (e) {
|
if (e) {
|
||||||
do_linear_interp = false;
|
do_linear_interp = false;
|
||||||
pro->super_r = bp->pro_super_r;
|
pro->super_r = bp->pro_super_r;
|
||||||
/* projection direction is direction of the edge */
|
/* projection direction is direction of the edge. */
|
||||||
sub_v3_v3v3(pro->proj_dir, e->e->v1->co, e->e->v2->co);
|
sub_v3_v3v3(pro->proj_dir, e->e->v1->co, e->e->v2->co);
|
||||||
if (e->is_rev) {
|
if (e->is_rev) {
|
||||||
negate_v3(pro->proj_dir);
|
negate_v3(pro->proj_dir);
|
||||||
}
|
}
|
||||||
normalize_v3(pro->proj_dir);
|
normalize_v3(pro->proj_dir);
|
||||||
project_to_edge(e->e, co1, co2, pro->middle);
|
project_to_edge(e->e, start, end, pro->middle);
|
||||||
if (DEBUG_OLD_PROJ_TO_PERP_PLANE) {
|
if (DEBUG_OLD_PROJ_TO_PERP_PLANE) {
|
||||||
/* put arc endpoints on plane with normal proj_dir, containing middle */
|
/* Put arc endpoints on plane with normal proj_dir, containing middle. */
|
||||||
add_v3_v3v3(co3, co1, pro->proj_dir);
|
add_v3_v3v3(co3, start, pro->proj_dir);
|
||||||
if (!isect_line_plane_v3(pro->start, co1, co3, pro->middle, pro->proj_dir)) {
|
if (!isect_line_plane_v3(pro->start, start, co3, pro->middle, pro->proj_dir)) {
|
||||||
/* shouldn't happen */
|
/* Shouldn't happen. */
|
||||||
copy_v3_v3(pro->start, co1);
|
copy_v3_v3(pro->start, start);
|
||||||
}
|
}
|
||||||
add_v3_v3v3(co3, co2, pro->proj_dir);
|
add_v3_v3v3(co3, end, pro->proj_dir);
|
||||||
if (!isect_line_plane_v3(pro->end, co2, co3, pro->middle, pro->proj_dir)) {
|
if (!isect_line_plane_v3(pro->end, end, co3, pro->middle, pro->proj_dir)) {
|
||||||
/* shouldn't happen */
|
/* Shouldn't happen. */
|
||||||
copy_v3_v3(pro->end, co2);
|
copy_v3_v3(pro->end, end);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
copy_v3_v3(pro->start, co1);
|
copy_v3_v3(pro->start, start);
|
||||||
copy_v3_v3(pro->end, co2);
|
copy_v3_v3(pro->end, end);
|
||||||
}
|
}
|
||||||
/* default plane to project onto is the one with triangle co1 - middle - co2 in it */
|
/* Default plane to project onto is the one with triangle start - middle - end in it. */
|
||||||
sub_v3_v3v3(d1, pro->middle, co1);
|
sub_v3_v3v3(d1, pro->middle, start);
|
||||||
sub_v3_v3v3(d2, pro->middle, co2);
|
sub_v3_v3v3(d2, pro->middle, end);
|
||||||
normalize_v3(d1);
|
normalize_v3(d1);
|
||||||
normalize_v3(d2);
|
normalize_v3(d2);
|
||||||
cross_v3_v3v3(pro->plane_no, d1, d2);
|
cross_v3_v3v3(pro->plane_no, d1, d2);
|
||||||
normalize_v3(pro->plane_no);
|
normalize_v3(pro->plane_no);
|
||||||
if (nearly_parallel(d1, d2)) {
|
if (nearly_parallel(d1, d2)) {
|
||||||
/* co1 - middle -co2 are collinear.
|
/* Start - middle - end are collinear.
|
||||||
* Should be case that beveled edge is coplanar with two boundary verts.
|
* Should be case that beveled edge is coplanar with two boundary verts.
|
||||||
* We want to move the profile to that common plane, if possible.
|
* We want to move the profile to that common plane, if possible.
|
||||||
* That makes the multi-segment bevels curve nicely in that plane, as users expect.
|
* That makes the multi-segment bevels curve nicely in that plane, as users expect.
|
||||||
@@ -1380,8 +1389,8 @@ static void set_profile_params(BevelParams *bp, BevVert *bv, BoundVert *bndv)
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (DEBUG_OLD_PROJ_TO_PERP_PLANE) {
|
if (DEBUG_OLD_PROJ_TO_PERP_PLANE) {
|
||||||
copy_v3_v3(pro->start, co1);
|
copy_v3_v3(pro->start, start);
|
||||||
copy_v3_v3(pro->end, co2);
|
copy_v3_v3(pro->end, end);
|
||||||
}
|
}
|
||||||
if (DEBUG_OLD_FLAT_MID) {
|
if (DEBUG_OLD_FLAT_MID) {
|
||||||
copy_v3_v3(pro->middle, bv->v->co);
|
copy_v3_v3(pro->middle, bv->v->co);
|
||||||
@@ -1389,7 +1398,7 @@ static void set_profile_params(BevelParams *bp, BevVert *bv, BoundVert *bndv)
|
|||||||
else {
|
else {
|
||||||
copy_v3_v3(pro->middle, bv->v->co);
|
copy_v3_v3(pro->middle, bv->v->co);
|
||||||
if (e->prev->is_bev && e->next->is_bev && bv->selcount >= 3) {
|
if (e->prev->is_bev && e->next->is_bev && bv->selcount >= 3) {
|
||||||
/* want mid at the meet point of next and prev offset edges */
|
/* Want mid at the meet point of next and prev offset edges. */
|
||||||
float d3[3], d4[3], co4[3], meetco[3], isect2[3];
|
float d3[3], d4[3], co4[3], meetco[3], isect2[3];
|
||||||
int isect_kind;
|
int isect_kind;
|
||||||
|
|
||||||
@@ -1398,34 +1407,34 @@ static void set_profile_params(BevelParams *bp, BevVert *bv, BoundVert *bndv)
|
|||||||
normalize_v3(d3);
|
normalize_v3(d3);
|
||||||
normalize_v3(d4);
|
normalize_v3(d4);
|
||||||
if (nearly_parallel(d3, d4)) {
|
if (nearly_parallel(d3, d4)) {
|
||||||
/* offset lines are collinear - want linear interpolation */
|
/* Offset lines are collinear - want linear interpolation. */
|
||||||
mid_v3_v3v3(pro->middle, co1, co2);
|
mid_v3_v3v3(pro->middle, start, end);
|
||||||
do_linear_interp = true;
|
do_linear_interp = true;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
add_v3_v3v3(co3, co1, d3);
|
add_v3_v3v3(co3, start, d3);
|
||||||
add_v3_v3v3(co4, co2, d4);
|
add_v3_v3v3(co4, end, d4);
|
||||||
isect_kind = isect_line_line_v3(co1, co3, co2, co4, meetco, isect2);
|
isect_kind = isect_line_line_v3(start, co3, end, co4, meetco, isect2);
|
||||||
if (isect_kind != 0) {
|
if (isect_kind != 0) {
|
||||||
copy_v3_v3(pro->middle, meetco);
|
copy_v3_v3(pro->middle, meetco);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
/* offset lines don't intersect - want linear interpolation */
|
/* Offset lines don't intersect - want linear interpolation. */
|
||||||
mid_v3_v3v3(pro->middle, co1, co2);
|
mid_v3_v3v3(pro->middle, start, end);
|
||||||
do_linear_interp = true;
|
do_linear_interp = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
copy_v3_v3(pro->end, co2);
|
copy_v3_v3(pro->end, end);
|
||||||
sub_v3_v3v3(d1, pro->middle, co1);
|
sub_v3_v3v3(d1, pro->middle, start);
|
||||||
normalize_v3(d1);
|
normalize_v3(d1);
|
||||||
sub_v3_v3v3(d2, pro->middle, co2);
|
sub_v3_v3v3(d2, pro->middle, end);
|
||||||
normalize_v3(d2);
|
normalize_v3(d2);
|
||||||
cross_v3_v3v3(pro->plane_no, d1, d2);
|
cross_v3_v3v3(pro->plane_no, d1, d2);
|
||||||
normalize_v3(pro->plane_no);
|
normalize_v3(pro->plane_no);
|
||||||
if (nearly_parallel(d1, d2)) {
|
if (nearly_parallel(d1, d2)) {
|
||||||
/* whole profile is collinear with edge: just interpolate */
|
/* Whole profile is collinear with edge: just interpolate. */
|
||||||
do_linear_interp = true;
|
do_linear_interp = true;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@@ -1434,12 +1443,12 @@ static void set_profile_params(BevelParams *bp, BevVert *bv, BoundVert *bndv)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
copy_v3_v3(pro->plane_co, co1);
|
copy_v3_v3(pro->plane_co, start);
|
||||||
}
|
}
|
||||||
else if (bndv->is_arc_start) {
|
else if (bndv->is_arc_start) {
|
||||||
/* assume pro->middle was alredy set */
|
/* Assume pro->middle was alredy set. */
|
||||||
copy_v3_v3(pro->start, co1);
|
copy_v3_v3(pro->start, start);
|
||||||
copy_v3_v3(pro->end, co2);
|
copy_v3_v3(pro->end, end);
|
||||||
pro->super_r = PRO_CIRCLE_R;
|
pro->super_r = PRO_CIRCLE_R;
|
||||||
zero_v3(pro->plane_co);
|
zero_v3(pro->plane_co);
|
||||||
zero_v3(pro->plane_no);
|
zero_v3(pro->plane_no);
|
||||||
@@ -1448,10 +1457,10 @@ static void set_profile_params(BevelParams *bp, BevVert *bv, BoundVert *bndv)
|
|||||||
}
|
}
|
||||||
if (do_linear_interp) {
|
if (do_linear_interp) {
|
||||||
pro->super_r = PRO_LINE_R;
|
pro->super_r = PRO_LINE_R;
|
||||||
copy_v3_v3(pro->start, co1);
|
copy_v3_v3(pro->start, start);
|
||||||
copy_v3_v3(pro->end, co2);
|
copy_v3_v3(pro->end, end);
|
||||||
mid_v3_v3v3(pro->middle, co1, co2);
|
mid_v3_v3v3(pro->middle, start, end);
|
||||||
/* won't use projection for this line profile */
|
/* Won't use projection for this line profile. */
|
||||||
zero_v3(pro->plane_co);
|
zero_v3(pro->plane_co);
|
||||||
zero_v3(pro->plane_no);
|
zero_v3(pro->plane_no);
|
||||||
zero_v3(pro->proj_dir);
|
zero_v3(pro->proj_dir);
|
||||||
@@ -2295,14 +2304,14 @@ static int count_bound_vert_seams(BevVert *bv)
|
|||||||
return ans;
|
return ans;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Is e between two planes where angle between is 180? */
|
/* Is e between two faces with a 180 degree angle between their normals? */
|
||||||
static bool eh_on_plane(EdgeHalf *e)
|
static bool eh_on_plane(EdgeHalf *e)
|
||||||
{
|
{
|
||||||
float dot;
|
float dot;
|
||||||
|
|
||||||
if (e->fprev && e->fnext) {
|
if (e->fprev && e->fnext) {
|
||||||
dot = dot_v3v3(e->fprev->no, e->fnext->no);
|
dot = dot_v3v3(e->fprev->no, e->fnext->no);
|
||||||
if (fabsf(dot) <= BEVEL_EPSILON_BIG || fabsf(dot - 1.0f) <= BEVEL_EPSILON_BIG) {
|
if (fabsf(dot + 1.0f) <= BEVEL_EPSILON_BIG || fabsf(dot - 1.0f) <= BEVEL_EPSILON_BIG) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2422,7 +2431,7 @@ static void build_boundary_terminal_edge(BevelParams *bp,
|
|||||||
* and join with the beveled edge to make a poly or adj mesh,
|
* and join with the beveled edge to make a poly or adj mesh,
|
||||||
* Because e->prev has offset 0, offset_meet will put co on that edge. */
|
* Because e->prev has offset 0, offset_meet will put co on that edge. */
|
||||||
/* TODO: should do something else if angle between e and e->prev > 180 */
|
/* TODO: should do something else if angle between e and e->prev > 180 */
|
||||||
offset_meet(e->prev, e, bv->v, e->fprev, false, co);
|
offset_meet(e->prev, e, bv->v, e->fprev, false, co, NULL);
|
||||||
if (construct) {
|
if (construct) {
|
||||||
bndv = add_new_bound_vert(mem_arena, vm, co);
|
bndv = add_new_bound_vert(mem_arena, vm, co);
|
||||||
bndv->efirst = e->prev;
|
bndv->efirst = e->prev;
|
||||||
@@ -2434,7 +2443,7 @@ static void build_boundary_terminal_edge(BevelParams *bp,
|
|||||||
adjust_bound_vert(e->leftv, co);
|
adjust_bound_vert(e->leftv, co);
|
||||||
}
|
}
|
||||||
e = e->next;
|
e = e->next;
|
||||||
offset_meet(e->prev, e, bv->v, e->fprev, false, co);
|
offset_meet(e->prev, e, bv->v, e->fprev, false, co, NULL);
|
||||||
if (construct) {
|
if (construct) {
|
||||||
bndv = add_new_bound_vert(mem_arena, vm, co);
|
bndv = add_new_bound_vert(mem_arena, vm, co);
|
||||||
bndv->efirst = e->prev;
|
bndv->efirst = e->prev;
|
||||||
@@ -2592,18 +2601,20 @@ static void adjust_miter_inner_coords(BevelParams *bp, BevVert *bv, EdgeHalf *em
|
|||||||
} while (v != vstart);
|
} while (v != vstart);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Make a circular list of BoundVerts for bv, each of which has the coordinates
|
/**
|
||||||
* of a vertex on the boundary of the beveled vertex bv->v.
|
* Make a circular list of BoundVerts for bv, each of which has the coordinates of a vertex on the
|
||||||
* This may adjust some EdgeHalf widths, and there might have to be
|
* boundary of the beveled vertex bv->v. This may adjust some EdgeHalf widths, and there might have
|
||||||
* a subsequent pass to make the widths as consistent as possible.
|
* to be a subsequent pass to make the widths as consistent as possible.
|
||||||
* The first time through, construct will be true and we are making the BoundVerts
|
* Doesn't make the actual BMVerts.
|
||||||
* and setting up the BoundVert and EdgeHalf pointers appropriately.
|
*
|
||||||
* For a width consistency path, we just recalculate the coordinates of the
|
* For a width consistency pass, we just recalculate the coordinates of the BoundVerts. If the
|
||||||
* BoundVerts. If the other ends have been (re)built already, then we
|
* other ends have been (re)built already, then we copy the offsets from there to match, else we
|
||||||
* copy the offsets from there to match, else we use the ideal (user-specified)
|
* use the ideal (user-specified) widths.
|
||||||
* widths.
|
*
|
||||||
* Also, if construct, decide on the mesh pattern that will be used inside the boundary.
|
* \param construct The first time through, construct will be true and we are making the BoundVerts
|
||||||
* Doesn't make the actual BMVerts */
|
* and setting up the BoundVert and EdgeHalf pointers appropriately. Also, if construct, decide on
|
||||||
|
* the mesh pattern that will be used inside the boundary.
|
||||||
|
*/
|
||||||
static void build_boundary(BevelParams *bp, BevVert *bv, bool construct)
|
static void build_boundary(BevelParams *bp, BevVert *bv, bool construct)
|
||||||
{
|
{
|
||||||
MemArena *mem_arena = bp->mem_arena;
|
MemArena *mem_arena = bp->mem_arena;
|
||||||
@@ -2614,7 +2625,7 @@ static void build_boundary(BevelParams *bp, BevVert *bv, bool construct)
|
|||||||
int in_plane, not_in_plane, miter_outer, miter_inner;
|
int in_plane, not_in_plane, miter_outer, miter_inner;
|
||||||
int ang_kind;
|
int ang_kind;
|
||||||
|
|
||||||
/* Current bevel does nothing if only one edge into a vertex */
|
/* Current bevel does nothing if only one edge into a vertex. */
|
||||||
if (bv->edgecount <= 1) {
|
if (bv->edgecount <= 1) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -2626,21 +2637,21 @@ static void build_boundary(BevelParams *bp, BevVert *bv, bool construct)
|
|||||||
|
|
||||||
vm = bv->vmesh;
|
vm = bv->vmesh;
|
||||||
|
|
||||||
/* Find a beveled edge to be efirst */
|
/* Find a beveled edge to be efirst. */
|
||||||
e = efirst = next_bev(bv, NULL);
|
e = efirst = next_bev(bv, NULL);
|
||||||
BLI_assert(e->is_bev);
|
BLI_assert(e->is_bev);
|
||||||
|
|
||||||
if (bv->selcount == 1) {
|
if (bv->selcount == 1) {
|
||||||
/* Special case: only one beveled edge in */
|
/* Special case: only one beveled edge in. */
|
||||||
build_boundary_terminal_edge(bp, bv, efirst, construct);
|
build_boundary_terminal_edge(bp, bv, efirst, construct);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Special miters outside only for 3 or more beveled edges */
|
/* Special miters outside only for 3 or more beveled edges. */
|
||||||
miter_outer = (bv->selcount >= 3) ? bp->miter_outer : BEVEL_MITER_SHARP;
|
miter_outer = (bv->selcount >= 3) ? bp->miter_outer : BEVEL_MITER_SHARP;
|
||||||
miter_inner = bp->miter_inner;
|
miter_inner = bp->miter_inner;
|
||||||
|
|
||||||
/* keep track of the first beveled edge of an outside miter (there can be at most 1 per bv */
|
/* Keep track of the first beveled edge of an outside miter (there can be at most 1 per bv). */
|
||||||
emiter = NULL;
|
emiter = NULL;
|
||||||
|
|
||||||
/* There is more than one beveled edge.
|
/* There is more than one beveled edge.
|
||||||
@@ -2650,14 +2661,13 @@ static void build_boundary(BevelParams *bp, BevVert *bv, bool construct)
|
|||||||
do {
|
do {
|
||||||
BLI_assert(e->is_bev);
|
BLI_assert(e->is_bev);
|
||||||
eon = NULL;
|
eon = NULL;
|
||||||
/* Make the BoundVert for the right side of e; other side will be made
|
/* Make the BoundVert for the right side of e; the other side will be made when the beveled
|
||||||
* when the beveled edge to the left of e is handled.
|
* edge to the left of e is handled.
|
||||||
* Analyze edges until next beveled edge.
|
* Analyze edges until next beveled edge: They are either "in plane" (preceding and subsequent
|
||||||
* They are either "in plane" (preceding and subsequent faces are coplanar) or not.
|
* faces are coplanar) or not. The "non-in-plane" edges affect the silhouette and we prefer to
|
||||||
* The "non-in-plane" edges affect the silhouette and we prefer to slide along one of those if
|
* slide along one of those if possible. */
|
||||||
* possible. */
|
in_plane = not_in_plane = 0; /* Counts of in-plane / not-in-plane. */
|
||||||
in_plane = not_in_plane = 0; /* Counts of in-plane / not-in-plane */
|
enip = eip = NULL; /* Representatives of each type. */
|
||||||
enip = eip = NULL; /* representatives of each */
|
|
||||||
for (e2 = e->next; !e2->is_bev; e2 = e2->next) {
|
for (e2 = e->next; !e2->is_bev; e2 = e2->next) {
|
||||||
if (eh_on_plane(e2)) {
|
if (eh_on_plane(e2)) {
|
||||||
in_plane++;
|
in_plane++;
|
||||||
@@ -2668,8 +2678,9 @@ static void build_boundary(BevelParams *bp, BevVert *bv, bool construct)
|
|||||||
enip = e2;
|
enip = e2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (in_plane == 0 && not_in_plane == 0) {
|
if (in_plane == 0 && not_in_plane == 0) {
|
||||||
offset_meet(e, e2, bv->v, e->fnext, false, co);
|
offset_meet(e, e2, bv->v, e->fnext, false, co, NULL);
|
||||||
}
|
}
|
||||||
else if (not_in_plane > 0) {
|
else if (not_in_plane > 0) {
|
||||||
if (bp->loop_slide && not_in_plane == 1 && good_offset_on_edge_between(e, e2, enip, bv->v)) {
|
if (bp->loop_slide && not_in_plane == 1 && good_offset_on_edge_between(e, e2, enip, bv->v)) {
|
||||||
@@ -2678,7 +2689,7 @@ static void build_boundary(BevelParams *bp, BevVert *bv, bool construct)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
offset_meet(e, e2, bv->v, NULL, true, co);
|
offset_meet(e, e2, bv->v, NULL, true, co, eip);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@@ -2689,9 +2700,10 @@ static void build_boundary(BevelParams *bp, BevVert *bv, bool construct)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
offset_meet(e, e2, bv->v, e->fnext, true, co);
|
offset_meet(e, e2, bv->v, e->fnext, true, co, eip);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (construct) {
|
if (construct) {
|
||||||
v = add_new_bound_vert(mem_arena, vm, co);
|
v = add_new_bound_vert(mem_arena, vm, co);
|
||||||
v->efirst = e;
|
v->efirst = e;
|
||||||
|
|||||||
Reference in New Issue
Block a user