diff options
-rw-r--r-- | source/blender/bmesh/tools/bmesh_bevel.c | 597 |
1 files changed, 436 insertions, 161 deletions
diff --git a/source/blender/bmesh/tools/bmesh_bevel.c b/source/blender/bmesh/tools/bmesh_bevel.c index be5521d45ab..ea2cc1c24d6 100644 --- a/source/blender/bmesh/tools/bmesh_bevel.c +++ b/source/blender/bmesh/tools/bmesh_bevel.c @@ -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; + } + 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; } - *r_snap_e = beste; - return beste_d2; + 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; + else { /* Edge bevel, Even number of segments. */ + if (k == ns2 - 1) { + se[1] = bme; + } if (j == ns2 - 1 && bndv->prev->ebev) { - bme3 = bndv->prev->ebev->e; + se[3] = bmeprev; } - 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); + 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; |