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:
authorHoward Trickey <howard.trickey@gmail.com>2014-01-08 16:40:01 +0400
committerHoward Trickey <howard.trickey@gmail.com>2014-01-08 16:40:01 +0400
commit49aa701645e97ea7a6f698e4063a5327f6c79815 (patch)
tree3469853a0ec2ca2c58c4c50184d05d66ff978d85 /source/blender/bmesh
parent8094ac91944f080c53943c8319eeaa9c3d1b9720 (diff)
Add profile control parameter to Bevel.
Parameter controls concavity / convexity. <.25 means: concave inward .25 means: straight slanted >.25 means: concave outward .5 means: circular (the default) 1 means: straight along original sides For now, there is a hard lower limit of .15 because more work is needed to get decent results in the range below that. The profile is actually a superellipse, and the parameter is 1/4 of the exponent in the implicit equation for a superellipse, except at the extreme values of 0 and 1.
Diffstat (limited to 'source/blender/bmesh')
-rw-r--r--source/blender/bmesh/intern/bmesh_opdefines.c1
-rw-r--r--source/blender/bmesh/operators/bmo_bevel.c3
-rw-r--r--source/blender/bmesh/tools/bmesh_bevel.c192
-rw-r--r--source/blender/bmesh/tools/bmesh_bevel.h4
4 files changed, 159 insertions, 41 deletions
diff --git a/source/blender/bmesh/intern/bmesh_opdefines.c b/source/blender/bmesh/intern/bmesh_opdefines.c
index 97624ec64bb..b4697935da5 100644
--- a/source/blender/bmesh/intern/bmesh_opdefines.c
+++ b/source/blender/bmesh/intern/bmesh_opdefines.c
@@ -1559,6 +1559,7 @@ static BMOpDefine bmo_bevel_def = {
{"offset", BMO_OP_SLOT_FLT}, /* amount to offset beveled edge */
{"offset_type", BMO_OP_SLOT_INT}, /* how to measure offset (enum) */
{"segments", BMO_OP_SLOT_INT}, /* number of segments in bevel */
+ {"profile", BMO_OP_SLOT_FLT}, /* profile shape, 0->1 (.5=>round) */
{"vertex_only", BMO_OP_SLOT_BOOL}, /* only bevel vertices, not edges */
{{'\0'}},
},
diff --git a/source/blender/bmesh/operators/bmo_bevel.c b/source/blender/bmesh/operators/bmo_bevel.c
index 4eec15d4d7e..07a2e674863 100644
--- a/source/blender/bmesh/operators/bmo_bevel.c
+++ b/source/blender/bmesh/operators/bmo_bevel.c
@@ -39,6 +39,7 @@ void bmo_bevel_exec(BMesh *bm, BMOperator *op)
const int offset_type = BMO_slot_int_get(op->slots_in, "offset_type");
const int seg = BMO_slot_int_get(op->slots_in, "segments");
const bool vonly = BMO_slot_bool_get(op->slots_in, "vertex_only");
+ const float profile = BMO_slot_float_get(op->slots_in, "profile");
if (offset > 0) {
BMOIter siter;
@@ -59,7 +60,7 @@ void bmo_bevel_exec(BMesh *bm, BMOperator *op)
}
}
- BM_mesh_bevel(bm, offset, offset_type, seg, vonly, false, false, NULL, -1);
+ BM_mesh_bevel(bm, offset, offset_type, seg, profile, vonly, false, false, NULL, -1);
BMO_slot_buffer_from_enabled_hflag(bm, op, op->slots_out, "faces.out", BM_FACE, BM_ELEM_TAG);
}
diff --git a/source/blender/bmesh/tools/bmesh_bevel.c b/source/blender/bmesh/tools/bmesh_bevel.c
index 984632d807d..4a6032a1281 100644
--- a/source/blender/bmesh/tools/bmesh_bevel.c
+++ b/source/blender/bmesh/tools/bmesh_bevel.c
@@ -89,14 +89,20 @@ typedef struct EdgeHalf {
} EdgeHalf;
/* Profile specification.
- * For now, only have round profiles and straight profiles, so only need midpoint.
+ * Many interesting profiles are in family of superellipses:
+ * (abs(x/a))^r + abs(y/b))^r = 1
+ * r==2 => ellipse; r==1 => line; r < 1 => concave; r > 1 => bulging out.
+ * Special cases: let r==0 mean straight-inward, and r==4 mean straight outward
* The start and end points of the profile are stored separately.
- * TODO: generalize to superellipse profiles.
*/
typedef struct Profile {
- bool flat;
+ float super_r; /* superellipse r parameter */
float midco[3]; /* mid control point for profile */
} Profile;
+#define PRO_SQUARE_R 4.0f
+#define PRO_CIRCLE_R 2.0f
+#define PRO_LINE_R 1.0f
+#define PRO_SQUARE_IN_R 0.0f
/* An element in a cyclic boundary of a Vertex Mesh (VMesh) */
typedef struct BoundVert {
@@ -150,6 +156,7 @@ typedef struct BevelParams {
float offset; /* blender units to offset each side of a beveled edge */
int offset_type; /* how offset is measured; enum defined in bmesh_operators.h */
int seg; /* number of segments in beveled edge profile */
+ float pro_super_r; /* superellipse parameter for edge profile */
bool vertex_only; /* bevel vertices only */
bool use_weights; /* bevel amount affected by weights on edges or verts */
bool preserve_widths; /* should bevel prefer widths over angles, if forced to choose? */
@@ -182,7 +189,7 @@ static BoundVert *add_new_bound_vert(MemArena *mem_arena, VMesh *vm, const float
tail->next = ans;
vm->boundstart->prev = ans;
}
- ans->profile.flat = true;
+ ans->profile.super_r = PRO_LINE_R;
vm->count++;
return ans;
}
@@ -825,13 +832,13 @@ static void project_to_edge(BMEdge *e, const float co_a[3], const float co_b[3],
/* If there is a bndv->ebev edge, find the mid control point if necessary.
* It is the closest point on the beveled edge to the line segment between
* bndv and bndv->next. */
-static void set_profile_params(BoundVert *bndv)
+static void set_profile_params(BevelParams *bp, BoundVert *bndv)
{
EdgeHalf *e;
e = bndv->ebev;
if (e) {
- bndv->profile.flat = false;
+ bndv->profile.super_r = bp->pro_super_r;
project_to_edge(e->e, bndv->nv.co, bndv->next->nv.co,
bndv->profile.midco);
}
@@ -1001,23 +1008,46 @@ static void get_point_on_round_edge(EdgeHalf *e, int k,
/* Find the point on given profile at parameter u which goes from 0 to 2 as
* the profile is moved from va to vb. */
static void get_profile_point(const Profile *pro, const float va[3], const float vb[3], float u, float r_co[3])
- {
- float p[3], angle;
+{
+ float p[3], vo[3], angle, r, w;
float m[4][4];
if (u <= 0.0f)
copy_v3_v3(r_co, va);
else if (u >= 2.0f)
copy_v3_v3(r_co, vb);
- else if (pro->flat || !make_unit_square_map(va, pro->midco, vb, m)) {
- interp_v3_v3v3(r_co, va, vb, u / 2.0f);
- }
else {
- angle = u * (float)M_PI / 4.0f; /* angle from y axis */
- p[0] = sinf(angle);
- p[1] = cosf(angle);
- p[2] = 0.0f;
- mul_v3_m4v3(r_co, m, p);
+ r = pro->super_r;
+ if (r == 1.0f || !make_unit_square_map(va, pro->midco, vb, m)) {
+ interp_v3_v3v3(r_co, va, vb, u / 2.0f);
+ }
+ else if (r == PRO_SQUARE_IN_R) {
+ /* square inward concave */
+ zero_v3(p);
+ mul_v3_m4v3(vo, m, p);
+ if (u <= 1.0f)
+ interp_v3_v3v3(r_co, va, vo, u);
+ else
+ interp_v3_v3v3(r_co, vo, vb, u - 1.0f);
+ }
+ else if (r >= PRO_SQUARE_R) {
+ /* square outward convex */
+ if (u <= 1.0f)
+ interp_v3_v3v3(r_co, va, pro->midco, u);
+ else
+ interp_v3_v3v3(r_co, pro->midco, vb, u - 1.0f);
+ }
+ else {
+ angle = u * (float)M_PI / 4.0f; /* angle from y axis */
+ p[0] = sinf(angle);
+ p[1] = cosf(angle);
+ p[2] = 0.0f;
+ if (r != PRO_CIRCLE_R) {
+ w = powf(powf(p[0], r) + pow(p[1], r), -1.0f / r);
+ mul_v2_fl(p, w);
+ }
+ mul_v3_m4v3(r_co, m, p);
+ }
}
}
@@ -1065,11 +1095,43 @@ static void snap_to_edge_profile(EdgeHalf *e, const float va[3], const float vb[
}
#endif
-/* Snapping a direction co to a unit radius sphere is just normalizing co.
- * TODO: generalize to superellipsoid */
-static void snap_to_sphere(float co[3])
+/* Snap a direction co to a superellipsoid with parameter super_r */
+static void snap_to_superellipsoid(float co[3], const float super_r)
{
- normalize_v3(co);
+ float a, b, c, x, y, z, r, rinv;
+
+ r = super_r;
+ if (r == PRO_CIRCLE_R) {
+ normalize_v3(co);
+ return;
+ }
+
+ x = a = max_ff(0.0f, co[0]);
+ y = b = max_ff(0.0f, co[1]);
+ z = c = max_ff(0.0f, co[2]);
+ if (r <= 0.0f)
+ r = 0.1f;
+ rinv = 1.0f / r;
+ if (a == 0.0f) {
+ if (b == 0.0f) {
+ x = 0.0f;
+ y = 0.0f;
+ z = powf(c, rinv);
+ }
+ else {
+ x = 0.0f;
+ y = powf(1.0f / (1.0f + powf(c / b, r)), rinv);
+ z = c * y / b;
+ }
+ }
+ else {
+ x = powf(1.0f / (1.0f + powf(b / a, r) + powf(c / a, r)), rinv);
+ y = b * x / a;
+ z = c * x / a;
+ }
+ co[0] = x;
+ co[1] = y;
+ co[2] = z;
}
static void snap_to_profile(BoundVert *bndv, EdgeHalf *e, float co[3])
@@ -1087,14 +1149,14 @@ static void snap_to_profile(BoundVert *bndv, EdgeHalf *e, float co[3])
closest_to_plane_v3(vb0, plane, vb);
closest_to_plane_v3(vmid0, plane, bndv->profile.midco);
if (make_unit_square_map(va0, vmid0, vb0, m)) {
- /* Transform co and project it onto sphere */
+ /* Transform co and project it onto superellipse */
if (!invert_m4_m4(minv, m)) {
/* shouldn't happen */
BLI_assert(!"failed inverse during profile snap");
return;
}
mul_v3_m4v3(p, minv, co);
- snap_to_sphere(p);
+ snap_to_superellipsoid(p, bndv->profile.super_r);
mul_v3_m4v3(snap, m, p);
copy_v3_v3(co, snap);
}
@@ -1207,7 +1269,7 @@ static void build_boundary(BevelParams *bp, BevVert *bv, bool construct)
else {
adjust_bound_vert(e->next->leftv, co);
}
- set_profile_params(vm->boundstart);
+ set_profile_params(bp, vm->boundstart);
return;
}
@@ -1311,7 +1373,7 @@ static void build_boundary(BevelParams *bp, BevVert *bv, bool construct)
v = vm->boundstart;
do {
- set_profile_params(v);
+ set_profile_params(bp, v);
} while ((v = v->next) != vm->boundstart);
if (bv->selcount == 1 && bv->edgecount == 3) {
@@ -2247,12 +2309,44 @@ static VMesh *cubic_subdiv(MemArena *mem_arena, VMesh *vm0)
return vm1;
}
+/* Special case for cube corner, when r is PRO_SQUARE_R,
+ * meaning straight sides */
+static VMesh *make_cube_corner_straight(MemArena *mem_arena, int nseg)
+{
+ VMesh *vm;
+ float co[3];
+ int i, j, k, ns2;
+
+ ns2 = nseg / 2;
+ vm = new_adj_subdiv_vmesh(mem_arena, 3, nseg, NULL);
+ vm->count = 0; // reset, so following loop will end up with correct count
+ for (i = 0; i < 3; i++) {
+ zero_v3(co);
+ co[i] = 1.0f;
+ add_new_bound_vert(mem_arena, vm, co);
+ }
+ for (i = 0; i < 3; i++) {
+ for (j = 0; j <= ns2; j++) {
+ for (k = 0; k <= ns2; k++) {
+ if (!is_canon(vm, i, j, k))
+ continue;
+ co[i] = 1.0f;
+ co[(i + 1) % 3] = (float) k * 2.0 / (float) nseg;
+ co[(i + 2) % 3] = (float) j * 2.0 / (float) nseg;
+ copy_v3_v3(mesh_vert(vm, i, j, k)->co, co);
+ }
+ }
+ }
+ vmesh_copy_equiv_verts(vm);
+ return vm;
+}
+
/* Make a VMesh with nseg segments that covers the unit radius sphere octant
* with center at (0,0,0).
* This has BoundVerts at (1,0,0), (0,1,0) and (0,0,1), with quarter circle arcs
* on the faces for the orthogonal planes through the origin.
*/
-static VMesh *make_cube_corner_adj_vmesh(MemArena *mem_arena, int nseg)
+static VMesh *make_cube_corner_adj_vmesh(MemArena *mem_arena, int nseg, float r)
{
VMesh *vm0, *vm1;
BoundVert *bndv;
@@ -2260,6 +2354,9 @@ static VMesh *make_cube_corner_adj_vmesh(MemArena *mem_arena, int nseg)
float co[3], coa[3], cob[3], coc[3];
float w;
+ if (r == PRO_SQUARE_R)
+ return make_cube_corner_straight(mem_arena, nseg);
+
/* initial mesh has 3 sides, 2 segments */
vm0 = new_adj_subdiv_vmesh(mem_arena, 3, 2, NULL);
vm0->count = 0; // reset, so following loop will end up with correct count
@@ -2276,6 +2373,7 @@ static VMesh *make_cube_corner_adj_vmesh(MemArena *mem_arena, int nseg)
coc[i] = 1.0f;
coc[(i + 1) % 3] = 1.0f;
coc[(i + 2) % 3] = 0.0f;
+ bndv->profile.super_r = r;
copy_v3_v3(bndv->profile.midco, coc);
copy_v3_v3(mesh_vert(vm0, i, 0, 0)->co, coa);
get_profile_point(&bndv->profile, coa, cob, 1.0f, co);
@@ -2289,7 +2387,10 @@ static VMesh *make_cube_corner_adj_vmesh(MemArena *mem_arena, int nseg)
co[1] = w;
co[2] = w;
if (nseg > 2) {
- mul_v3_fl(co, 1.4f);
+ if (r > 1.5f)
+ mul_v3_fl(co, 1.4f);
+ else if (r < 0.75f)
+ mul_v3_fl(co, 0.6f);
}
copy_v3_v3(mesh_vert(vm0, 0, 1, 1)->co, co);
@@ -2307,7 +2408,7 @@ static VMesh *make_cube_corner_adj_vmesh(MemArena *mem_arena, int nseg)
for (i = 0; i < 3; i++) {
for (j = 0; j <= ns2; j++) {
for (k = 0; k <= nseg; k++) {
- snap_to_sphere(mesh_vert(vm1, i, j, k)->co);
+ snap_to_superellipsoid(mesh_vert(vm1, i, j, k)->co, r);
}
}
}
@@ -2315,7 +2416,7 @@ static VMesh *make_cube_corner_adj_vmesh(MemArena *mem_arena, int nseg)
}
/* Is this a good candidate for using tri_corner_adj_vmesh? */
-static bool tri_corner_test(BevVert *bv)
+static bool tri_corner_test(BevelParams *bp, BevVert *bv)
{
float ang, totang, angdiff;
EdgeHalf *e;
@@ -2332,7 +2433,8 @@ static bool tri_corner_test(BevVert *bv)
totang += ang;
}
angdiff = fabsf(totang - 3.0f * (float)M_PI_2);
- if (angdiff > (float)M_PI_4)
+ if ((bp->pro_super_r == PRO_SQUARE_R && angdiff > (float)M_PI/16.0f) ||
+ angdiff > (float)M_PI_4)
return false;
return true;
}
@@ -2355,7 +2457,7 @@ static VMesh *tri_corner_adj_vmesh(BevelParams *bp, BevVert *bv)
make_unit_cube_map(co0, co1, co2, bv->v->co, mat);
ns = bp->seg;
ns2 = ns / 2;
- vm = make_cube_corner_adj_vmesh(bp->mem_arena, bp->seg);
+ vm = make_cube_corner_adj_vmesh(bp->mem_arena, bp->seg, bp->pro_super_r);
for (i = 0; i < 3; i++) {
for (j = 0; j <= ns2; j++) {
for (k = 0; k <= ns; k++) {
@@ -2377,7 +2479,7 @@ static VMesh *adj_vmesh(BevelParams *bp, BevVert *bv)
float co[3], coa[3], cob[3], dir[3];
BoundVert *bndv;
MemArena *mem_arena = bp->mem_arena;
- float fac, fullness;
+ float r, fac, fullness;
/* First construct an initial control mesh, with nseg==2 */
n = bv->vmesh->count;
@@ -2404,11 +2506,21 @@ static VMesh *adj_vmesh(BevelParams *bp, BevVert *bv)
mul_v3_fl(co, 1.0f / (float)n);
sub_v3_v3v3(cob, co, coa);
add_v3_v3(cob, co);
- if (bp->vertex_only)
- fac = 0.25f;
- else
- fac = 0.5f;
- fullness = 1.0f - fac / 2.0f;
+ r = bp->pro_super_r;
+ if (r == 1.0)
+ fullness = 0.0f;
+ else if (r > 1.0) {
+ if (bp->vertex_only)
+ fac = 0.25f;
+ else if (r == PRO_SQUARE_R)
+ fac = -2.0;
+ else
+ fac = 0.5f;
+ fullness = 1.0f - fac / r;
+ }
+ else {
+ fullness = r - 1.0f;
+ }
sub_v3_v3v3(dir, coa, co);
if (len_squared_v3(dir) > BEVEL_EPSILON_SQ)
madd_v3_v3fl(co, dir, fullness);
@@ -2478,7 +2590,7 @@ static void bevel_build_rings_subdiv(BevelParams *bp, BMesh *bm, BevVert *bv)
if (epipe)
vm1 = pipe_adj_vmesh(bp, bv, epipe);
- else if (tri_corner_test(bv))
+ else if (tri_corner_test(bp, bv))
vm1 = tri_corner_adj_vmesh(bp, bv);
else
vm1 = adj_vmesh(bp, bv);
@@ -3293,7 +3405,7 @@ static float bevel_limit_offset(BMesh *bm, BevelParams *bp)
* \warning all tagged edges _must_ be manifold.
*/
void BM_mesh_bevel(BMesh *bm, const float offset, const int offset_type,
- const float segments,
+ const float segments, const float profile,
const bool vertex_only, const bool use_weights, const bool limit_offset,
const struct MDeformVert *dvert, const int vertex_group)
{
@@ -3306,6 +3418,7 @@ void BM_mesh_bevel(BMesh *bm, const float offset, const int offset_type,
bp.offset = offset;
bp.offset_type = offset_type;
bp.seg = segments;
+ bp.pro_super_r = 4.0f * profile; /* convert to superellipse exponent */
bp.vertex_only = vertex_only;
bp.use_weights = use_weights;
bp.preserve_widths = false;
@@ -3313,6 +3426,9 @@ void BM_mesh_bevel(BMesh *bm, const float offset, const int offset_type,
bp.dvert = dvert;
bp.vertex_group = vertex_group;
+ if (bp.pro_super_r < 0.15f)
+ bp.pro_super_r = 0.15f; /* TODO: implement 0 case properly */
+
if (bp.offset > 0) {
/* primary alloc */
bp.vert_hash = BLI_ghash_ptr_new(__func__);
diff --git a/source/blender/bmesh/tools/bmesh_bevel.h b/source/blender/bmesh/tools/bmesh_bevel.h
index 9897b6b070c..0c088ce0238 100644
--- a/source/blender/bmesh/tools/bmesh_bevel.h
+++ b/source/blender/bmesh/tools/bmesh_bevel.h
@@ -30,7 +30,7 @@
struct MDeformVert;
void BM_mesh_bevel(BMesh *bm, const float offset, const int offset_type, const float segments,
- const bool vertex_only, const bool use_weights, const bool limit_offset,
- const struct MDeformVert *dvert, const int vertex_group);
+ const float profile, const bool vertex_only, const bool use_weights,
+ const bool limit_offset, const struct MDeformVert *dvert, const int vertex_group);
#endif /* __BMESH_BEVEL_H__ */