Fix T56625: Bevel sometimes made zero area UV faces.

This substantially redoes the logic by which bevel chooses, for
the middle segment when there are an odd number of segments,
which face to interpolate in, and which vertices to snap to which
edges before doing that interpolation. It changes the UV layouts
of a number of the regression tests, for the better.
An example, in the reference bug, is a cube with all seams, unwrapped
and then packed with some margin around them, now looks much
better in UV space when there are an odd number of segments.
This commit is contained in:
Howard Trickey 2022-04-22 09:26:34 -04:00
parent a7c65ef4cb
commit 984cd552f0
Notes: blender-bot 2023-02-14 05:22:18 +01:00
Referenced by issue #56625, Bevel modifier UV problem with corners
1 changed files with 438 additions and 163 deletions

View File

@ -675,7 +675,7 @@ static BMFace *bev_create_ngon(BMesh *bm,
const int totv,
BMFace **face_arr,
BMFace *facerep,
BMEdge **edge_arr,
BMEdge **snap_edge_arr,
int mat_nr,
bool do_interp)
{
@ -699,8 +699,8 @@ static BMFace *bev_create_ngon(BMesh *bm,
}
if (interp_f) {
BMEdge *bme = NULL;
if (edge_arr) {
bme = edge_arr[i];
if (snap_edge_arr) {
bme = snap_edge_arr[i];
}
float save_co[3];
if (bme) {
@ -734,44 +734,6 @@ static BMFace *bev_create_ngon(BMesh *bm,
return f;
}
static BMFace *bev_create_quad(BMesh *bm,
BMVert *v1,
BMVert *v2,
BMVert *v3,
BMVert *v4,
BMFace *f1,
BMFace *f2,
BMFace *f3,
BMFace *f4,
int mat_nr)
{
BMVert *varr[4] = {v1, v2, v3, v4};
BMFace *farr[4] = {f1, f2, f3, f4};
return bev_create_ngon(bm, varr, 4, farr, f1, NULL, mat_nr, true);
}
static BMFace *bev_create_quad_ex(BMesh *bm,
BMVert *v1,
BMVert *v2,
BMVert *v3,
BMVert *v4,
BMFace *f1,
BMFace *f2,
BMFace *f3,
BMFace *f4,
BMEdge *e1,
BMEdge *e2,
BMEdge *e3,
BMEdge *e4,
BMFace *frep,
int mat_nr)
{
BMVert *varr[4] = {v1, v2, v3, v4};
BMFace *farr[4] = {f1, f2, f3, f4};
BMEdge *earr[4] = {e1, e2, e3, e4};
return bev_create_ngon(bm, varr, 4, farr, frep, earr, mat_nr, true);
}
/* Is Loop layer layer_index contiguous across shared vertex of l1 and l2? */
static bool contig_ldata_across_loops(BMesh *bm, BMLoop *l1, BMLoop *l2, int layer_index)
{
@ -828,6 +790,25 @@ static bool contig_ldata_across_edge(BMesh *bm, BMEdge *e, BMFace *f1, BMFace *f
return true;
}
/**
* In array face_component of total totface elements, swap values c1 and c2
* whereever they occur.
*/
static void swap_face_components(int *face_component, int totface, int c1, int c2)
{
if (c1 == c2) {
return; /* Nothing to do. */
}
for (int f = 0; f < totface; f++) {
if (face_component[f] == c1) {
face_component[f] = c2;
}
else if (face_component[f] == c2) {
face_component[f] = c1;
}
}
}
/*
* Set up the fields of bp->math_layer_info.
* We always set has_math_layers to the correct value.
@ -836,6 +817,7 @@ static bool contig_ldata_across_edge(BMesh *bm, BMEdge *e, BMFace *f1, BMFace *f
*/
static void math_layer_info_init(BevelParams *bp, BMesh *bm)
{
int f;
bp->math_layer_info.has_math_layers = false;
bp->math_layer_info.face_component = NULL;
for (int i = 0; i < bm->ldata.totlayer; i++) {
@ -860,12 +842,12 @@ static void math_layer_info_init(BevelParams *bp, BMesh *bm)
bool *in_stack = MEM_malloc_arrayN(totface, sizeof(bool), __func__);
/* Set all component ids by DFS from faces with unassigned components. */
for (int f = 0; f < totface; f++) {
for (f = 0; f < totface; f++) {
face_component[f] = -1;
in_stack[f] = false;
}
int current_component = -1;
for (int f = 0; f < totface; f++) {
for (f = 0; f < totface; f++) {
if (face_component[f] == -1 && !in_stack[f]) {
int stack_top = 0;
current_component++;
@ -910,6 +892,44 @@ static void math_layer_info_init(BevelParams *bp, BMesh *bm)
}
MEM_freeN(stack);
MEM_freeN(in_stack);
/* We can usually get more pleasing result if components 0 and 1
* are the topmost and bottommost (in z-coordinate) componenets,
* so adjust component indices to make that so.
*/
if (current_component <= 0) {
return; /* Only one component, so no need to do this. */
}
BMFace *top_face = NULL;
float top_face_z = -1e30f;
int top_face_component = -1;
BMFace *bot_face = NULL;
float bot_face_z = 1e30f;
int bot_face_component = -1;
for (f = 0; f < totface; f++) {
float cent[3];
BMFace *bmf = BM_face_at_index(bm, f);
BM_face_calc_center_bounds(bmf, cent);
float fz = cent[2];
if (fz > top_face_z) {
top_face_z = fz;
top_face = bmf;
top_face_component = face_component[f];
}
if (fz < bot_face_z) {
bot_face_z = fz;
bot_face = bmf;
bot_face_component = face_component[f];
}
}
BLI_assert(top_face != NULL && bot_face != NULL);
swap_face_components(face_component, totface, face_component[0], top_face_component);
if (bot_face_component != top_face_component) {
if (bot_face_component == 0) {
/* It was swapped with old top_face_component. */
bot_face_component = top_face_component;
}
swap_face_components(face_component, totface, face_component[1], bot_face_component);
}
}
/**
@ -3843,8 +3863,8 @@ static VMesh *new_adj_vmesh(MemArena *mem_arena, int count, int seg, BoundVert *
* where ns2 = floor(nseg / 2).
* But these overlap data from previous and next i: there are some forced equivalences.
* Let's call these indices the canonical ones: we will just calculate data for these
* 0 <= j <= ns2, 0 <= k < ns2 (for odd ns2)
* 0 <= j < ns2, 0 <= k <= ns2 (for even ns2)
* 0 <= j <= ns2, 0 <= k <= ns2 (for odd ns)
* 0 <= j < ns2, 0 <= k <= ns2 (for even ns)
* also (j=ns2, k=ns2) at i=0 (for even ns2)
* This function returns the canonical one for any i, j, k in [0,n],[0,ns],[0,ns].
*/
@ -4118,7 +4138,7 @@ static VMesh *cubic_subdiv(BevelParams *bp, VMesh *vm_in)
for (int i = 0; i < n_boundary; i++) {
float co1[3], co2[3], acc[3];
EdgeHalf *e = bndv->elast;
/* Generate tangents. This is hacked together and would ideally be done elsewhere and then only
/* Generate tangents. This is hacked together and would ideally be done elsewere and then only
* used here. */
float tangent[3], tangent2[3], normal[3];
bool convex = true;
@ -4185,7 +4205,7 @@ static VMesh *cubic_subdiv(BevelParams *bp, VMesh *vm_in)
sub_v3_v3v3(co1, mesh_vert(vm_in, i, 0, 0)->co, mesh_vert(vm_in, i, 0, 1)->co);
sub_v3_v3v3(co2, mesh_vert(vm_in, i, 0, 1)->co, mesh_vert(vm_in, i, 0, 2)->co);
cross_v3_v3v3(tangent, co1, co2);
/** The following constant is chosen to best match the old results. */
/** The following constant is choosen to best match the old results. */
normalize_v3_length(tangent, 1.5f / ns_out);
}
/** Copy corner vertex. */
@ -4799,46 +4819,85 @@ static BMEdge *find_closer_edge(float *co, BMEdge *e1, BMEdge *e2)
return e2;
}
/* Snap co to the closest edge of face f. Return the edge in *r_snap_e,
* the coordinates of snap point in r_ snap_co,
* and the distance squared to the snap point as function return */
static float snap_face_dist_squared(float *co, BMFace *f, BMEdge **r_snap_e, float *r_snap_co)
/**
* Find which BoundVerts of \a bv are internal to face \a f.
* That is, when both the face and the point are projected to 2d,
* the point is on the boundary of or inside the projected face.
* There can only be up to three of then, since, including miters,
* that is the maximum number of BoundVerts that can be between two edges.
* Return the number of face-internal vertices found.
*/
static int find_face_internal_boundverts(const BevVert *bv,
const BMFace *f,
BoundVert *(r_internal[3]))
{
BMEdge *beste = NULL;
float beste_d2 = 1e20f;
BMIter iter;
BMEdge *e;
BM_ITER_ELEM (e, &iter, f, BM_EDGES_OF_FACE) {
float closest[3];
closest_to_line_segment_v3(closest, co, e->v1->co, e->v2->co);
float d2 = len_squared_v3v3(closest, co);
if (d2 < beste_d2) {
beste_d2 = d2;
beste = e;
copy_v3_v3(r_snap_co, closest);
}
if (f == NULL) {
return 0;
}
*r_snap_e = beste;
return beste_d2;
int n_internal = 0;
VMesh *vm = bv->vmesh;
BLI_assert(vm != NULL);
BoundVert *v = vm->boundstart;
do {
/* Possible speedup: do the matrix projection done by the following
* once, outside the loop, or even better, cache it if ever done
* in the course of Bevel. */
if (BM_face_point_inside_test(f, v->nv.co)) {
r_internal[n_internal++] = v;
if (n_internal == 3) {
break;
}
}
} while ((v = v->next) != vm->boundstart);
for (int i = n_internal; i < 3; i++) {
r_internal[i] = NULL;
}
return n_internal;
}
/* What would be the area of the polygon around bv if interpolated in face frep?
/**
* Find where the coordinates of the BndVerts in \a bv should snap to in face \a f.
* Face \a f should contain vertex `bv->v`.
* Project the snapped verts to 2d, then return the area of the resulting polygon.
* Usually one BndVert is inside the face, sometimes up to 3 (if there are miters),
* so don't snap those to an edge; all the rest snap to one of the edges of \a bmf
* incident on `bv->v`.
*/
static float interp_poly_area(BevVert *bv, BMFace *frep)
static float projected_boundary_area(BevVert *bv, BMFace *f)
{
BMEdge *e1, *e2;
VMesh *vm = bv->vmesh;
float(*proj_co)[2] = BLI_array_alloca(proj_co, vm->count);
float axis_mat[3][3];
axis_dominant_v3_to_m3(axis_mat, f->no);
get_incident_edges(f, bv->v, &e1, &e2);
BLI_assert(e1 != NULL && e2 != NULL);
BLI_assert(vm != NULL);
float(*uv_co)[3] = BLI_array_alloca(uv_co, vm->count);
BoundVert *v = vm->boundstart;
int n = 0;
int i = 0;
BoundVert *unsnapped[3];
find_face_internal_boundverts(bv, f, unsnapped);
do {
BLI_assert(n < vm->count);
BMEdge *snape;
snap_face_dist_squared(v->nv.v->co, frep, &snape, uv_co[n]);
n++;
float *co = v->nv.v->co;
if (v == unsnapped[0] || v == unsnapped[1] || v == unsnapped[2]) {
mul_v2_m3v3(proj_co[i], axis_mat, co);
}
else {
float snap1[3], snap2[3];
closest_to_line_segment_v3(snap1, co, e1->v1->co, e1->v2->co);
closest_to_line_segment_v3(snap2, co, e2->v1->co, e2->v2->co);
float d1_sq = len_squared_v3v3(snap1, co);
float d2_sq = len_squared_v3v3(snap2, co);
if (d1_sq <= d2_sq) {
mul_v2_m3v3(proj_co[i], axis_mat, snap1);
}
else {
mul_v2_m3v3(proj_co[i], axis_mat, snap2);
}
}
++i;
} while ((v = v->next) != vm->boundstart);
float area = fabsf(area_poly_v3(uv_co, n));
float area = area_poly_v2(proj_co, vm->count);
return area;
}
@ -4851,7 +4910,9 @@ static float interp_poly_area(BevVert *bv, BMFace *frep)
*/
static bool is_bad_uv_poly(BevVert *bv, BMFace *frep)
{
float area = interp_poly_area(bv, frep);
VMesh *vm = bv->vmesh;
BLI_assert(vm != NULL);
float area = projected_boundary_area(bv, frep);
return area < BEVEL_EPSILON_BIG;
}
@ -4876,6 +4937,8 @@ static BMFace *frep_for_center_poly(BevelParams *bp, BevVert *bv)
bool consider_all_faces = bv->selcount == 1;
/* Make an array that can hold maximum possible number of choices. */
BMFace **fchoices = BLI_array_alloca(fchoices, bv->edgecount);
/* For each choice, need to remember the unsnapped BoundVerts. */
for (int i = 0; i < bv->edgecount; i++) {
if (!bv->edges[i].is_bev && !consider_all_faces) {
continue;
@ -4924,9 +4987,11 @@ static void build_center_ngon(BevelParams *bp, BMesh *bm, BevVert *bv, int mat_n
int ns2 = vm->seg / 2;
BMFace *frep;
BMEdge *frep_e1, *frep_e2;
BoundVert *frep_unsnapped[3];
if (bv->any_seam) {
frep = frep_for_center_poly(bp, bv);
get_incident_edges(frep, bv->v, &frep_e1, &frep_e2);
find_face_internal_boundverts(bv, frep, frep_unsnapped);
}
else {
frep = NULL;
@ -4938,8 +5003,13 @@ static void build_center_ngon(BevelParams *bp, BMesh *bm, BevVert *bv, int mat_n
BLI_array_append(vv, mesh_vert(vm, i, ns2, ns2)->v);
if (frep) {
BLI_array_append(vf, frep);
BMEdge *frep_e = find_closer_edge(mesh_vert(vm, i, ns2, ns2)->v->co, frep_e1, frep_e2);
BLI_array_append(ve, v == vm->boundstart ? NULL : frep_e);
if (v == frep_unsnapped[0] || v == frep_unsnapped[1] || v == frep_unsnapped[2]) {
BLI_array_append(ve, NULL);
}
else {
BMEdge *frep_e = find_closer_edge(mesh_vert(vm, i, ns2, ns2)->v->co, frep_e1, frep_e2);
BLI_array_append(ve, frep_e);
}
}
else {
BLI_array_append(vf, boundvert_rep_face(v, NULL));
@ -5242,6 +5312,109 @@ static VMesh *square_out_adj_vmesh(BevelParams *bp, BevVert *bv)
return vm;
}
static BMEdge *snap_edge_for_center_vmesh_vert(int i,
int n_bndv,
BMEdge *eprev,
BMEdge *enext,
BMFace **bndv_rep_faces,
BMFace *center_frep,
const bool *frep_beats_next)
{
int previ = (i + n_bndv - 1) % n_bndv;
int nexti = (i + 1) % n_bndv;
if (frep_beats_next[previ] && bndv_rep_faces[previ] == center_frep) {
return eprev;
}
else if (!frep_beats_next[i] && bndv_rep_faces[nexti] == center_frep) {
return enext;
}
/* If n_bndv > 3 then we won't snap in the boundvert regions
* that are not directly adjacent to the center-winning boundvert.
* This is probably wrong, maybe getting UV positions outside the
* original area, but the alternative may be even worse. */
return NULL;
}
/**
* Fill the r_snap_edges array with the edges to snap to (or NUL, if no snapping)
* for the adj mesh face with lower left corner at (i, ring j, segment k).
* The indices of the four corners are (i,j,k), (i,j,k+1), (i,j+1,k+1), (i,j+1,k).
* The answer will be one of NULL (don't snap), eprev (the edge between
* boundvert i and boundvert i-1), or enext (the edge between boundvert i
* and boundvert i+1).
* When n is odd, the center column (seg ns2) is ambiguous as to whether it
* interpolates in the current boundvert's frep [= interpolation face] or the next one's.
* Similarly, when n is odd, the center row (ring ns2) is ambiguous as to
* whether it interpolates in the current boundvert's frep or the previous one's.
* Parameter frep_beats_next should have an array of size n_bndv of bools
* that say whether the tie should be broken in favor of the next boundvert's
* frep (if true) or the current one's.
* For vertices in the center polygon (when ns is odd), the snapping edge depends
* on where the boundvert is in relation to the boundvert that has the center face's frep,
* so the arguments bndv_rep_faces is an array of size n_bndv give the freps for each i,
* and center_frep is the frep for the center.
*
* Note: this function is for edge bevels only, at the moment.
*/
static void snap_edges_for_vmesh_vert(int i,
int j,
int k,
int ns,
int ns2,
int n_bndv,
BMEdge *eprev,
BMEdge *enext,
BMEdge *enextnext,
BMFace **bndv_rep_faces,
BMFace *center_frep,
const bool *frep_beats_next,
BMEdge *r_snap_edges[4])
{
BLI_assert(0 <= i && i < n_bndv && 0 <= j && j < ns2 && 0 <= k && k <= ns2);
for (int corner = 0; corner < 4; corner++) {
r_snap_edges[corner] = NULL;
if (ns % 2 == 0) {
continue;
}
int previ = (i + n_bndv - 1) % n_bndv;
/* Make jj and kk be the j and k indices for this corner. */
int jj = corner < 2 ? j : j + 1;
int kk = (corner == 0 || corner == 3) ? k : k + 1;
if (jj < ns2 && kk < ns2) {
; /* No snap. */
}
else if (jj < ns2 && kk == ns2) {
/* On the left side of the center strip quads, but not on center poly. */
if (!frep_beats_next[i]) {
r_snap_edges[corner] = enext;
}
}
else if (jj < ns2 && kk == ns2 + 1) {
/* On the right side of the center strip quads, but not on center poly. */
if (frep_beats_next[i]) {
r_snap_edges[corner] = enext;
}
}
else if (jj == ns2 && kk < ns2) {
/* On the top of the top strip quads, but not on center poly. */
if (frep_beats_next[previ]) {
r_snap_edges[corner] = eprev;
}
}
else if (jj == ns2 && kk == ns2) {
/* Center poly vert for boundvert i. */
r_snap_edges[corner] = snap_edge_for_center_vmesh_vert(
i, n_bndv, eprev, enext, bndv_rep_faces, center_frep, frep_beats_next);
}
else if (jj == ns2 && kk == ns2 + 1) {
/* Center poly vert for boundvert i+1. */
r_snap_edges[corner] = snap_edge_for_center_vmesh_vert(
i + 1, n_bndv, enext, enextnext, bndv_rep_faces, center_frep, frep_beats_next);
}
}
}
/**
* Given that the boundary is built and the boundary #BMVert's have been made,
* calculate the positions of the interior mesh points for the M_ADJ pattern,
@ -5295,17 +5468,62 @@ static void bevel_build_rings(BevelParams *bp, BMesh *bm, BevVert *bv, BoundVert
}
}
vmesh_copy_equiv_verts(vm);
/* Make the polygons. */
/* Find and store the interpolation face for each BoundVert. */
BMFace **bndv_rep_faces = BLI_array_alloca(bndv_rep_faces, n_bndv);
BoundVert *bndv = vm->boundstart;
do {
int i = bndv->index;
BMFace *f = boundvert_rep_face(bndv, NULL);
BMFace *f2 = boundvert_rep_face(bndv->next, NULL);
BMFace *fchoices[2] = {f, f2};
BMFace *fc = odd ? choose_rep_face(bp, fchoices, 2) : NULL;
bndv_rep_faces[i] = boundvert_rep_face(bndv, NULL);
} while ((bndv = bndv->next) != vm->boundstart);
EdgeHalf *e = (bp->affect_type == BEVEL_AFFECT_VERTICES) ? bndv->efirst : bndv->ebev;
/* If odd number of segments, need data to break interpolation ties. */
BMVert **center_verts = NULL;
BMEdge **center_edge_snaps = NULL;
BMFace **center_face_interps = NULL;
bool *frep_beats_next = NULL;
BMFace *center_frep = NULL;
if (odd && bp->affect_type == BEVEL_AFFECT_EDGES) {
center_verts = BLI_array_alloca(center_verts, n_bndv);
center_edge_snaps = BLI_array_alloca(center_edge_snaps, n_bndv);
center_face_interps = BLI_array_alloca(center_face_interps, n_bndv);
frep_beats_next = BLI_array_alloca(frep_beats_next, n_bndv);
center_frep = frep_for_center_poly(bp, bv);
for (int i = 0; i < n_bndv; i++) {
center_edge_snaps[i] = NULL;
/* frep_beats_next[i] == true if frep for i is chosen over that for i + 1. */
int inext = (i + 1) % n_bndv;
BMFace *fchoices[2] = {bndv_rep_faces[i], bndv_rep_faces[inext]};
BMFace *fwinner = choose_rep_face(bp, fchoices, 2);
frep_beats_next[i] = fwinner == bndv_rep_faces[i];
}
}
/* Make the polygons. */
bndv = vm->boundstart;
do {
int i = bndv->index;
int inext = bndv->next->index;
BMFace *f = bndv_rep_faces[i];
BMFace *f2 = bndv_rep_faces[inext];
BMFace *fc = NULL;
if (odd && bp->affect_type == BEVEL_AFFECT_EDGES) {
fc = frep_beats_next[i] ? f : f2;
}
EdgeHalf *e, *eprev, *enext;
if (bp->affect_type == BEVEL_AFFECT_VERTICES) {
e = bndv->efirst;
eprev = bndv->prev->efirst;
enext = bndv->next->efirst;
}
else {
e = bndv->ebev;
eprev = bndv->prev->ebev;
enext = bndv->next->ebev;
}
BMEdge *bme = e ? e->e : NULL;
BMEdge *bmeprev = eprev ? eprev->e : NULL;
BMEdge *bmenext = enext ? enext->e : NULL;
/* For odd ns, make polys with lower left corner at (i,j,k) for
* j in [0, ns2-1], k in [0, ns2]. And then the center ngon.
* For even ns,
@ -5315,77 +5533,84 @@ static void bevel_build_rings(BevelParams *bp, BMesh *bm, BevVert *bv, BoundVert
*/
for (int j = 0; j < ns2; j++) {
for (int k = 0; k < ns2 + odd; k++) {
/* We will create a quad with these four corners. */
BMVert *bmv1 = mesh_vert(vm, i, j, k)->v;
BMVert *bmv2 = mesh_vert(vm, i, j, k + 1)->v;
BMVert *bmv3 = mesh_vert(vm, i, j + 1, k + 1)->v;
BMVert *bmv4 = mesh_vert(vm, i, j + 1, k)->v;
BMVert *bmvs[4] = {bmv1, bmv2, bmv3, bmv4};
BLI_assert(bmv1 && bmv2 && bmv3 && bmv4);
BMFace *r_f;
/* For each created quad, the UVs etc. will be interpolated
* in potentially a different face for each corner and may need
* to snap to a particular edge before intorpolating.
* The fr and se arrays will be filled with the interpolation faces
* and snapping edges for the for corners in the order given
* in the bmvs array.
*/
BMFace *fr[4];
BMEdge *se[4] = {NULL, NULL, NULL, NULL};
if (bp->affect_type == BEVEL_AFFECT_VERTICES) {
fr[0] = fr[1] = fr[2] = fr[3] = f2;
if (j < k) {
if (k == ns2 && j == ns2 - 1) {
r_f = bev_create_quad_ex(bm,
bmv1,
bmv2,
bmv3,
bmv4,
f2,
f2,
f2,
f2,
NULL,
NULL,
bndv->next->efirst->e,
bme,
f2,
mat_nr);
}
else {
r_f = bev_create_quad(bm, bmv1, bmv2, bmv3, bmv4, f2, f2, f2, f2, mat_nr);
se[2] = bndv->next->efirst->e;
se[3] = bme;
}
}
else if (j > k) {
r_f = bev_create_quad(bm, bmv1, bmv2, bmv3, bmv4, f2, f2, f2, f2, mat_nr);
}
else { /* j == k */
else if (j == k) {
/* Only one edge attached to v, since vertex only. */
if (e->is_seam) {
r_f = bev_create_quad_ex(
bm, bmv1, bmv2, bmv3, bmv4, f2, f2, f2, f2, bme, NULL, bme, NULL, f2, mat_nr);
}
else {
r_f = bev_create_quad_ex(
bm, bmv1, bmv2, bmv3, bmv4, f2, f2, f2, f, bme, NULL, bme, NULL, f2, mat_nr);
se[0] = se[2] = bme;
if (!e->is_seam) {
fr[3] = f;
}
}
}
else { /* Edge bevel. */
fr[0] = fr[1] = fr[2] = fr[3] = f;
if (odd) {
BMEdge *b1 = (eprev && eprev->is_seam) ? bmeprev : NULL;
BMEdge *b2 = (e && e->is_seam) ? bme : NULL;
BMEdge *b3 = (enext && enext->is_seam) ? bmenext : NULL;
snap_edges_for_vmesh_vert(i,
j,
k,
ns,
ns2,
n_bndv,
b1,
b2,
b3,
bndv_rep_faces,
center_frep,
frep_beats_next,
se);
if (k == ns2) {
if (e && e->is_seam) {
r_f = bev_create_quad_ex(
bm, bmv1, bmv2, bmv3, bmv4, fc, fc, fc, fc, NULL, bme, bme, NULL, fc, mat_nr);
if (!e || e->is_seam) {
fr[0] = fr[1] = fr[2] = fr[3] = fc;
}
else {
r_f = bev_create_quad_ex(
bm, bmv1, bmv2, bmv3, bmv4, f, f2, f2, f, NULL, bme, bme, NULL, fc, mat_nr);
fr[0] = fr[3] = f;
fr[1] = fr[2] = f2;
}
if (j == ns2 - 1) {
/* Use the 4th vertex of these faces as the ones used for the center polygon. */
center_verts[i] = bmvs[3];
center_edge_snaps[i] = se[3];
center_face_interps[i] = bv->any_seam ? center_frep : f;
}
}
else {
r_f = bev_create_quad(bm, bmv1, bmv2, bmv3, bmv4, f, f, f, f, mat_nr);
}
}
else {
BMEdge *bme1 = k == ns2 - 1 ? bme : NULL;
BMEdge *bme3 = NULL;
if (j == ns2 - 1 && bndv->prev->ebev) {
bme3 = bndv->prev->ebev->e;
else { /* Edge bevel, Even number of segments. */
if (k == ns2 - 1) {
se[1] = bme;
}
BMEdge *bme2 = bme1 != NULL ? bme1 : bme3;
r_f = bev_create_quad_ex(
bm, bmv1, bmv2, bmv3, bmv4, f, f, f, f, NULL, bme1, bme2, bme3, f, mat_nr);
if (j == ns2 - 1 && bndv->prev->ebev) {
se[3] = bmeprev;
}
se[2] = se[1] != NULL ? se[1] : se[3];
}
}
BMFace *r_f = bev_create_ngon(bm, bmvs, 4, fr, NULL, se, mat_nr, true);
record_face_kind(bp, r_f, F_VERT);
}
}
@ -5413,7 +5638,18 @@ static void bevel_build_rings(BevelParams *bp, BMesh *bm, BevVert *bv, BoundVert
/* Center ngon. */
if (odd) {
build_center_ngon(bp, bm, bv, mat_nr);
if (bp->affect_type == BEVEL_AFFECT_EDGES) {
BMFace *frep = NULL;
if (bv->any_seam) {
frep = frep_for_center_poly(bp, bv);
}
BMFace *cen_f = bev_create_ngon(
bm, center_verts, n_bndv, center_face_interps, frep, center_edge_snaps, mat_nr, true);
record_face_kind(bp, cen_f, F_VERT);
}
else {
build_center_ngon(bp, bm, bv, mat_nr);
}
}
}
@ -5421,11 +5657,11 @@ static void bevel_build_rings(BevelParams *bp, BMesh *bm, BevVert *bv, BoundVert
* Builds the vertex mesh when the vertex mesh type is set to "cut off" with a face closing
* off each incoming edge's profile.
*
* TODO(Hans): Make cutoff VMesh work with outer miter != sharp. This should be possible but there
* are two problems currently:
* TODO(Hans): Make cutoff VMesh work with outer miter != sharp. This should be possible but
* there are two problems currently:
* - Miter profiles don't have plane_no filled, so down direction is incorrect.
* - Indexing profile points of miters with (i, 0, k) seems to return zero except for the first
* and last profile point.
* - Indexing profile points of miters with (i, 0, k) seems to return zero except for the
* first and last profile point.
* TODO(Hans): Use repface / edge arrays for UV interpolation properly.
*/
static void bevel_build_cutoff(BevelParams *bp, BMesh *bm, BevVert *bv)
@ -5449,7 +5685,8 @@ static void bevel_build_cutoff(BevelParams *bp, BMesh *bm, BevVert *bv)
negate_v3(down_direction);
}
/* Move down from the boundvert by average profile height from the two adjacent profiles. */
/* Move down from the boundvert by average profile height from the two adjacent profiles.
*/
float length = (bndv->profile.height / sqrtf(2.0f) +
bndv->prev->profile.height / sqrtf(2.0f)) /
2;
@ -5502,8 +5739,8 @@ static void bevel_build_cutoff(BevelParams *bp, BMesh *bm, BevVert *bv)
}
/* Build the profile cutoff faces. */
/* Extra one or two for corner vertices and one for last point along profile, or the size of the
* center face array if it's bigger. */
/* Extra one or two for corner vertices and one for last point along profile, or the size of
* the center face array if it's bigger. */
#ifdef DEBUG_CUSTOM_PROFILE_CUTOFF
printf("Building profile cutoff faces.\n");
#endif
@ -5591,9 +5828,11 @@ static BMFace *bevel_build_poly(BevelParams *bp, BMesh *bm, BevVert *bv)
BMFace *repface;
BMEdge *repface_e1, *repface_e2;
BoundVert *unsnapped[3];
if (bv->any_seam) {
repface = frep_for_center_poly(bp, bv);
get_incident_edges(repface, bv->v, &repface_e1, &repface_e2);
find_face_internal_boundverts(bv, repface, unsnapped);
}
else {
repface = NULL;
@ -5607,8 +5846,13 @@ static BMFace *bevel_build_poly(BevelParams *bp, BMesh *bm, BevVert *bv)
BLI_array_append(bmverts, bndv->nv.v);
if (repface) {
BLI_array_append(bmfaces, repface);
BMEdge *frep_e = find_closer_edge(bndv->nv.v->co, repface_e1, repface_e2);
BLI_array_append(bmedges, n > 0 ? frep_e : NULL);
if (bndv == unsnapped[0] || bndv == unsnapped[1] || bndv == unsnapped[2]) {
BLI_array_append(bmedges, NULL);
}
else {
BMEdge *frep_e = find_closer_edge(bndv->nv.v->co, repface_e1, repface_e2);
BLI_array_append(bmedges, frep_e);
}
}
else {
BLI_array_append(bmfaces, boundvert_rep_face(bndv, NULL));
@ -5858,7 +6102,8 @@ static void build_vmesh(BevelParams *bp, BMesh *bm, BevVert *bv)
}
}
/* Make sure the pipe case ADJ mesh is used for both the "Grid Fill" (ADJ) and cutoff options. */
/* Make sure the pipe case ADJ mesh is used for both the "Grid Fill" (ADJ) and cutoff
* options. */
BoundVert *vpipe = NULL;
if (ELEM(vm->count, 3, 4) && bp->seg > 1) {
/* Result is passed to bevel_build_rings to avoid overhead. */
@ -5979,8 +6224,8 @@ static int bevel_edge_order_extend(BMesh *bm, BevVert *bv, int i)
* Assume the first edge is already in bv->edges[0].e and it is tagged. */
#ifdef FASTER_FASTORDER
/* The alternative older code is O(n^2) where n = # of edges incident to bv->v.
* This implementation is O(n * m) where m = average number of faces attached to an edge incident
* to bv->v, which is almost certainly a small constant except in very strange cases.
* This implementation is O(n * m) where m = average number of faces attached to an edge
* incident to bv->v, which is almost certainly a small constant except in very strange cases.
* But this code produces different choices of ordering than the legacy system,
* leading to differences in vertex orders etc. in user models,
* so for now will continue to use the legacy code. */
@ -6070,8 +6315,8 @@ static bool fast_bevel_edge_order(BevVert *bv)
#endif
/* Fill in bv->edges with a good ordering of non-wire edges around bv->v.
* Use only edges where BM_BEVEL_EDGE_TAG is disabled so far (if edge beveling, others are wire).
* first_bme is a good edge to start with. */
* Use only edges where BM_BEVEL_EDGE_TAG is disabled so far (if edge beveling, others are
* wire). first_bme is a good edge to start with. */
static void find_bevel_edge_order(BMesh *bm, BevVert *bv, BMEdge *first_bme)
{
int ntot = bv->edgecount;
@ -6359,10 +6604,10 @@ static BevVert *bevel_vert_construct(BMesh *bm, BevelParams *bp, BMVert *v)
sub_v3_v3v3(edge_dir, bv->v->co, v2->co);
float z = fabsf(2.0f * sinf(angle_v3v3(vert_axis, edge_dir)));
if (z < BEVEL_EPSILON) {
e->offset_l_spec = 0.01f * bv->offset; /* Undefined behavior, so tiny bevel. */
e->offset_l_spec = 0.01f * bp->offset; /* Undefined behavior, so tiny bevel. */
}
else {
e->offset_l_spec = bv->offset / z;
e->offset_l_spec = bp->offset / z;
}
break;
}
@ -6371,10 +6616,10 @@ static BevVert *bevel_vert_construct(BMesh *bm, BevelParams *bp, BMVert *v)
sub_v3_v3v3(edge_dir, bv->v->co, v2->co);
float z = fabsf(cosf(angle_v3v3(vert_axis, edge_dir)));
if (z < BEVEL_EPSILON) {
e->offset_l_spec = 0.01f * bv->offset; /* Undefined behavior, so tiny bevel. */
e->offset_l_spec = 0.01f * bp->offset; /* Undefined behavior, so tiny bevel. */
}
else {
e->offset_l_spec = bv->offset / z;
e->offset_l_spec = bp->offset / z;
}
break;
}
@ -6494,7 +6739,8 @@ static bool bev_rebuild_polygon(BMesh *bm, BevelParams *bp, BMFace *f)
BLI_array_append(ee, bme);
}
while (v != vend) {
/* Check for special case: multi-segment 3rd face opposite a beveled edge with no vmesh. */
/* Check for special case: multi-segment 3rd face opposite a beveled edge with no
* vmesh. */
bool corner3special = (vm->mesh_kind == M_NONE && v->ebev != e && v->ebev != eprev);
if (go_ccw) {
int i = v->index;
@ -6829,13 +7075,20 @@ static void bevel_build_edge_polygons(BMesh *bm, BevelParams *bp, BMEdge *bme)
int odd = nseg % 2;
int mid = nseg / 2;
BMEdge *center_bme = NULL;
BMFace *fchoices[2] = {f1, f2};
BMFace *f_choice = NULL;
int center_adj_k = -1;
if (odd & e1->is_seam) {
f_choice = choose_rep_face(bp, fchoices, 2);
if (nseg > 1) {
center_adj_k = f_choice == f1 ? mid + 2 : mid;
}
}
for (int k = 1; k <= nseg; k++) {
verts[3] = mesh_vert(vm1, i1, 0, k)->v;
verts[2] = mesh_vert(vm2, i2, 0, nseg - k)->v;
BMFace *r_f;
if (odd && k == mid + 1) {
BMFace *fchoices[2] = {f1, f2};
BMFace *f_choice = choose_rep_face(bp, fchoices, 2);
if (e1->is_seam) {
/* Straddles a seam: choose to interpolate in f_choice and snap the loops whose verts
* are in the non-chosen face to bme for interpolation purposes.
@ -6856,6 +7109,25 @@ static void bevel_build_edge_polygons(BMesh *bm, BevelParams *bp, BMEdge *bme)
r_f = bev_create_ngon(bm, verts, 4, faces, f_choice, NULL, mat_nr, true);
}
}
else if (odd && k == center_adj_k && e1->is_seam) {
/* The strip adjacent to the center one, in another UV island.
* Snap the edge near the seam to bme to match what happens in
* the bevel rings.
*/
BMEdge *edges[4];
BMFace *f_interp;
if (k == mid) {
edges[0] = edges[1] = NULL;
edges[2] = edges[3] = bme;
f_interp = f1;
}
else {
edges[0] = edges[1] = bme;
edges[2] = edges[3] = NULL;
f_interp = f2;
}
r_f = bev_create_ngon(bm, verts, 4, NULL, f_interp, edges, mat_nr, true);
}
else if (!odd && k == mid) {
/* Left poly that touches an even center line on right. */
BMEdge *edges[4] = {NULL, NULL, bme, bme};
@ -6929,7 +7201,8 @@ static double find_superellipse_chord_endpoint(double x0, double dtarget, float
const double tol = 1e-13; /* accumulates for many segments so use low value. */
const int maxiter = 10;
/* For gradient between -1 and 1, xnew can only be in [x0 + sqrt(2)/2*dtarget, x0 + dtarget]. */
/* For gradient between -1 and 1, xnew can only be in [x0 + sqrt(2)/2*dtarget, x0 + dtarget].
*/
double xmin = x0 + M_SQRT2 / 2.0 * dtarget;
if (xmin > 1.0) {
xmin = 1.0;
@ -7222,8 +7495,8 @@ static float find_profile_fullness(BevelParams *bp)
* The superellipse used for multi-segment profiles does not have a closed-form way
* to generate evenly spaced points along an arc. We use an expensive search procedure
* to find the parameter values that lead to bp->seg even chords.
* We also want spacing for a number of segments that is a power of 2 >= bp->seg (but at least 4).
* Use doubles because otherwise we cannot come close to float precision for final results.
* We also want spacing for a number of segments that is a power of 2 >= bp->seg (but at least
* 4). Use doubles because otherwise we cannot come close to float precision for final results.
*
* \param pro_spacing: The struct to fill. Changes depending on whether there needs
* to be a separate miter profile.
@ -7426,9 +7699,9 @@ static float geometry_collide_offset(BevelParams *bp, EdgeHalf *eb)
}
/**
* 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.
* 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)
{
@ -7448,9 +7721,10 @@ static float vertex_collide_offset(BevelParams *bp, EdgeHalf *ea)
}
/**
* Calculate an offset that is the lesser of the current bp.offset and the maximum possible offset
* before geometry collisions happen. 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.
* Calculate an offset that is the lesser of the current bp.offset and the maximum possible
* offset before geometry collisions happen. 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 void bevel_limit_offset(BevelParams *bp, BMesh *bm)
{
@ -7577,7 +7851,8 @@ void BM_mesh_bevel(BMesh *bm,
double start_time = PIL_check_seconds_timer();
#endif
/* Disable the miters with the cutoff vertex mesh method, the combination isn't useful anyway. */
/* Disable the miters with the cutoff vertex mesh method, the combination isn't useful
* anyway. */
if (bp.vmesh_method == BEVEL_VMESH_CUTOFF) {
bp.miter_outer = BEVEL_MITER_SHARP;
bp.miter_inner = BEVEL_MITER_SHARP;