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.c827
1 files changed, 582 insertions, 245 deletions
diff --git a/source/blender/bmesh/tools/bmesh_bevel.c b/source/blender/bmesh/tools/bmesh_bevel.c
index fe9b0f21812..f685b741176 100644
--- a/source/blender/bmesh/tools/bmesh_bevel.c
+++ b/source/blender/bmesh/tools/bmesh_bevel.c
@@ -30,20 +30,23 @@
#include "MEM_guardedalloc.h"
+#include "DNA_object_types.h"
+#include "DNA_meshdata_types.h"
+
#include "BLI_array.h"
#include "BLI_math.h"
#include "BLI_memarena.h"
#include "BKE_customdata.h"
+#include "BKE_deform.h"
#include "bmesh.h"
+#include "./intern/bmesh_private.h"
-/* 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"
@@ -93,7 +96,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;
@@ -105,6 +108,7 @@ typedef struct BevVert {
BMVert *v; /* original mesh vertex */
int edgecount; /* total number of edges around the vertex */
int selcount; /* number of selected edges around the vertex */
+ float offset; /* offset for this vertex, if vertex_only bevel */
EdgeHalf *edges; /* array of size edgecount; CCW order from vertex normal side */
VMesh *vmesh; /* mesh structure for replacing vertex */
} BevVert;
@@ -118,11 +122,15 @@ typedef struct BevelParams {
float offset; /* blender units to offset each side of a beveled edge */
int seg; /* number of segments in beveled edge profile */
+ bool vertex_only; /* bevel vertices only */
+ bool use_weights; /* bevel amount affected by weights on edges or verts */
+ const struct MDeformVert *dvert; /* vertex group array, maybe set if vertex_only */
+ int vertex_group; /* vertex group index, maybe set if vertex_only */
} 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. */
@@ -165,6 +173,7 @@ static void create_mesh_bmvert(BMesh *bm, VMesh *vm, int i, int j, int k, BMVert
{
NewVert *nv = mesh_vert(vm, i, j, k);
nv->v = BM_vert_create(bm, nv->co, eg, 0);
+ BM_elem_flag_disable(nv->v, BM_ELEM_TAG);
}
static void copy_mesh_vert(VMesh *vm, int ito, int jto, int kto,
@@ -268,7 +277,11 @@ static BMFace *bev_create_ngon(BMesh *bm, BMVert **vert_arr, const int totv, BMF
for (i = 0; i < totv; i++) {
ee[i] = BM_edge_create(bm, vert_arr[i], vert_arr[(i + 1) % totv], NULL, BM_CREATE_NO_DOUBLE);
}
+#if 0
f = BM_face_create_ngon(bm, vert_arr[0], vert_arr[1], ee, totv, 0);
+#else
+ f = BM_face_create(bm, vert_arr, ee, totv, 0);
+#endif
}
if (facerep && f) {
int has_mdisps = CustomData_has_layer(&bm->ldata, CD_MDISPS);
@@ -316,7 +329,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 */
@@ -362,8 +375,8 @@ static void offset_meet(EdgeHalf *e1, EdgeHalf *e2, BMVert *v, BMFace *f,
}
}
-/* Like offset_meet, but here f1 and f2 must not be NULL and give the
- * planes in which to run the offset lines.
+/* Like offset_meet, but with a mid edge between them that is used
+ * to calculate the planes in which to run the offset lines.
* They may not meet exactly: the offsets for the edges may be different
* or both the planes and the lines may be angled so that they can't meet.
* In that case, pick a close point on emid, which should be the dividing
@@ -371,15 +384,13 @@ static void offset_meet(EdgeHalf *e1, EdgeHalf *e2, BMVert *v, BMFace *f,
* TODO: should have a global 'offset consistency' prepass to adjust offset
* widths so that all edges have the same offset at both ends. */
static void offset_in_two_planes(EdgeHalf *e1, EdgeHalf *e2, EdgeHalf *emid,
- BMVert *v, BMFace *f1, BMFace *f2, float meetco[3])
+ BMVert *v, float meetco[3])
{
float dir1[3], dir2[3], dirmid[3], norm_perp1[3], norm_perp2[3],
off1a[3], off1b[3], off2a[3], off2b[3], isect2[3], co[3],
f1no[3], f2no[3];
int iret;
- BLI_assert(f1 != NULL && f2 != NULL);
-
/* get direction vectors for two offset lines */
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);
@@ -402,7 +413,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);
}
@@ -414,7 +425,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);
@@ -463,7 +474,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);
}
@@ -494,79 +505,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.
@@ -586,7 +524,7 @@ static void get_point_on_round_edge(const float uv[2],
* We want M to make M*A=B where A has the left side above, as columns
* and B has the right side as columns - both extended into homogeneous coords.
* So M = B*(Ainverse). Doing Ainverse by hand gives the code below.
-*/
+ */
static int make_unit_square_map(const float va[3], const float vmid[3], const float vb[3],
float r_mat[4][4])
{
@@ -594,7 +532,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);
@@ -687,14 +625,13 @@ 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.
* Doesn't make the actual BMVerts */
-static void build_boundary(MemArena *mem_arena, BevVert *bv)
+static void build_boundary(BevelParams *bp, BevVert *bv)
{
+ MemArena *mem_arena = bp->mem_arena;
EdgeHalf *efirst, *e;
BoundVert *v;
VMesh *vm;
@@ -702,9 +639,13 @@ static void build_boundary(MemArena *mem_arena, BevVert *bv)
const float *no;
float lastd;
- e = efirst = next_bev(bv, NULL);
vm = bv->vmesh;
+ if (bp->vertex_only)
+ e = efirst = &bv->edges[0];
+ else
+ e = efirst = next_bev(bv, NULL);
+
BLI_assert(bv->edgecount >= 2); /* since bevel edges incident to 2 faces */
if (bv->edgecount == 2 && bv->selcount == 1) {
@@ -729,7 +670,7 @@ static void build_boundary(MemArena *mem_arena, BevVert *bv)
return;
}
- lastd = e->offset;
+ lastd = bp->vertex_only? bv->offset : e->offset;
vm->boundstart = NULL;
do {
if (e->is_bev) {
@@ -748,8 +689,7 @@ static void build_boundary(MemArena *mem_arena, BevVert *bv)
if (e->prev->prev->is_bev) {
BLI_assert(e->prev->prev != e); /* see: edgecount 2, selcount 1 case */
/* find meet point between e->prev->prev and e and attach e->prev there */
- offset_in_two_planes(e->prev->prev, e, e->prev, bv->v,
- e->prev->prev->fnext, e->fprev, co);
+ offset_in_two_planes(e->prev->prev, e, e->prev, bv->v, co);
v = add_new_bound_vert(mem_arena, vm, co);
v->efirst = e->prev->prev;
v->elast = v->ebev = e;
@@ -799,7 +739,10 @@ static void build_boundary(MemArena *mem_arena, 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) {
@@ -834,19 +777,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;
@@ -863,7 +793,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;
}
@@ -906,37 +836,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);
@@ -950,7 +849,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);
}
@@ -978,9 +876,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);
@@ -1029,9 +925,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);
@@ -1041,9 +935,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);
@@ -1053,9 +945,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);
@@ -1135,7 +1025,7 @@ static void bevel_build_rings(BMesh *bm, BevVert *bv)
/* Make center ngon if odd number of segments and fully beveled */
if (ns % 2 == 1 && vm->count == bv->selcount) {
BMVert **vv = NULL;
- BLI_array_declare(vv);
+ BLI_array_staticdeclare(vv, BM_DEFAULT_NGON_STACK_SIZE);
v = vm->boundstart;
do {
@@ -1153,7 +1043,7 @@ static void bevel_build_rings(BMesh *bm, BevVert *bv)
if (vm->count > bv->selcount) {
int j;
BMVert **vv = NULL;
- BLI_array_declare(vv);
+ BLI_array_staticdeclare(vv, BM_DEFAULT_NGON_STACK_SIZE);
v = vm->boundstart;
f = boundvert_rep_face(v);
@@ -1208,6 +1098,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;
@@ -1215,7 +1512,7 @@ static BMFace *bevel_build_poly_ex(BMesh *bm, BevVert *bv)
VMesh *vm = bv->vmesh;
BoundVert *v;
BMVert **vv = NULL;
- BLI_array_declare(vv);
+ BLI_array_staticdeclare(vv, BM_DEFAULT_NGON_STACK_SIZE);
v = vm->boundstart;
n = 0;
@@ -1271,7 +1568,7 @@ static void bevel_build_trifan(BMesh *bm, BevVert *bv)
else { BLI_assert(0); }
}
else {
- if (l_fan->v == v_fan) { l_fan = l_fan; }
+ if (l_fan->v == v_fan) { /* l_fan = l_fan; */ }
else if (l_fan->next->v == v_fan) { l_fan = l_fan->next; }
else if (l_fan->prev->v == v_fan) { l_fan = l_fan->prev; }
else { BLI_assert(0); }
@@ -1320,30 +1617,14 @@ static void bevel_build_quadstrip(BMesh *bm, BevVert *bv)
/* Given that the boundary is built, now make the actual BMVerts
* for the boundary and the interior of the vertex mesh. */
-static void build_vmesh(MemArena *mem_arena, BMesh *bm, BevVert *bv)
+static void build_vmesh(BevelParams *bp, BMesh *bm, BevVert *bv)
{
+ MemArena *mem_arena = bp->mem_arena;
VMesh *vm = bv->vmesh;
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;
@@ -1376,69 +1657,21 @@ static void build_vmesh(MemArena *mem_arena, 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);
+ if (bv->edgecount == 3 && bv->selcount == 1) {
+ /* special case: profile cuts the third face, so line it up with that */
+ copy_v3_v3(midco, bv->v->co);
+ }
+ else {
+ project_to_edge(v->ebev->e, va, vb, midco);
+ }
for (k = 1; k < ns; k++) {
get_point_on_round_edge(v->ebev, k, va, midco, vb, co);
copy_v3_v3(mesh_vert(vm, i, 0, k)->co, co);
if (!weld)
create_mesh_bmvert(bm, vm, i, 0, k, bv->v);
}
-#endif /* !USE_ALTERNATE_ADJ */
}
} while ((v = v->next) != vm->boundstart);
@@ -1465,6 +1698,9 @@ static void build_vmesh(MemArena *mem_arena, 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;
@@ -1475,9 +1711,9 @@ static void build_vmesh(MemArena *mem_arena, BMesh *bm, BevVert *bv)
}
/* take care, this flag isn't cleared before use, it just so happens that its not set */
-#define BM_BEVEL_EDGE_TAG_ENABLE(bme) BM_elem_flag_enable( (bme)->l, BM_ELEM_TAG)
-#define BM_BEVEL_EDGE_TAG_DISABLE(bme) BM_elem_flag_disable( (bme)->l, BM_ELEM_TAG)
-#define BM_BEVEL_EDGE_TAG_TEST(bme) BM_elem_flag_test( (bme)->l, BM_ELEM_TAG)
+#define BM_BEVEL_EDGE_TAG_ENABLE(bme) BM_ELEM_API_FLAG_ENABLE( (bme), _FLAG_OVERLAP)
+#define BM_BEVEL_EDGE_TAG_DISABLE(bme) BM_ELEM_API_FLAG_DISABLE( (bme), _FLAG_OVERLAP)
+#define BM_BEVEL_EDGE_TAG_TEST(bme) BM_ELEM_API_FLAG_TEST( (bme), _FLAG_OVERLAP)
/*
* Construction around the vertex
@@ -1486,10 +1722,11 @@ static void bevel_vert_construct(BMesh *bm, BevelParams *bp, BMVert *v)
{
BMEdge *bme;
BevVert *bv;
- BMEdge *bme2, *unflagged_bme;
+ BMEdge *bme2, *unflagged_bme, *first_bme;
BMFace *f;
BMIter iter, iter2;
EdgeHalf *e;
+ float weight;
int i, found_shared_face, ccw_test_sum;
int nsel = 0;
int ntot = 0;
@@ -1498,15 +1735,23 @@ static void bevel_vert_construct(BMesh *bm, BevelParams *bp, BMVert *v)
* Only bevel selected edges that have exactly two incident faces.
*/
+ if (bp->vertex_only)
+ first_bme = v->e;
+ else
+ first_bme = NULL;
BM_ITER_ELEM (bme, &iter, v, BM_EDGES_OF_VERT) {
- if (BM_elem_flag_test(bme, BM_ELEM_TAG)) {
+ if (BM_elem_flag_test(bme, BM_ELEM_TAG) && !bp->vertex_only) {
BLI_assert(BM_edge_is_manifold(bme));
nsel++;
+ if (!first_bme)
+ first_bme = bme;
}
ntot++;
+
+ BM_BEVEL_EDGE_TAG_DISABLE(bme);
}
- if (nsel == 0) {
+ if ((nsel == 0 && !bp->vertex_only) || (ntot < 3 && bp->vertex_only)) {
/* signal this vert isn't being beveled */
BM_elem_flag_disable(v, BM_ELEM_TAG);
return;
@@ -1520,15 +1765,29 @@ static void bevel_vert_construct(BMesh *bm, BevelParams *bp, BMVert *v)
bv->v = v;
bv->edgecount = ntot;
bv->selcount = nsel;
+ bv->offset = bp->offset;
bv->edges = (EdgeHalf *)BLI_memarena_alloc(bp->mem_arena, ntot * sizeof(EdgeHalf));
bv->vmesh = (VMesh *)BLI_memarena_alloc(bp->mem_arena, sizeof(VMesh));
bv->vmesh->seg = bp->seg;
BLI_ghash_insert(bp->vert_hash, v, bv);
+ if (bp->vertex_only) {
+ /* if weighted, modify offset by weight */
+ if (bp->dvert != NULL && bp->vertex_group != -1) {
+ weight = defvert_find_weight(bp->dvert + BM_elem_index_get(v), bp->vertex_group);
+ if (weight <= 0.0f) {
+ BM_elem_flag_disable(v, BM_ELEM_TAG);
+ return;
+ }
+ bv->offset *= weight;
+ }
+ }
+
/* add edges to bv->edges in order that keeps adjacent edges sharing
* a face, if possible */
i = 0;
- bme = v->e;
+
+ bme = first_bme;
BM_BEVEL_EDGE_TAG_ENABLE(bme);
e = &bv->edges[0];
e->e = bme;
@@ -1542,6 +1801,8 @@ static void bevel_vert_construct(BMesh *bm, BevelParams *bp, BMVert *v)
continue;
if (!unflagged_bme)
unflagged_bme = bme2;
+ if (!bme->l)
+ continue;
BM_ITER_ELEM (f, &iter2, bme2, BM_FACES_OF_EDGE) {
if (BM_face_edge_share_loop(f, bme)) {
found_shared_face = 1;
@@ -1563,7 +1824,7 @@ static void bevel_vert_construct(BMesh *bm, BevelParams *bp, BMVert *v)
}
bme = e->e;
BM_BEVEL_EDGE_TAG_ENABLE(bme);
- if (BM_elem_flag_test(bme, BM_ELEM_TAG)) {
+ if (BM_elem_flag_test(bme, BM_ELEM_TAG) && !bp->vertex_only) {
e->is_bev = TRUE;
e->seg = bp->seg;
}
@@ -1572,11 +1833,20 @@ static void bevel_vert_construct(BMesh *bm, BevelParams *bp, BMVert *v)
e->seg = 0;
}
e->is_rev = (bme->v2 == v);
- e->offset = e->is_bev ? bp->offset : 0.0f;
+ if (e->is_bev) {
+ e->offset = bp->offset;
+ if (bp->use_weights) {
+ weight = BM_elem_float_data_get(&bm->edata, bme, CD_BWEIGHT);
+ e->offset *= weight;
+ }
+ }
+ else {
+ e->offset = 0.0f;
+ }
}
/* find wrap-around shared face */
BM_ITER_ELEM (f, &iter2, bme, BM_FACES_OF_EDGE) {
- if (BM_face_edge_share_loop(f, bv->edges[0].e)) {
+ if (bv->edges[0].e->l && BM_face_edge_share_loop(f, bv->edges[0].e)) {
if (bv->edges[0].fnext == f)
continue; /* if two shared faces, want the other one now */
bv->edges[ntot - 1].fnext = f;
@@ -1619,8 +1889,8 @@ static void bevel_vert_construct(BMesh *bm, BevelParams *bp, BMVert *v)
BM_BEVEL_EDGE_TAG_DISABLE(e->e);
}
- build_boundary(bp->mem_arena, bv);
- build_vmesh(bp->mem_arena, bm, bv);
+ build_boundary(bp, bv);
+ build_vmesh(bp, bm, bv);
}
/* Face f has at least one beveled vertex. Rebuild f */
@@ -1663,6 +1933,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);
}
@@ -1774,6 +2052,51 @@ static void bevel_build_edge_polygons(BMesh *bm, BevelParams *bp, BMEdge *bme)
}
}
+/*
+ * Calculate and return an offset that is the lesser of the current
+ * bp.offset and the maximum possible offset before geometry
+ * collisions happen.
+ * Currently this is a quick and dirty estimate of the max
+ * possible: half the minimum edge length of any vertex involved
+ * in a bevel. This is usually conservative.
+ * The correct calculation is quite complicated.
+ * TODO: implement this correctly.
+ */
+static float bevel_limit_offset(BMesh *bm, BevelParams *bp)
+{
+ BMVert *v;
+ BMEdge *e;
+ BMIter v_iter, e_iter;
+ float limited_offset, half_elen;
+ bool vbeveled;
+
+ limited_offset = bp->offset;
+ BM_ITER_MESH(v, &v_iter, bm, BM_VERTS_OF_MESH) {
+ if (BM_elem_flag_test(v, BM_ELEM_TAG)) {
+ if (bp->vertex_only) {
+ vbeveled = true;
+ }
+ else {
+ vbeveled = false;
+ BM_ITER_ELEM(e, &e_iter, v, BM_EDGES_OF_VERT) {
+ if (BM_elem_flag_test(BM_edge_other_vert(e, v), BM_ELEM_TAG)) {
+ vbeveled = true;
+ break;
+ }
+ }
+ }
+ if (vbeveled) {
+ BM_ITER_ELEM(e, &e_iter, v, BM_EDGES_OF_VERT) {
+ half_elen = 0.5f * BM_edge_calc_length(e);
+ if (half_elen < limited_offset)
+ limited_offset = half_elen;
+ }
+ }
+ }
+ }
+ return limited_offset;
+}
+
/**
* - Currently only bevels BM_ELEM_TAG'd verts and edges.
*
@@ -1781,9 +2104,14 @@ static void bevel_build_edge_polygons(BMesh *bm, BevelParams *bp, BMEdge *bme)
* the caller needs to ensure this is cleared before calling
* if its going to use this face tag.
*
+ * - If limit_offset is set, adjusts offset down if necessary
+ * to avoid geometry collisions.
+ *
* \warning all tagged edges _must_ be manifold.
*/
-void BM_mesh_bevel(BMesh *bm, const float offset, const float segments)
+void BM_mesh_bevel(BMesh *bm, const float offset, const float segments,
+ const bool vertex_only, const bool use_weights, const bool limit_offset,
+ const struct MDeformVert *dvert, const int vertex_group)
{
BMIter iter;
BMVert *v;
@@ -1792,6 +2120,10 @@ void BM_mesh_bevel(BMesh *bm, const float offset, const float segments)
bp.offset = offset;
bp.seg = segments;
+ bp.vertex_only = vertex_only;
+ bp.use_weights = use_weights;
+ bp.dvert = dvert;
+ bp.vertex_group = vertex_group;
if (bp.offset > 0) {
/* primary alloc */
@@ -1799,6 +2131,9 @@ void BM_mesh_bevel(BMesh *bm, const float offset, const float segments)
bp.mem_arena = BLI_memarena_new((1 << 16), __func__);
BLI_memarena_use_calloc(bp.mem_arena);
+ if (limit_offset)
+ bp.offset = bevel_limit_offset(bm, &bp);
+
/* The analysis of the input vertices and execution additional constructions */
BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) {
if (BM_elem_flag_test(v, BM_ELEM_TAG)) {
@@ -1807,9 +2142,11 @@ void BM_mesh_bevel(BMesh *bm, const float offset, const float segments)
}
/* Build polygons for edges */
- BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) {
- if (BM_elem_flag_test(e, BM_ELEM_TAG)) {
- bevel_build_edge_polygons(bm, &bp, e);
+ if (!bp.vertex_only) {
+ BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) {
+ if (BM_elem_flag_test(e, BM_ELEM_TAG)) {
+ bevel_build_edge_polygons(bm, &bp, e);
+ }
}
}