Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'source/blender/bmesh/tools/bmesh_bevel.c')
-rw-r--r--source/blender/bmesh/tools/bmesh_bevel.c644
1 files changed, 432 insertions, 212 deletions
diff --git a/source/blender/bmesh/tools/bmesh_bevel.c b/source/blender/bmesh/tools/bmesh_bevel.c
index a593e40d850..5461e5fe788 100644
--- a/source/blender/bmesh/tools/bmesh_bevel.c
+++ b/source/blender/bmesh/tools/bmesh_bevel.c
@@ -41,10 +41,8 @@
-/* experemental - Campbell */
-// #define USE_ALTERNATE_ADJ
-
-#define BEVEL_EPSILON 1e-6
+#define BEVEL_EPSILON_D 1e-6
+#define BEVEL_EPSILON 1e-6f
/* for testing */
// #pragma GCC diagnostic error "-Wpadded"
@@ -94,7 +92,7 @@ typedef struct VMesh {
M_NONE, /* no polygon mesh needed */
M_POLY, /* a simple polygon */
M_ADJ, /* "adjacent edges" mesh pattern */
-// M_CROSS, /* "cross edges" mesh pattern */
+ M_ADJ_SUBDIV, /* like M_ADJ, but using subdivision */
M_TRI_FAN, /* a simple polygon - fan filled */
M_QUAD_STRIP, /* a simple polygon - cut into paralelle strips */
} mesh_kind;
@@ -124,7 +122,7 @@ typedef struct BevelParams {
// #pragma GCC diagnostic ignored "-Wpadded"
-//#include "bevdebug.c"
+// #include "bevdebug.c"
/* Make a new BoundVert of the given kind, insert it at the end of the circular linked
* list with entry point bv->boundstart, and return it. */
@@ -323,7 +321,7 @@ static void offset_meet(EdgeHalf *e1, EdgeHalf *e2, BMVert *v, BMFace *f,
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);
- if (angle_v3v3(dir1, dir2) < 100.0f * (float)BEVEL_EPSILON) {
+ if (angle_v3v3(dir1, dir2) < 100.0f * BEVEL_EPSILON) {
/* special case: e1 and e2 are parallel; put offset point perp to both, from v.
* need to find a suitable plane.
* if offsets are different, we're out of luck: just use e1->offset */
@@ -409,7 +407,7 @@ static void offset_in_two_planes(EdgeHalf *e1, EdgeHalf *e2, EdgeHalf *emid,
madd_v3_v3fl(off2a, norm_perp2, e2->offset);
add_v3_v3v3(off2b, off2a, dir2);
- if (angle_v3v3(dir1, dir2) < 100.0f * (float)BEVEL_EPSILON) {
+ if (angle_v3v3(dir1, dir2) < 100.0f * BEVEL_EPSILON) {
/* lines are parallel; off1a is a good meet point */
copy_v3_v3(meetco, off1a);
}
@@ -421,7 +419,7 @@ static void offset_in_two_planes(EdgeHalf *e1, EdgeHalf *e2, EdgeHalf *emid,
}
else if (iret == 2) {
/* lines are not coplanar; meetco and isect2 are nearest to first and second lines */
- if (len_v3v3(meetco, isect2) > 100.0f * (float)BEVEL_EPSILON) {
+ if (len_v3v3(meetco, isect2) > 100.0f * BEVEL_EPSILON) {
/* offset lines don't meet: project average onto emid; this is not ideal (see TODO above) */
mid_v3_v3v3(co, meetco, isect2);
closest_to_line_v3(meetco, co, v->co, BM_edge_other_vert(emid->e, v)->co);
@@ -470,7 +468,7 @@ static void slide_dist(EdgeHalf *e, BMVert *v, float d, float slideco[3])
sub_v3_v3v3(dir, v->co, BM_edge_other_vert(e->e, v)->co);
len = normalize_v3(dir);
if (d > len)
- d = len - (float)(50.0 * BEVEL_EPSILON);
+ d = len - (float)(50.0 * BEVEL_EPSILON_D);
copy_v3_v3(slideco, v->co);
madd_v3_v3fl(slideco, dir, -d);
}
@@ -501,79 +499,6 @@ static int bev_ccw_test(BMEdge *a, BMEdge *b, BMFace *f)
return lb->next == la ? 1 : -1;
}
-#ifdef USE_ALTERNATE_ADJ
-
-static void vmesh_cent(VMesh *vm, float r_cent[3])
-{
- BoundVert *v;
- zero_v3(r_cent);
-
- v = vm->boundstart;
- do {
- add_v3_v3(r_cent, v->nv.co);
- } while ((v = v->next) != vm->boundstart);
- mul_v3_fl(r_cent, 1.0f / (float)vm->count);
-}
-
-/**
- *
- * This example shows a tri fan of quads,
- * but could be an NGon fan of quads too.
- * <pre>
- * The whole triangle X
- * represents the / \
- * new bevel face. / \
- * / \
- * Split into / \
- * a quad fan. / \
- * / \
- * / \
- * / \
- * co_prev +-. .-+
- * / `-._ _.-' \
- * / co_cent`-+-' \
- * / | \
- * Quad of / | \
- * interest -- / ---> X | \
- * / | \
- * / | \
- * / co_next| \
- * co_orig +-----------------+-----------------+
- *
- * For each quad, calcualte UV's based on the following:
- * U = k / (vm->seg * 2)
- * V = ring / (vm->seg * 2)
- * quad = (co_orig, co_prev, co_cent, co_next)
- * ... note that co_cent is the same for all quads in the fan.
- * </pre>
- *
- */
-
-static void get_point_uv(float uv[2],
- /* all these args are int's originally
- * but pass as floats to the function */
- const float seg, const float ring, const float k)
-{
- uv[0] = (ring / seg) * 2.0f;
- uv[1] = (k / seg) * 2.0f;
-}
-
-/* TODO: make this a lot smarter!,
- * this is the main reason USE_ALTERNATE_ADJ isn't so good right now :S */
-static float get_point_uv_factor(const float uv[2])
-{
- return sinf(1.0f - max_ff(uv[0], uv[1]) / 2.0f);
-}
-
-static void get_point_on_round_edge(const float uv[2],
- float quad[4][3],
- float r_co[3])
-{
- interp_bilinear_quad_v3(quad, uv[0], uv[1], r_co);
-}
-
-#else /* USE_ALTERNATE_ADJ */
-
/* Fill matrix r_mat so that a point in the sheared parallelogram with corners
* va, vmid, vb (and the 4th that is implied by it being a parallelogram)
* is transformed to the unit square by multiplication with r_mat.
@@ -601,7 +526,7 @@ static int make_unit_square_map(const float va[3], const float vmid[3], const fl
sub_v3_v3v3(va_vmid, vmid, va);
sub_v3_v3v3(vb_vmid, vmid, vb);
- if (fabsf(angle_v3v3(va_vmid, vb_vmid) - (float)M_PI) > 100.f *(float)BEVEL_EPSILON) {
+ if (fabsf(angle_v3v3(va_vmid, vb_vmid) - (float)M_PI) > 100.0f * BEVEL_EPSILON) {
sub_v3_v3v3(vo, va, vb_vmid);
cross_v3_v3v3(vddir, vb_vmid, va_vmid);
normalize_v3(vddir);
@@ -694,8 +619,6 @@ static void snap_to_edge_profile(EdgeHalf *e, const float va[3], const float vb[
}
}
-#endif /* !USE_ALTERNATE_ADJ */
-
/* Make a circular list of BoundVerts for bv, each of which has the coordinates
* of a vertex on the the boundary of the beveled vertex bv->v.
* Also decide on the mesh pattern that will be used inside the boundary.
@@ -811,7 +734,10 @@ static void build_boundary(BevelParams *bp, BevVert *bv)
} while ((e = e->next) != efirst);
BLI_assert(vm->count >= 2);
- if (vm->count == 2 && bv->edgecount == 3) {
+ if (bp->vertex_only) {
+ vm->mesh_kind = bp->seg > 1 ? M_ADJ_SUBDIV : M_POLY;
+ }
+ else if (vm->count == 2 && bv->edgecount == 3) {
vm->mesh_kind = M_NONE;
}
else if (bv->selcount == 2) {
@@ -846,19 +772,6 @@ static void bevel_build_rings(BMesh *bm, BevVert *bv)
float co[3], coa[3], cob[3], midco[3];
float va_pipe[3], vb_pipe[3];
-#ifdef USE_ALTERNATE_ADJ
- /* ordered as follows (orig, prev, center, next)*/
- float quad_plane[4][3];
- float quad_orig[4][3];
-#endif
-
-
-#ifdef USE_ALTERNATE_ADJ
- /* the rest are initialized inline, this remains the same for all */
- vmesh_cent(vm, quad_plane[2]);
- copy_v3_v3(quad_orig[2], bv->v->co);
-#endif
-
n = vm->count;
ns = vm->seg;
ns2 = ns / 2;
@@ -875,7 +788,7 @@ static void bevel_build_rings(BMesh *bm, BevVert *bv)
float dir1[3], dir2[3];
sub_v3_v3v3(dir1, bv->v->co, BM_edge_other_vert(e1->e, bv->v)->co);
sub_v3_v3v3(dir2, BM_edge_other_vert(e2->e, bv->v)->co, bv->v->co);
- if (angle_v3v3(dir1, dir2) < 100.0f * (float)BEVEL_EPSILON) {
+ if (angle_v3v3(dir1, dir2) < 100.0f * BEVEL_EPSILON) {
epipe = e1;
break;
}
@@ -918,37 +831,6 @@ static void bevel_build_rings(BMesh *bm, BevVert *bv)
copy_v3_v3(nv->co, cob);
nv->v = nvnext->v;
-#ifdef USE_ALTERNATE_ADJ
- /* plane */
- copy_v3_v3(quad_plane[0], v->nv.co);
- mid_v3_v3v3(quad_plane[1], v->nv.co, v->prev->nv.co);
- /* quad[2] is set */
- mid_v3_v3v3(quad_plane[3], v->nv.co, v->next->nv.co);
-
- /* orig */
- copy_v3_v3(quad_orig[0], v->nv.co); /* only shared location between 2 quads */
- project_to_edge(v->ebev->prev->e, v->nv.co, v->prev->nv.co, quad_orig[1]);
- project_to_edge(v->ebev->e, v->nv.co, v->next->nv.co, quad_orig[3]);
-
- //bl_debug_draw_quad_add(UNPACK4(quad_plane));
- //bl_debug_draw_quad_add(UNPACK4(quad_orig));
-#endif
-
-#ifdef USE_ALTERNATE_ADJ
- for (k = 1; k < ns; k++) {
- float uv[2];
- float fac;
- float co_plane[3];
- float co_orig[3];
-
- get_point_uv(uv, v->ebev->seg, ring, k);
- get_point_on_round_edge(uv, quad_plane, co_plane);
- get_point_on_round_edge(uv, quad_orig, co_orig);
- fac = get_point_uv_factor(uv);
- interp_v3_v3v3(co, co_plane, co_orig, fac);
- copy_v3_v3(mesh_vert(vm, i, ring, k)->co, co);
- }
-#else
/* TODO: better calculation of new midarc point? */
project_to_edge(v->ebev->e, coa, cob, midco);
@@ -962,7 +844,6 @@ static void bevel_build_rings(BMesh *bm, BevVert *bv)
copy_v3_v3(va_pipe, mesh_vert(vm, i, 0, 0)->co);
copy_v3_v3(vb_pipe, mesh_vert(vm, i, 0, ns)->co);
}
-#endif
}
} while ((v = v->next) != vm->boundstart);
}
@@ -990,9 +871,7 @@ static void bevel_build_rings(BMesh *bm, BevVert *bv)
if (epipe)
snap_to_edge_profile(epipe, va_pipe, vb_pipe, co);
-#ifndef USE_ALTERNATE_ADJ
copy_v3_v3(nv->co, co);
-#endif
BLI_assert(nv->v == NULL && nvprev->v == NULL);
create_mesh_bmvert(bm, vm, i, ring, k, bv->v);
copy_mesh_vert(vm, vprev->index, k, ns - ring, i, ring, k);
@@ -1041,9 +920,7 @@ static void bevel_build_rings(BMesh *bm, BevVert *bv)
mid_v3_v3v3v3(co, nvprev->co, nv->co, nvnext->co);
if (epipe)
snap_to_edge_profile(epipe, va_pipe, vb_pipe, co);
-#ifndef USE_ALTERNATE_ADJ
copy_v3_v3(nv->co, co);
-#endif
create_mesh_bmvert(bm, vm, i, k, ns2, bv->v);
copy_mesh_vert(vm, vprev->index, ns2, ns - k, i, k, ns2);
copy_mesh_vert(vm, vnext->index, ns2, k, i, k, ns2);
@@ -1053,9 +930,7 @@ static void bevel_build_rings(BMesh *bm, BevVert *bv)
mid_v3_v3v3(co, nvprev->co, nv->co);
if (epipe)
snap_to_edge_profile(epipe, va_pipe, vb_pipe, co);
-#ifndef USE_ALTERNATE_ADJ
copy_v3_v3(nv->co, co);
-#endif
create_mesh_bmvert(bm, vm, i, k, ns2, bv->v);
copy_mesh_vert(vm, vprev->index, ns2, ns - k, i, k, ns2);
@@ -1065,9 +940,7 @@ static void bevel_build_rings(BMesh *bm, BevVert *bv)
mid_v3_v3v3(co, nv->co, nvnext->co);
if (epipe)
snap_to_edge_profile(epipe, va_pipe, vb_pipe, co);
-#ifndef USE_ALTERNATE_ADJ
copy_v3_v3(nv->co, co);
-#endif
create_mesh_bmvert(bm, vm, i, k, ns2, bv->v);
copy_mesh_vert(vm, vnext->index, ns2, k, i, k, ns2);
@@ -1220,6 +1093,413 @@ static void bevel_build_rings(BMesh *bm, BevVert *bv)
}
}
+static VMesh *new_adj_subdiv_vmesh(MemArena *mem_arena, int count, int seg, BoundVert *bounds)
+{
+ VMesh *vm;
+
+ vm = (VMesh *)BLI_memarena_alloc(mem_arena, sizeof(VMesh));
+ vm->count = count;
+ vm->seg = seg;
+ vm->boundstart = bounds;
+ vm->mesh = (NewVert *)BLI_memarena_alloc(mem_arena, count * (1 + seg / 2) * (1 + seg) * sizeof(NewVert));
+ vm->mesh_kind = M_ADJ_SUBDIV;
+ return vm;
+}
+
+/* VMesh verts for vertex i have data for (i, 0 <= j <= ns2, 0 <= k <= ns), 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)
+ * 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] */
+static NewVert *mesh_vert_canon(VMesh *vm, int i, int j, int k)
+{
+ int n, ns, ns2, odd;
+ NewVert *ans;
+
+ n = vm->count;
+ ns = vm->seg;
+ ns2 = ns / 2;
+ odd = ns % 2;
+ BLI_assert(0 <= i && i <= n && 0 <= j && j <= ns && 0 <= k && k <= ns);
+
+ if (!odd && j == ns2 && k == ns2)
+ ans = mesh_vert(vm, 0, j, k);
+ else if (j <= ns2 - 1 + odd && k <= ns2)
+ ans = mesh_vert(vm, i, j, k);
+ else if (k <= ns2)
+ ans = mesh_vert(vm, (i + n - 1) % n, k, ns - j);
+ else
+ ans = mesh_vert(vm, (i + 1) % n, ns - k, j);
+ return ans;
+}
+
+static int is_canon(VMesh *vm, int i, int j, int k)
+{
+ int ns2 = vm->seg / 2;
+ if (vm->seg % 2 == 1)
+ return (j <= ns2 && k <= ns2);
+ else
+ return ((j < ns2 && k <= ns2) || (j == ns2 && k == ns2 && i == 0));
+}
+
+/* Copy the vertex data to all of vm verts from canonical ones */
+static void vmesh_copy_equiv_verts(VMesh *vm)
+{
+ int n, ns, ns2, i, j, k;
+ NewVert *v0, *v1;
+
+ n = vm->count;
+ ns = vm->seg;
+ ns2 = ns / 2;
+ for (i = 0; i < n; i++) {
+ for (j = 0; j <= ns2; j++) {
+ for (k = 0; k <= ns; k++) {
+ if (is_canon(vm, i, j, k))
+ continue;
+ v1 = mesh_vert(vm, i, j, k);
+ v0 = mesh_vert_canon(vm, i, j, k);
+ copy_v3_v3(v1->co, v0->co);
+ v1->v = v0->v;
+ }
+ }
+ }
+}
+
+/* Calculate and return in r_cent the centroid of the center poly */
+static void vmesh_center(VMesh *vm, float r_cent[3])
+{
+ int n, ns2, i;
+
+ n = vm->count;
+ ns2 = vm->seg / 2;
+ if (vm->seg % 2) {
+ zero_v3(r_cent);
+ for (i = 0; i < n; i++) {
+ add_v3_v3(r_cent, mesh_vert(vm, i, ns2, ns2)->co);
+ }
+ mul_v3_fl(r_cent, 1.0f / (float) n);
+ }
+ else {
+ copy_v3_v3(r_cent, mesh_vert(vm, 0, ns2, ns2)->co);
+ }
+}
+
+/* Do one step of quadratic subdivision (Doo-Sabin), with special rules at boundaries.
+ * For now, this is written assuming vm0->nseg is odd.
+ * See Hwang-Chuang 2003 paper: "N-sided hole filling and vertex blending using subdivision surfaces" */
+static VMesh *quadratic_subdiv(MemArena *mem_arena, VMesh *vm0)
+{
+ int n, ns0, ns20, ns1 /*, ns21 */;
+ int i, j, k, j1, k1;
+ VMesh *vm1;
+ float co[3], co1[3], co2[3], co3[3], co4[3];
+ float co11[3], co21[3], co31[3], co41[3];
+ float denom;
+ const float wcorner[4] = {0.25f, 0.25f, 0.25f, 0.25f};
+ const float wboundary[4] = {0.375f, 0.375f, 0.125f, 0.125f}; /* {3, 3, 1, 1}/8 */
+ const float winterior[4] = {0.5625f, 0.1875f, 0.1875f, 0.0625f}; /* {9, 3, 3, 1}/16 */
+
+ n = vm0->count;
+ ns0 = vm0->seg;
+ ns20 = ns0 / 2;
+ BLI_assert(ns0 % 2 == 1);
+
+ ns1 = 2 * ns0 - 1;
+ // ns21 = ns1 / 2; /* UNUSED */
+ vm1 = new_adj_subdiv_vmesh(mem_arena, n, ns1, vm0->boundstart);
+
+ for (i = 0; i < n; i ++) {
+ /* For handle vm0 polys with lower left corner at (i,j,k) for
+ * j in [0, ns20], k in [0, ns20]; then the center ngon.
+ * but only fill in data for canonical verts of v1. */
+ for (j = 0; j <= ns20; j++) {
+ for (k = 0; k <= ns20; k++) {
+ if (j == ns20 && k == ns20)
+ continue; /* center ngon is special */
+ copy_v3_v3(co1, mesh_vert_canon(vm0, i, j, k)->co);
+ copy_v3_v3(co2, mesh_vert_canon(vm0, i, j, k + 1)->co);
+ copy_v3_v3(co3, mesh_vert_canon(vm0, i, j + 1, k + 1)->co);
+ copy_v3_v3(co4, mesh_vert_canon(vm0, i, j + 1, k)->co);
+ if (j == 0 && k == 0) {
+ /* corner */
+ copy_v3_v3(co11, co1);
+ interp_v3_v3v3(co21, co1, co2, 0.5f);
+ interp_v3_v3v3v3v3(co31, co1, co2, co3, co4, wcorner);
+ interp_v3_v3v3(co41, co1, co4, 0.5f);
+ }
+ else if (j == 0) {
+ /* ring 0 boundary */
+ interp_v3_v3v3(co11, co1, co2, 0.25f);
+ interp_v3_v3v3(co21, co1, co2, 0.75f);
+ interp_v3_v3v3v3v3(co31, co2, co3, co1, co4, wboundary);
+ interp_v3_v3v3v3v3(co41, co1, co4, co2, co3, wboundary);
+ }
+ else if (k == 0) {
+ /* ring-starts boundary */
+ interp_v3_v3v3(co11, co1, co4, 0.25f);
+ interp_v3_v3v3v3v3(co21, co1, co2, co3, co4, wboundary);
+ interp_v3_v3v3v3v3(co31, co3, co4, co1, co2, wboundary);
+ interp_v3_v3v3(co41, co1, co4, 0.75f);
+ }
+ else {
+ /* interior */
+ interp_v3_v3v3v3v3(co11, co1, co2, co4, co3, winterior);
+ interp_v3_v3v3v3v3(co21, co2, co1, co3, co4, winterior);
+ interp_v3_v3v3v3v3(co31, co3, co2, co4, co1, winterior);
+ interp_v3_v3v3v3v3(co41, co4, co1, co3, co2, winterior);
+ }
+ j1 = 2 * j;
+ k1 = 2 * k;
+ if (is_canon(vm1, i, j1, k1))
+ copy_v3_v3(mesh_vert(vm1, i, j1, k1)->co, co11);
+ if (is_canon(vm1, i, j1, k1 + 1))
+ copy_v3_v3(mesh_vert(vm1, i, j1, k1 + 1)->co, co21);
+ if (is_canon(vm1, i, j1 + 1, k1 + 1))
+ copy_v3_v3(mesh_vert(vm1, i, j1 + 1, k1 + 1)->co, co31);
+ if (is_canon(vm1, i, j1 + 1, k1))
+ copy_v3_v3(mesh_vert(vm1, i, j1 + 1, k1)->co, co41);
+ }
+ }
+
+ /* center ngon */
+ denom = 8.0f * (float) n;
+ zero_v3(co);
+ for (j = 0; j < n; j++) {
+ copy_v3_v3(co1, mesh_vert(vm0, j, ns20, ns20)->co);
+ if (i == j)
+ madd_v3_v3fl(co, co1, (4.0f * (float) n + 2.0f) / denom);
+ else if ((i + 1) % n == j || (i + n - 1) % n == j)
+ madd_v3_v3fl(co, co1, ((float) n + 2.0f) / denom);
+ else
+ madd_v3_v3fl(co, co1, 2.0f / denom);
+ }
+ copy_v3_v3(mesh_vert(vm1, i, 2 * ns20, 2 * ns20)->co, co);
+ }
+
+ vmesh_copy_equiv_verts(vm1);
+ return vm1;
+}
+
+/* After a step of quadratic_subdiv, adjust the ring 1 verts to be on the planes of their respective faces,
+ * so that the cross-tangents will match on further subdivision. */
+static void fix_vmesh_tangents(VMesh *vm, BevVert *bv)
+{
+ int i, n;
+ NewVert *v;
+ BoundVert *bndv;
+ float co[3];
+
+ n = vm->count;
+ bndv = vm->boundstart;
+ do {
+ i = bndv->index;
+
+ /* (i, 1, 1) snap to edge line */
+ v = mesh_vert(vm, i, 1, 1);
+ closest_to_line_v3(co, v->co, bndv->nv.co, bv->v->co);
+ copy_v3_v3(v->co, co);
+ copy_v3_v3(mesh_vert(vm, (i + n -1) % n, 1, vm->seg - 1)->co, co);
+
+ /* Also want (i, 1, k) snapped to plane of adjacent face for
+ * 1 < k < ns - 1, but current initial cage and subdiv rules
+ * ensure this, so nothing to do */
+ } while ((bndv = bndv->next) != vm->boundstart);
+}
+
+/* Fill frac with fractions of way along ring 0 for vertex i, for use with interp_range function */
+static void fill_vmesh_fracs(VMesh *vm, float *frac, int i)
+{
+ int k, ns;
+ float total = 0.0f;
+
+ ns = vm->seg;
+ frac[0] = 0.0f;
+ for (k = 0; k < ns; k++) {
+ total += len_v3v3(mesh_vert(vm, i, 0, k)->co, mesh_vert(vm, i, 0, k + 1)->co);
+ frac[k + 1] = total;
+ }
+ if (total > BEVEL_EPSILON) {
+ for (k = 1; k <= ns; k++)
+ frac[k] /= total;
+ }
+}
+
+/* Return i such that frac[i] <= f <= frac[i + 1], where frac[n] == 1.0
+ * and put fraction of rest of way between frac[i] and frac[i + 1] into r_rest */
+static int interp_range(const float *frac, int n, const float f, float *r_rest)
+{
+ int i;
+ float rest;
+
+ /* could binary search in frac, but expect n to be reasonably small */
+ for (i = 0; i < n; i++) {
+ if (f <= frac[i + 1]) {
+ rest = f - frac[i];
+ if (rest == 0)
+ *r_rest = 0.0f;
+ else
+ *r_rest = rest / (frac[i + 1] - frac[i]);
+ return i;
+ }
+ }
+ *r_rest = 0.0f;
+ return n;
+}
+
+/* Interpolate given vmesh to make one with target nseg and evenly spaced border vertices */
+static VMesh *interp_vmesh(MemArena *mem_arena, VMesh *vm0, int nseg)
+{
+ int n, ns0, nseg2, odd, i, j, k, j0, k0;
+ float *prev_frac, *frac, f, restj, restk;
+ float quad[4][3], co[3], center[3];
+ VMesh *vm1;
+
+ n = vm0->count;
+ ns0 = vm0->seg;
+ nseg2 = nseg / 2;
+ odd = nseg % 2;
+ vm1 = new_adj_subdiv_vmesh(mem_arena, n, nseg, vm0->boundstart);
+ prev_frac = (float *)BLI_memarena_alloc(mem_arena, (ns0 + 1 ) *sizeof(float));
+ frac = (float *)BLI_memarena_alloc(mem_arena, (ns0 + 1 ) *sizeof(float));
+
+ fill_vmesh_fracs(vm0, prev_frac, n - 1);
+ fill_vmesh_fracs(vm0, frac, 0);
+ for (i = 0; i < n; i++) {
+ for (j = 0; j <= nseg2 -1 + odd; j++) {
+ for (k = 0; k <= nseg2; k++) {
+ f = (float) k / (float) nseg;
+ k0 = interp_range(frac, ns0, f, &restk);
+ f = 1.0f - (float) j / (float) nseg;
+ j0 = interp_range(prev_frac, ns0, f, &restj);
+ if (restj < BEVEL_EPSILON) {
+ j0 = ns0 - j0;
+ restj = 0.0f;
+ }
+ else {
+ j0 = ns0 - j0 - 1;
+ restj = 1.0f - restj;
+ }
+ /* Use bilinear interpolation within the source quad; could be smarter here */
+ if (restj < BEVEL_EPSILON && restk < BEVEL_EPSILON) {
+ copy_v3_v3(co, mesh_vert_canon(vm0, i, j0, k0)->co);
+ }
+ else {
+ copy_v3_v3(quad[0], mesh_vert_canon(vm0, i, j0, k0)->co);
+ copy_v3_v3(quad[1], mesh_vert_canon(vm0, i, j0, k0 + 1)->co);
+ copy_v3_v3(quad[2], mesh_vert_canon(vm0, i, j0 + 1, k0 + 1)->co);
+ copy_v3_v3(quad[3], mesh_vert_canon(vm0, i, j0 + 1, k0)->co);
+ interp_bilinear_quad_v3(quad, restk, restj, co);
+ }
+ copy_v3_v3(mesh_vert(vm1, i, j, k)->co, co);
+ }
+ }
+ }
+ if (!odd) {
+ vmesh_center(vm0, center);
+ copy_v3_v3(mesh_vert(vm1, 0, nseg2, nseg2)->co, center);
+ }
+ vmesh_copy_equiv_verts(vm1);
+ return vm1;
+}
+
+/*
+ * 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_SUBDIV pattern,
+ * then make the BMVerts and the new faces. */
+static void bevel_build_rings_subdiv(BevelParams *bp, BMesh *bm, BevVert *bv)
+{
+ int n, ns, ns2, odd, i, j, k;
+ VMesh *vm0, *vm1, *vm;
+ float coa[3], cob[3], coc[3];
+ BoundVert *v;
+ BMVert *bmv1, *bmv2, *bmv3, *bmv4;
+ BMFace *f;
+ MemArena *mem_arena = bp->mem_arena;
+ const float fullness = 0.5f;
+
+ n = bv->edgecount;
+ ns = bv->vmesh->seg;
+ ns2 = ns / 2;
+ odd = ns % 2;
+ BLI_assert(n >= 3 && ns > 1);
+
+ /* First construct an initial control mesh, with nseg==3 */
+ vm0 = new_adj_subdiv_vmesh(mem_arena, n, 3, bv->vmesh->boundstart);
+
+ for (i = 0; i < n; i++) {
+ /* Boundaries just divide input polygon edges into 3 even segments */
+ copy_v3_v3(coa, mesh_vert(bv->vmesh, i, 0, 0)->co);
+ copy_v3_v3(cob, mesh_vert(bv->vmesh, (i + 1) % n, 0, 0)->co);
+ copy_v3_v3(coc, mesh_vert(bv->vmesh, (i + n -1) % n, 0, 0)->co);
+ copy_v3_v3(mesh_vert(vm0, i, 0, 0)->co, coa);
+ interp_v3_v3v3(mesh_vert(vm0, i, 0, 1)->co, coa, cob, 1.0f / 3.0f);
+ interp_v3_v3v3(mesh_vert(vm0, i, 1, 0)->co, coa, coc, 1.0f / 3.0f);
+ interp_v3_v3v3(mesh_vert(vm0, i, 1, 1)->co, coa, bv->v->co, fullness);
+ }
+ vmesh_copy_equiv_verts(vm0);
+
+ vm1 = vm0;
+ do {
+ vm1 = quadratic_subdiv(mem_arena, vm1);
+ fix_vmesh_tangents(vm1, bv);
+ } while (vm1->seg <= ns);
+ vm1 = interp_vmesh(mem_arena, vm1, ns);
+
+ /* copy final vmesh into bv->vmesh, make BMVerts and BMFaces */
+ vm = bv->vmesh;
+ for (i = 0; i < n; i ++) {
+ for (j = 0; j <= ns2; j++) {
+ for (k = 0; k <= ns; k++) {
+ if (j == 0 && (k == 0 || k == ns))
+ continue; /* boundary corners already made */
+ if (!is_canon(vm, i, j, k))
+ continue;
+ copy_v3_v3(mesh_vert(vm, i, j, k)->co, mesh_vert(vm1, i, j, k)->co);
+ create_mesh_bmvert(bm, vm, i, j, k, bv->v);
+ }
+ }
+ }
+ vmesh_copy_equiv_verts(vm);
+ /* make the polygons */
+ v = vm->boundstart;
+ do {
+ i = v->index;
+ f = boundvert_rep_face(v);
+ /* 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,
+ * j in [0, ns2-1], k in [0, ns2-1] */
+ for (j = 0; j < ns2; j++) {
+ for (k = 0; k < ns2 + odd; k++) {
+ bmv1 = mesh_vert(vm, i, j, k)->v;
+ bmv2 = mesh_vert(vm, i, j, k + 1)->v;
+ 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);
+ bev_create_quad_tri(bm, bmv1, bmv2, bmv3, bmv4, f);
+ }
+ }
+ } while ((v = v->next) != vm->boundstart);
+
+ /* center ngon */
+ if (odd) {
+ BMVert **vv = NULL;
+ BLI_array_staticdeclare(vv, BM_DEFAULT_NGON_STACK_SIZE);
+
+ v = vm->boundstart;
+ do {
+ i = v->index;
+ BLI_array_append(vv, mesh_vert(vm, i, ns2, ns2)->v);
+ } while ((v = v->next) != vm->boundstart);
+ f = boundvert_rep_face(vm->boundstart);
+ bev_create_ngon(bm, vv, BLI_array_count(vv), f);
+
+ BLI_array_free(vv);
+ }
+}
+
static BMFace *bevel_build_poly_ex(BMesh *bm, BevVert *bv)
{
BMFace *f;
@@ -1339,24 +1619,7 @@ static void build_vmesh(BevelParams *bp, BMesh *bm, BevVert *bv)
BoundVert *v, *weld1, *weld2;
int n, ns, ns2, i, k, weld;
float *va, *vb, co[3];
-
-#ifdef USE_ALTERNATE_ADJ
- /* ordered as follows (orig, prev, center, next)*/
- float quad_plane[4][3];
- float quad_orig_a[4][3];
- float quad_orig_b[4][3];
- const int is_odd = (vm->seg % 2);
-#else
float midco[3];
-#endif
-
-#ifdef USE_ALTERNATE_ADJ
- /* the rest are initialized inline, this remains the same for all */
- /* NOTE; in this usage we only interpolate on the 'V' so cent and next points are unused (2,3)*/
- vmesh_cent(vm, quad_plane[2]);
- copy_v3_v3(quad_orig_a[2], bv->v->co);
- copy_v3_v3(quad_orig_b[2], bv->v->co);
-#endif
n = vm->count;
ns = vm->seg;
@@ -1389,59 +1652,6 @@ static void build_vmesh(BevelParams *bp, BMesh *bm, BevVert *bv)
i = v->index;
copy_mesh_vert(vm, i, 0, ns, v->next->index, 0, 0);
if (v->ebev) {
-
-#ifdef USE_ALTERNATE_ADJ
- copy_v3_v3(quad_plane[0], v->nv.co);
- mid_v3_v3v3(quad_plane[1], v->nv.co, v->prev->nv.co);
- /* quad[2] is set */
- mid_v3_v3v3(quad_plane[3], v->nv.co, v->next->nv.co);
-
- /* orig 'A' */
- copy_v3_v3(quad_orig_a[0], v->nv.co); /* only shared location between 2 quads */
- project_to_edge(v->ebev->prev->e, v->nv.co, v->prev->nv.co, quad_orig_a[1]);
- project_to_edge(v->ebev->e, v->nv.co, v->next->nv.co, quad_orig_a[3]);
-
- /* orig 'B' */
- copy_v3_v3(quad_orig_b[3], v->next->nv.co); /* only shared location between 2 quads */
- project_to_edge(v->ebev->prev->e, v->nv.co, v->prev->nv.co, quad_orig_b[1]);
- project_to_edge(v->ebev->e, v->nv.co, v->next->nv.co, quad_orig_b[0]);
-
- //bl_debug_draw_quad_add(UNPACK4(quad_plane));
- //bl_debug_draw_quad_add(UNPACK4(quad_orig_a));
- //bl_debug_draw_quad_add(UNPACK4(quad_orig_b));
-#endif /* USE_ALTERNATE_ADJ */
-
-#ifdef USE_ALTERNATE_ADJ
- for (k = 1; k < ns; k++) {
- float uv[2];
- float fac;
- float co_plane[3];
- float co_orig[3];
-
- /* quad_plane */
- get_point_uv(uv, v->ebev->seg, 0, k);
- get_point_on_round_edge(uv, quad_plane, co_plane);
-
- /* quad_orig */
- /* each half has different UV's */
- if (k <= ns2) {
- get_point_uv(uv, v->ebev->seg, 0, k);
- get_point_on_round_edge(uv, quad_orig_a, co_orig);
- }
- else {
- get_point_uv(uv, v->ebev->seg, 0, (k - ns2) - (is_odd ? 0.5f : 0.0f));
- get_point_on_round_edge(uv, quad_orig_b, co_orig);
- uv[1] = 1.0f - uv[1]; /* so we can get the factor */
- }
- fac = get_point_uv_factor(uv);
-
- /* done. interp */
- interp_v3_v3v3(co, co_plane, co_orig, fac);
- copy_v3_v3(mesh_vert(vm, i, 0, k)->co, co);
- if (!weld)
- create_mesh_bmvert(bm, vm, i, 0, k, bv->v);
- }
-#else /* USE_ALTERNATE_ADJ */
va = mesh_vert(vm, i, 0, 0)->co;
vb = mesh_vert(vm, i, 0, ns)->co;
project_to_edge(v->ebev->e, va, vb, midco);
@@ -1451,7 +1661,6 @@ static void build_vmesh(BevelParams *bp, BMesh *bm, BevVert *bv)
if (!weld)
create_mesh_bmvert(bm, vm, i, 0, k, bv->v);
}
-#endif /* !USE_ALTERNATE_ADJ */
}
} while ((v = v->next) != vm->boundstart);
@@ -1478,6 +1687,9 @@ static void build_vmesh(BevelParams *bp, BMesh *bm, BevVert *bv)
case M_ADJ:
bevel_build_rings(bm, bv);
break;
+ case M_ADJ_SUBDIV:
+ bevel_build_rings_subdiv(bp, bm, bv);
+ break;
case M_TRI_FAN:
bevel_build_trifan(bm, bv);
break;
@@ -1687,6 +1899,14 @@ static int bev_rebuild_polygon(BMesh *bm, BevelParams *bp, BMFace *f)
BLI_array_append(vv, bmv);
}
}
+ else if (bp->vertex_only && vm->mesh_kind == M_ADJ_SUBDIV && vm->seg > 1) {
+ BLI_assert(v->prev == vend);
+ i = vend->index;
+ for (k = vm->seg - 1; k > 0; k--) {
+ bmv = mesh_vert(vm, i, 0, k)->v;
+ BLI_array_append(vv, bmv);
+ }
+ }
v = v->prev;
BLI_array_append(vv, v->nv.v);
}