Fix T44049, edge bevel with sometimes breaks UVs.

Fairly large changes to bevel code to do a better job
of keeping UVs from crossing islands, etc.
Updated http://wiki.blender.org/index.php/Dev:2.5/Source/Modeling/Bevel
to explain algorithm used for maintaining UVs.
Updated the bevel_regression.blend tests in lib tests.
This commit is contained in:
Howard Trickey 2015-08-12 10:18:58 -04:00
parent 4eefba091d
commit 39ce0a9916
Notes: blender-bot 2023-02-14 10:21:15 +01:00
Referenced by issue #44049, Edge Bevel with more than one segment breaks UVs
1 changed files with 387 additions and 115 deletions

View File

@ -53,6 +53,7 @@
#define BEVEL_EPSILON 1e-6f
#define BEVEL_EPSILON_SQ 1e-12f
#define BEVEL_EPSILON_BIG 1e-4f
#define BEVEL_EPSILON_BIG_SQ 1e-8f
/* happens far too often, uncomment for development */
// #define BEVEL_ASSERT_PROJECT
@ -345,33 +346,64 @@ static EdgeHalf *next_bev(BevVert *bv, EdgeHalf *from_e)
}
/* Return a good representative face (for materials, etc.) for faces
* created around/near BoundVert v */
static BMFace *boundvert_rep_face(BoundVert *v)
* created around/near BoundVert v.
* Sometimes care about a second choice, if there is one.
* If r_fother paramenter is non-NULL and there is another, different,
* possible frep, return the other one in that parameter. */
static BMFace *boundvert_rep_face(BoundVert *v, BMFace **r_fother)
{
BLI_assert(v->efirst != NULL && v->elast != NULL);
if (v->efirst->fnext == v->elast->fprev)
return v->efirst->fnext;
else if (v->efirst->fnext)
return v->efirst->fnext;
else
return v->elast->fprev;
BMFace *frep, *frep2;
frep2 = NULL;
if (v->ebev) {
frep = v->ebev->fprev;
if (v->efirst->fprev != frep)
frep2 = v->efirst->fprev;
}
else {
frep = v->efirst->fprev;
if (frep) {
if (v->elast->fnext != frep)
frep2 = v->elast->fnext;
else if (v->efirst->fnext != frep)
frep2 = v->efirst->fnext;
else if (v->elast->fprev != frep)
frep2 = v->efirst->fprev;
}
else if (v->efirst->fnext) {
frep = v->efirst->fnext;
if (v->elast->fnext != frep)
frep2 = v->elast->fnext;
}
else if (v->elast->fprev) {
frep = v->elast->fprev;
}
}
if (r_fother)
*r_fother = frep2;
return frep;
}
/**
* Make ngon from verts alone.
* Make sure to properly copy face attributes and do custom data interpolation from
* corresponding elements of face_arr, if that is non-NULL, else from facerep.
* If edge_arr is non-NULL, then for interpolation purposes only, the corresponding
* elements of vert_arr are snapped to any non-NULL edges in that array.
* If mat_nr >= 0 then the material of the face is set to that.
*
* \note ALL face creation goes through this function, this is important to keep!
*/
static BMFace *bev_create_ngon(
BMesh *bm, BMVert **vert_arr, const int totv,
BMFace **face_arr, BMFace *facerep, int mat_nr, bool do_interp)
BMFace **face_arr, BMFace *facerep, BMEdge **edge_arr,
int mat_nr, bool do_interp)
{
BMIter iter;
BMLoop *l;
BMFace *f, *interp_f;
BMEdge *bme;
float save_co[3];
int i;
f = BM_face_create_verts(bm, vert_arr, totv, facerep, BM_CREATE_NOP, true);
@ -389,8 +421,19 @@ static BMFace *bev_create_ngon(
else {
interp_f = facerep;
}
if (interp_f)
if (interp_f) {
bme = NULL;
if (edge_arr)
bme = edge_arr[i];
if (bme) {
copy_v3_v3(save_co, l->v->co);
closest_to_line_segment_v3(l->v->co, save_co, bme->v1->co, bme->v2->co);
}
BM_loop_interp_from_face(bm, l, interp_f, true, true);
if (bme) {
copy_v3_v3(l->v->co, save_co);
}
}
i++;
}
}
@ -407,23 +450,27 @@ static BMFace *bev_create_ngon(
return f;
}
static BMFace *bev_create_quad_tri(
static BMFace *bev_create_quad(
BMesh *bm, BMVert *v1, BMVert *v2, BMVert *v3, BMVert *v4,
BMFace *facerep, int mat_nr, bool do_interp)
{
BMVert *varr[4] = {v1, v2, v3, v4};
return bev_create_ngon(bm, varr, v4 ? 4 : 3, NULL, facerep, mat_nr, do_interp);
}
static BMFace *bev_create_quad_tri_ex(
BMesh *bm, BMVert *v1, BMVert *v2, BMVert *v3, BMVert *v4,
BMFace *f1, BMFace *f2, BMFace *f3, BMFace *f4, int mat_nr)
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, v4 ? 4 : 3, farr, f1, mat_nr, true);
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,
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, f1, earr, mat_nr, true);
}
/* Is Loop layer layer_index contiguous across shared vertex of l1 and l2? */
static bool contig_ldata_across_loops(
@ -486,49 +533,6 @@ static bool contig_ldata_across_edge(BMesh *bm, BMEdge *e, BMFace *f1, BMFace *f
return true;
}
/**
* Like bev_create_quad_tri, but when verts straddle an old edge.
* <pre>
* e
* |
* v1+---|---+v4
* | | |
* | | |
* v2+---|---+v3
* |
* f1 | f2
* </pre>
*
* Most CustomData for loops can be interpolated in their respective
* faces' loops, but for UVs and other 'has_math_cd' layers, only
* do this if the UVs are continuous across the edge e, otherwise pick
* one side (f1, arbitrarily), and interpolate them all on that side.
* For face data, use f1 (arbitrarily) as face representative.
*/
static BMFace *bev_create_quad_straddle(
BMesh *bm, BMVert *v1, BMVert *v2, BMVert *v3, BMVert *v4,
BMFace *f1, BMFace *f2, int mat_nr, bool is_seam)
{
BMFace *f, *facerep;
BMLoop *l;
BMIter iter;
f = bev_create_quad_tri(bm, v1, v2, v3, v4, f1, mat_nr, false);
if (!f)
return NULL;
BM_ITER_ELEM (l, &iter, f, BM_LOOPS_OF_FACE) {
if (is_seam || l->v == v1 || l->v == v2)
facerep = f1;
else
facerep = f2;
if (facerep)
BM_loop_interp_from_face(bm, l, facerep, true, true);
}
return f;
}
/* Merge (using average) all the UV values for loops of v's faces.
* Caller should ensure that no seams are violated by doing this. */
static void bev_merge_uvs(BMesh *bm, BMVert *v)
@ -564,6 +568,47 @@ static void bev_merge_uvs(BMesh *bm, BMVert *v)
}
}
/* Merge (using average) the UV values for two specific loops of v: those for faces containing v,
* and part of faces that share edge bme */
static void bev_merge_edge_uvs(BMesh *bm, BMEdge *bme, BMVert *v)
{
BMIter iter;
MLoopUV *luv;
BMLoop *l, *l1, *l2;
float uv[2];
int num_of_uv_layers = CustomData_number_of_layers(&bm->ldata, CD_MLOOPUV);
int i;
l1 = NULL;
l2 = NULL;
BM_ITER_ELEM (l, &iter, v, BM_LOOPS_OF_VERT) {
if (l->e == bme)
l1 = l;
else if (l->prev->e == bme)
l2 = l;
}
if (l1 == NULL || l2 == NULL)
return;
for (i = 0; i < num_of_uv_layers; i++) {
int cd_loop_uv_offset = CustomData_get_n_offset(&bm->ldata, CD_MLOOPUV, i);
if (cd_loop_uv_offset == -1)
return;
zero_v2(uv);
luv = BM_ELEM_CD_GET_VOID_P(l1, cd_loop_uv_offset);
add_v2_v2(uv, luv->uv);
luv = BM_ELEM_CD_GET_VOID_P(l2, cd_loop_uv_offset);
add_v2_v2(uv, luv->uv);
mul_v2_fl(uv, 0.5f);
luv = BM_ELEM_CD_GET_VOID_P(l1, cd_loop_uv_offset);
copy_v2_v2(luv->uv, uv);
luv = BM_ELEM_CD_GET_VOID_P(l2, cd_loop_uv_offset);
copy_v2_v2(luv->uv, uv);
}
}
/* Calculate coordinates of a point a distance d from v on e->e and return it in slideco */
static void slide_dist(EdgeHalf *e, BMVert *v, float d, float slideco[3])
{
@ -1523,6 +1568,20 @@ static void set_bound_vert_seams(BevVert *bv)
} while ((v = v->next) != bv->vmesh->boundstart);
}
static int count_bound_vert_seams(BevVert *bv)
{
int ans, i;
if (!bv->any_seam)
return 0;
ans = 0;
for (i = 0; i < bv->edgecount; i++)
if (bv->edges[i].is_seam)
ans++;
return ans;
}
#ifndef PRE_275_ALGORITHM
/* Is e between two planes where angle between is 180? */
static bool eh_on_plane(EdgeHalf *e)
@ -2948,6 +3007,63 @@ static VMesh *pipe_adj_vmesh(BevelParams *bp, BevVert *bv, BoundVert *vpipe)
return vm;
}
static void get_incident_edges(BMFace *f, BMVert *v, BMEdge **r_e1, BMEdge **r_e2)
{
BMIter iter;
BMEdge *e;
*r_e1 = NULL;
*r_e2 = NULL;
if (!f)
return;
BM_ITER_ELEM (e, &iter, f, BM_EDGES_OF_FACE) {
if (e->v1 == v || e->v2 == v) {
if (*r_e1 == NULL)
*r_e1 = e;
else if (*r_e2 == NULL)
*r_e2 = e;
}
}
}
static BMEdge *find_closer_edge(float *co, BMEdge *e1, BMEdge *e2)
{
float dsq1, dsq2;
BLI_assert(e1 != NULL && e2 != NULL);
dsq1 = dist_squared_to_line_segment_v3(co, e1->v1->co, e1->v2->co);
dsq2 = dist_squared_to_line_segment_v3(co, e2->v1->co, e2->v2->co);
if (dsq1 < dsq2)
return e1;
else
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)
{
BMIter iter;
BMEdge *beste = NULL;
float d2, beste_d2;
BMEdge *e;
float closest[3];
beste_d2 = 1e20;
BM_ITER_ELEM(e, &iter, f, BM_EDGES_OF_FACE) {
closest_to_line_segment_v3(closest, co, e->v1->co, e->v2->co);
d2 = len_squared_v3v3(closest, co);
if (d2 < beste_d2) {
beste_d2 = d2;
beste = e;
copy_v3_v3(r_snap_co, closest);
}
}
*r_snap_e = beste;
return beste_d2;
}
/*
* Given that the boundary is built and the boundary BMVerts have been made,
* calculate the positions of the interior mesh points for the M_ADJ pattern,
@ -2958,7 +3074,9 @@ static void bevel_build_rings(BevelParams *bp, BMesh *bm, BevVert *bv)
VMesh *vm1, *vm;
BoundVert *v;
BMVert *bmv1, *bmv2, *bmv3, *bmv4;
BMFace *f, *f2, *f23;
BMFace *f, *f2;
BMEdge *bme, *bme1, *bme2, *bme3;
EdgeHalf *e;
BoundVert *vpipe;
int mat_nr = bp->mat_nr;
@ -2996,8 +3114,14 @@ static void bevel_build_rings(BevelParams *bp, BMesh *bm, BevVert *bv)
v = vm->boundstart;
do {
i = v->index;
f = boundvert_rep_face(v);
f2 = boundvert_rep_face(v->next);
f = boundvert_rep_face(v, NULL);
f2 = boundvert_rep_face(v->next, NULL);
if (bp->vertex_only)
e = v->efirst;
else
e = v->ebev;
BLI_assert(e != NULL);
bme = e->e;
/* 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,
@ -3009,11 +3133,47 @@ static void bevel_build_rings(BevelParams *bp, BMesh *bm, BevVert *bv)
bmv3 = mesh_vert(vm, i, j + 1, k + 1)->v;
bmv4 = mesh_vert(vm, i, j + 1, k)->v;
BLI_assert(bmv1 && bmv2 && bmv3 && bmv4);
f23 = f;
if (odd && k == ns2 && f2 && !v->any_seam)
f23 = f2;
bev_create_quad_tri_ex(bm, bmv1, bmv2, bmv3, bmv4,
f, f23, f23, f, mat_nr);
if (bp->vertex_only) {
if (j < k) {
if (k == ns2 && j == ns2 - 1)
bev_create_quad_ex(bm, bmv1, bmv2, bmv3, bmv4, f2, f2, f2, f2,
NULL, NULL, v->next->efirst->e, bme, mat_nr);
else
bev_create_quad(bm, bmv1, bmv2, bmv3, bmv4, f2, f2, f2, f2, mat_nr);
}
else if (j > k) {
bev_create_quad(bm, bmv1, bmv2, bmv3, bmv4, f2, f2, f2, f2, mat_nr);
}
else { /* j == k */
/* only one edge attached to v, since vertex_only */
if (e->is_seam)
bev_create_quad_ex(bm, bmv1, bmv2, bmv3, bmv4, f2, f2, f2, f2,
bme, NULL, bme, NULL, mat_nr);
else
bev_create_quad_ex(bm, bmv1, bmv2, bmv3, bmv4, f2, f2, f2, f,
bme, NULL, bme, NULL, mat_nr);
}
}
else { /* edge bevel */
if (odd) {
if (k == ns2) {
if (e->is_seam)
bev_create_quad_ex(bm, bmv1, bmv2, bmv3, bmv4, f, f, f, f,
NULL, bme, bme, NULL, mat_nr);
else
bev_create_quad(bm, bmv1, bmv2, bmv3, bmv4, f, f2, f2, f, mat_nr);
}
else
bev_create_quad(bm, bmv1, bmv2, bmv3, bmv4, f, f, f, f, mat_nr);
}
else {
bme1 = k == ns2 - 1 ? bme : NULL;
bme3 = j == ns2 - 1 ? v->prev->ebev->e : NULL;
bme2 = bme1 != NULL ? bme1 : bme3;
bev_create_quad_ex(bm, bmv1, bmv2, bmv3, bmv4, f, f, f, f,
NULL, bme1, bme2, bme3, mat_nr);
}
}
}
}
} while ((v = v->next) != vm->boundstart);
@ -3032,66 +3192,147 @@ static void bevel_build_rings(BevelParams *bp, BMesh *bm, BevVert *bv)
}
}
} while ((v = v->next) != vm->boundstart);
if (!bv->any_seam)
bev_merge_uvs(bm, mesh_vert(vm, 0, ns2, ns2)->v);
bmv1 = mesh_vert(vm, 0, ns2, ns2)->v;
if (bp->vertex_only || count_bound_vert_seams(bv) <= 1)
bev_merge_uvs(bm, bmv1);
}
/* center ngon */
if (odd) {
BMFace *frep;
BMEdge *frep_e1, *frep_e2, *frep_e;
BMVert **vv = NULL;
BMFace **vf = NULL;
BMEdge **ve = NULL;
BLI_array_staticdeclare(vv, BM_DEFAULT_NGON_STACK_SIZE);
BLI_array_staticdeclare(vf, BM_DEFAULT_NGON_STACK_SIZE);
BLI_array_staticdeclare(ve, BM_DEFAULT_NGON_STACK_SIZE);
if (bv->any_seam) {
frep = boundvert_rep_face(vm->boundstart, NULL);
get_incident_edges(frep, bv->v, &frep_e1, &frep_e2);
}
else {
frep = NULL;
frep_e1 = frep_e2 = NULL;
}
v = vm->boundstart;
do {
i = v->index;
BLI_array_append(vv, mesh_vert(vm, i, ns2, ns2)->v);
BLI_array_append(vf, v->any_seam ? f : boundvert_rep_face(v));
if (frep) {
BLI_array_append(vf, frep);
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);
}
else {
BLI_array_append(vf, frep);
BLI_array_append(ve, NULL);
}
} while ((v = v->next) != vm->boundstart);
f = boundvert_rep_face(vm->boundstart);
bev_create_ngon(bm, vv, BLI_array_count(vv), vf, f, mat_nr, true);
bev_create_ngon(bm, vv, BLI_array_count(vv), vf, frep, ve, mat_nr, true);
BLI_array_free(vv);
BLI_array_free(vf);
BLI_array_free(ve);
}
}
/* If we make a poly out of verts around bv, snapping to rep frep, will uv poly have zero area?
* The uv poly is made by snapping all outside-of-frep vertices to the closest edge in frep.
* Assume that this funciton is called when the only inside-of-frep vertex is vm->boundstart.
* The poly will have zero area if the distance of that first vertex to some edge e is zero, and all
* the other vertices snap to e or snap to an edge at a point that is essentially on e too. */
static bool is_bad_uv_poly(BevVert *bv, BMFace *frep)
{
BoundVert *v;
BMEdge *snape, *firste;
float co[3];
VMesh *vm = bv->vmesh;
float d2;
v = vm->boundstart;
d2 = snap_face_dist_squared(v->nv.v->co, frep, &firste, co);
if (d2 > BEVEL_EPSILON_BIG_SQ || firste == NULL)
return false;
for (v = v->next; v != vm->boundstart; v = v->next) {
snap_face_dist_squared(v->nv.v->co, frep, &snape, co);
if (snape != firste) {
d2 = dist_to_line_v3(co, firste->v1->co, firste->v2->co);
if (d2 > BEVEL_EPSILON_BIG_SQ)
return false;
}
}
return true;
}
static BMFace *bevel_build_poly(BevelParams *bp, BMesh *bm, BevVert *bv)
{
BMFace *f;
BMFace *f, *frep, *frep2;
int n, k;
VMesh *vm = bv->vmesh;
BoundVert *v;
BMFace *frep;
BMEdge *frep_e1, *frep_e2, *frep_e;
BMVert **vv = NULL;
BMFace **vf = NULL;
BMEdge **ve = NULL;
BLI_array_staticdeclare(vv, BM_DEFAULT_NGON_STACK_SIZE);
BLI_array_staticdeclare(vf, BM_DEFAULT_NGON_STACK_SIZE);
BLI_array_staticdeclare(ve, BM_DEFAULT_NGON_STACK_SIZE);
frep = boundvert_rep_face(vm->boundstart);
if (bv->any_seam) {
frep = boundvert_rep_face(vm->boundstart, &frep2);
if (frep2 && frep && is_bad_uv_poly(bv, frep)) {
frep = frep2;
}
get_incident_edges(frep, bv->v, &frep_e1, &frep_e2);
}
else {
frep = NULL;
frep_e1 = frep_e2 = NULL;
}
v = vm->boundstart;
n = 0;
do {
/* accumulate vertices for vertex ngon */
/* also accumulate faces in which uv interpolation is to happen for each */
BLI_array_append(vv, v->nv.v);
BLI_array_append(vf, bv->any_seam ? frep : boundvert_rep_face(v));
if (frep) {
BLI_array_append(vf, frep);
frep_e = find_closer_edge(v->nv.v->co, frep_e1, frep_e2);
BLI_array_append(ve, n > 0 ? frep_e : NULL);
}
else {
BLI_array_append(vf, boundvert_rep_face(v, NULL));
BLI_array_append(ve, NULL);
}
n++;
if (v->ebev && v->ebev->seg > 1) {
for (k = 1; k < v->ebev->seg; k++) {
BLI_array_append(vv, mesh_vert(vm, v->index, 0, k)->v);
BLI_array_append(vf, bv->any_seam ? frep : boundvert_rep_face(v));
if (frep) {
BLI_array_append(vf, frep);
frep_e = find_closer_edge(mesh_vert(vm, v->index, 0, k)->v->co, frep_e1, frep_e2);
BLI_array_append(ve, k < v->ebev->seg / 2 ? NULL : frep_e);
}
else {
BLI_array_append(vf, boundvert_rep_face(v, NULL));
BLI_array_append(ve, NULL);
}
n++;
}
}
} while ((v = v->next) != vm->boundstart);
if (n > 2) {
f = bev_create_ngon(bm, vv, n, vf, boundvert_rep_face(v), bp->mat_nr, true);
f = bev_create_ngon(bm, vv, n, vf, frep, ve, bp->mat_nr, true);
}
else {
f = NULL;
}
BLI_array_free(vv);
BLI_array_free(vf);
BLI_array_free(ve);
return f;
}
@ -3688,7 +3929,7 @@ static bool bev_rebuild_polygon(BMesh *bm, BevelParams *bp, BMFace *f)
}
if (do_rebuild) {
n = BLI_array_count(vv);
f_new = bev_create_ngon(bm, vv, n, NULL, f, -1, true);
f_new = bev_create_ngon(bm, vv, n, NULL, f, NULL, -1, true);
for (k = 0; k < BLI_array_count(vv_fix); k++) {
bev_merge_uvs(bm, vv_fix[k]);
@ -3886,18 +4127,20 @@ static void weld_cross_attrs_copy(BMesh *bm, BevVert *bv, VMesh *vm, int vmindex
}
}
/*
* Build the polygons along the selected Edge
*/
static void bevel_build_edge_polygons(BMesh *bm, BevelParams *bp, BMEdge *bme)
{
BevVert *bv1, *bv2;
BMVert *bmv1, *bmv2, *bmv3, *bmv4, *bmv1i, *bmv2i, *bmv3i, *bmv4i;
BMVert *bmv1, *bmv2, *bmv3, *bmv4;
VMesh *vm1, *vm2;
EdgeHalf *e1, *e2;
BMEdge *bme1, *bme2;
BMEdge *bme1, *bme2, *center_bme;
BMFace *f1, *f2, *f;
BMVert *verts[4];
BMFace *faces[4];
BMEdge *edges[4];
int k, nseg, i1, i2, odd, mid;
int mat_nr = bp->mat_nr;
@ -3914,11 +4157,15 @@ static void bevel_build_edge_polygons(BMesh *bm, BevelParams *bp, BMEdge *bme)
BLI_assert(e1 && e2);
/* v4 v3
* \ /
* e->v1 - e->v2
* / \
* v1 v2
/*
* bme->v1
* / | \
* v1--|--v4
* | | |
* | | |
* v2--|--v3
* \ | /
* bme->v2
*/
nseg = e1->seg;
BLI_assert(nseg > 0 && nseg == e2->seg);
@ -3932,42 +4179,67 @@ static void bevel_build_edge_polygons(BMesh *bm, BevelParams *bp, BMEdge *bme)
f1 = e1->fprev;
f2 = e1->fnext;
faces[0] = faces[1] = f1;
faces[2] = faces[3] = f2;
i1 = e1->leftv->index;
i2 = e2->leftv->index;
vm1 = bv1->vmesh;
vm2 = bv2->vmesh;
if (nseg == 1) {
bev_create_quad_straddle(bm, bmv1, bmv2, bmv3, bmv4, f1, f2, mat_nr, e1->is_seam);
}
else {
bmv1i = bmv1;
bmv2i = bmv2;
odd = nseg % 2;
mid = nseg / 2;
for (k = 1; k <= nseg; k++) {
bmv4i = mesh_vert(vm1, i1, 0, k)->v;
bmv3i = mesh_vert(vm2, i2, 0, nseg - k)->v;
if (odd && k == mid + 1) {
bev_create_quad_straddle(bm, bmv1i, bmv2i, bmv3i, bmv4i, f1, f2, mat_nr, e1->is_seam);
verts[0] = bmv1;
verts[1] = bmv2;
odd = nseg % 2;
mid = nseg / 2;
center_bme = NULL;
for (k = 1; k <= nseg; k++) {
verts[3] = mesh_vert(vm1, i1, 0, k)->v;
verts[2] = mesh_vert(vm2, i2, 0, nseg - k)->v;
if (odd && k == mid + 1) {
if (e1->is_seam) {
/* straddles a seam: choose to interpolate in f1 and snap right edge to bme */
edges[0] = edges[1] = NULL;
edges[2] = edges[3] = bme;
bev_create_ngon(bm, verts, 4, NULL, f1, edges, mat_nr, true);
}
else {
f = (k <= mid) ? f1 : f2;
bev_create_quad_tri(bm, bmv1i, bmv2i, bmv3i, bmv4i, f, mat_nr, true);
/* straddles but not a seam: interpolate left half in f1, right half in f2 */
bev_create_ngon(bm, verts, 4, faces, NULL, NULL, mat_nr, true);
}
bmv1i = bmv4i;
bmv2i = bmv3i;
}
if (!odd && !e1->is_seam) {
bev_merge_uvs(bm, mesh_vert(vm1, i1, 0, mid)->v);
bev_merge_uvs(bm, mesh_vert(vm2, i2, 0, mid)->v);
else if (!odd && k == mid) {
/* left poly that touches an even center line on right */
edges[0] = edges[1] = NULL;
edges[2] = edges[3] = bme;
bev_create_ngon(bm, verts, 4, NULL, f1, edges, mat_nr, true);
center_bme = BM_edge_exists(verts[2], verts[3]);
BLI_assert(center_bme != NULL);
}
else if (!odd && k == mid + 1) {
/* right poly that touches an even center line on left */
edges[0] = edges[1] = bme;
edges[2] = edges[3] = NULL;
bev_create_ngon(bm, verts, 4, NULL, f2, edges, mat_nr, true);
}
else {
/* doesn't cross or touch the center line, so interpolate in appropriate f1 or f2 */
f = (k <= mid) ? f1 : f2;
bev_create_ngon(bm, verts, 4, NULL, f, NULL, mat_nr, true);
}
verts[0] = verts[3];
verts[1] = verts[2];
}
if (!odd) {
if (!e1->is_seam)
bev_merge_edge_uvs(bm, center_bme, mesh_vert(vm1, i1, 0, mid)->v);
if (!e2->is_seam)
bev_merge_edge_uvs(bm, center_bme, mesh_vert(vm2, i2, 0, mid)->v);
}
/* Fix UVs along end edge joints. A nop unless other side built already. */
if (!e1->is_seam && bv1->vmesh->mesh_kind == M_NONE)
/* TODO: if some seam, may want to do selective merge */
if (!bv1->any_seam && bv1->vmesh->mesh_kind == M_NONE)
bev_merge_end_uvs(bm, bv1, e1);
if (!e2->is_seam && bv2->vmesh->mesh_kind == M_NONE)
if (!bv2->any_seam && bv2->vmesh->mesh_kind == M_NONE)
bev_merge_end_uvs(bm, bv2, e2);
/* Copy edge data to first and last edge */