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:
authorHans Goudey <h.goudey@me.com>2019-11-21 00:12:32 +0300
committerHans Goudey <h.goudey@me.com>2019-11-21 00:25:28 +0300
commitba1e9ae4733ae956331c7e8899f6939997205298 (patch)
tree007362ed2c9ee4564b67404f552906d6e66848db /source/blender/bmesh/tools
parent8c6ce742391b2b8798143a4a2c2224ebbeb7f1ec (diff)
Bevel: Custom Profile and CurveProfile Widget
Custom profiles in bevel allows the profile curve to be controlled by manually placed control points. Orientation is regularized along groups of edges, and the 'pipe case' is updated. This commit includes many updates to comments and changed variable names as well. A 'cutoff' vertex mesh method is added to bevel in addition to the existing grid fill option for replacing vertices. The UI of the bevel modifier and tool are updated and unified. Also, a 'CurveProfile' widget is added to BKE for defining the profile in the interface, which may be useful in other situations. Many thanks to Howard, my mentor for this GSoC project. Reviewers: howardt, campbellbarton Differential Revision: https://developer.blender.org/D5516
Diffstat (limited to 'source/blender/bmesh/tools')
-rw-r--r--source/blender/bmesh/tools/bmesh_bevel.c2011
-rw-r--r--source/blender/bmesh/tools/bmesh_bevel.h7
2 files changed, 1393 insertions, 625 deletions
diff --git a/source/blender/bmesh/tools/bmesh_bevel.c b/source/blender/bmesh/tools/bmesh_bevel.c
index 797e2ca864e..6989946147e 100644
--- a/source/blender/bmesh/tools/bmesh_bevel.c
+++ b/source/blender/bmesh/tools/bmesh_bevel.c
@@ -38,6 +38,9 @@
#include "eigen_capi.h"
+#include "BKE_curveprofile.h"
+#include "DNA_curveprofile_types.h"
+
#include "bmesh.h"
#include "bmesh_bevel.h" /* own include */
@@ -50,10 +53,22 @@
#define BEVEL_EPSILON_BIG_SQ 1e-8f
#define BEVEL_EPSILON_ANG DEG2RADF(2.0f)
#define BEVEL_SMALL_ANG DEG2RADF(10.0f)
+/** Difference in dot products that corresponds to 10 degree difference between vectors. */
+#define BEVEL_SMALL_ANG_DOT 1 - cosf(BEVEL_SMALL_ANG)
#define BEVEL_MAX_ADJUST_PCT 10.0f
#define BEVEL_MAX_AUTO_ADJUST_PCT 300.0f
#define BEVEL_MATCH_SPEC_WEIGHT 0.2
+//#define DEBUG_CUSTOM_PROFILE_CUTOFF
+//#define DEBUG_CUSTOM_PROFILE_SAMPLE
+
+#if defined(DEBUG_PROFILE_ORIENTATION_DRAW) || defined(DEBUG_CUSTOM_PROFILE_PIPE)
+static float debug_color_red[4] = {1.0f, 0.0f, 0.0f, 1.0f};
+static float debug_color_blue[4] = {0.0f, 0.0f, 1.0f, 1.0f};
+extern void DRW_debug_sphere(const float center[3], const float radius, const float color[4]);
+extern void DRW_debug_line_v3v3(const float v1[3], const float v2[3], const float color[4]);
+#endif
+
/* happens far too often, uncomment for development */
// #define BEVEL_ASSERT_PROJECT
@@ -64,72 +79,108 @@
typedef struct NewVert {
BMVert *v;
float co[3];
- // int _pad;
+ char _pad[4];
} NewVert;
struct BoundVert;
/* Data for one end of an edge involved in a bevel */
typedef struct EdgeHalf {
- struct EdgeHalf *next, *prev; /* in CCW order */
- BMEdge *e; /* original mesh edge */
- BMFace *fprev; /* face between this edge and previous, if any */
- BMFace *fnext; /* face between this edge and next, if any */
- struct BoundVert *leftv; /* left boundary vert (looking along edge to end) */
- struct BoundVert *rightv; /* right boundary vert, if beveled */
- int profile_index; /* offset into profile to attach non-beveled edge */
- int seg; /* how many segments for the bevel */
- float offset_l; /* offset for this edge, on left side */
- float offset_r; /* offset for this edge, on right side */
- float offset_l_spec; /* user specification for offset_l */
- float offset_r_spec; /* user specification for offset_r */
- bool is_bev; /* is this edge beveled? */
- bool is_rev; /* is e->v2 the vertex at this end? */
- bool is_seam; /* is e a seam for custom loopdata (e.g., UVs)? */
- // int _pad;
+ /** Other EdgeHalves connected to the same BevVert, in CCW order. */
+ struct EdgeHalf *next, *prev;
+ /** Original mesh edge */
+ BMEdge *e;
+ /** Face between this edge and previous, if any */
+ BMFace *fprev;
+ /** Face between this edge and next, if any */
+ BMFace *fnext;
+ /** Left boundary vert (looking along edge to end) */
+ struct BoundVert *leftv;
+ /** Right boundary vert, if beveled */
+ struct BoundVert *rightv;
+ /** Offset into profile to attach non-beveled edge */
+ int profile_index;
+ /** How many segments for the bevel */
+ int seg;
+ /** Offset for this edge, on left side */
+ float offset_l;
+ /** Offset for this edge, on right side */
+ float offset_r;
+ /** User specification for offset_l */
+ float offset_l_spec;
+ /** User specification for offset_r */
+ float offset_r_spec;
+ /** Is this edge beveled? */
+ bool is_bev;
+ /** Is e->v2 the vertex at this end? */
+ bool is_rev;
+ /** Is e a seam for custom loopdata (e.g., UVs)? */
+ bool is_seam;
+ /** Used during the custom profile orientation pass */
+ bool visited_rpo;
+ char _pad[4];
} EdgeHalf;
-/* Profile specification.
+/* Profile specification:
* 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 profile is an arc with control points coa, midco,
+ * The profile is a path defined with start, middle, and end control points
* projected onto a plane (plane_no is normal, plane_co is a point on it)
* via lines in a given direction (proj_dir).
* After the parameters are all set, the actual profile points are calculated
- * and point in prof_co. We also may need profile points for a higher resolution
- * number of segments, in order to make the vertex mesh pattern, and that goes
- * in prof_co_2.
+ * and pointed to by prof_co. We also may need profile points for a higher resolution
+ * number of segments for the subdivision while making the ADJ vertex mesh pattern,
+ * and that goes in prof_co_2.
*/
typedef struct Profile {
- float super_r; /* superellipse r parameter */
- float coa[3]; /* start control point for profile */
- float midco[3]; /* mid control point for profile */
- float cob[3]; /* end control point for profile */
- float plane_no[3]; /* normal of plane to project to */
- float plane_co[3]; /* coordinate on plane to project to */
- float proj_dir[3]; /* direction of projection line */
- float *prof_co; /* seg+1 profile coordinates (triples of floats) */
- float *prof_co_2; /* like prof_co, but for seg power of 2 >= seg */
+ /** Superellipse r parameter */
+ float super_r;
+ /** Height for profile cutoff face sides */
+ float height;
+ /** Start control point for profile */
+ float start[3];
+ /** Mid control point for profile */
+ float middle[3];
+ /** End control point for profile */
+ float end[3];
+ /** Normal of plane to project to */
+ float plane_no[3];
+ /** Coordinate on plane to project to */
+ float plane_co[3];
+ /** Direction of projection line */
+ float proj_dir[3];
+ /** seg+1 profile coordinates (triples of floats) */
+ float *prof_co;
+ /** Like prof_co, but for seg power of 2 >= seg */
+ float *prof_co_2;
} Profile;
#define PRO_SQUARE_R 1e4f
#define PRO_CIRCLE_R 2.0f
#define PRO_LINE_R 1.0f
#define PRO_SQUARE_IN_R 0.0f
-/* Cache result of expensive calculation of u parameter values to
+/** The un-transformed 2D storage of profile vertex locations. Also for non-custom profiles
+ * this serves as a cache for the results of the expensive calculation of u parameter values to
* get even spacing on superellipse for current BevelParams seg
* and pro_super_r. */
typedef struct ProfileSpacing {
- double *xvals; /* seg+1 x values */
- double *xvals_2; /* seg_2+1 x values, seg_2 = power of 2 >= seg */
- double *yvals; /* seg+1 y values */
- double *yvals_2; /* seg_2+1 y values, seg_2 = power of 2 >= seg */
- int seg_2; /* the seg_2 value */
+ /** The profile's seg+1 x values */
+ double *xvals;
+ /** The profile's seg+1 y values */
+ double *yvals;
+ /** The profile's seg_2+1 x values, (seg_2 = power of 2 >= seg) */
+ double *xvals_2;
+ /** The profile's seg_2+1 y values, (seg_2 = power of 2 >= seg) */
+ double *yvals_2;
+ /** The power of two greater than or equal to the number of segments. */
+ int seg_2;
+ /** How far "out" the profile is, used at the start of subdivision */
+ float fullness;
} ProfileSpacing;
-/* An element in a cyclic boundary of a Vertex Mesh (VMesh) */
+/** An element in a cyclic boundary of a Vertex Mesh (VMesh) */
typedef struct BoundVert {
/** In CCW order. */
struct BoundVert *next, *prev;
@@ -137,7 +188,7 @@ typedef struct BoundVert {
/** First of edges attached here: in CCW order. */
EdgeHalf *efirst;
EdgeHalf *elast;
- /** The "edge between" that this is on, in offset_on_edge_between case. */
+ /** The "edge between" that this boundvert on, in offset_on_edge_between case. */
EdgeHalf *eon;
/** Beveled edge whose left side is attached here, if any. */
EdgeHalf *ebev;
@@ -157,26 +208,35 @@ typedef struct BoundVert {
bool is_arc_start;
/** This boundvert begins a patch profile */
bool is_patch_start;
+ /** Is this boundvert the side of the custom profile's start */
+ bool is_profile_start;
+ char _pad[3];
/** Length of seam starting from current boundvert to next boundvert with ccw ordering */
int seam_len;
/** Same as seam_len but defines length of sharp edges */
int sharp_len;
- // int _pad;
} BoundVert;
-/* Mesh structure replacing a vertex */
+/** Mesh structure replacing a vertex */
typedef struct VMesh {
- NewVert *mesh; /* allocated array - size and structure depends on kind */
- BoundVert *boundstart; /* start of boundary double-linked list */
- int count; /* number of vertices in the boundary */
- int seg; /* common # of segments for segmented edges */
+ /** Allocated array - size and structure depends on kind */
+ NewVert *mesh;
+ /** Start of boundary double-linked list */
+ BoundVert *boundstart;
+ /** Number of vertices in the boundary */
+ int count;
+ /** Common number of segments for segmented edges (same as bp->seg) */
+ int seg;
+ /** The kind of mesh to build at the corner vertex meshes */
enum {
M_NONE, /* no polygon mesh needed */
M_POLY, /* a simple polygon */
M_ADJ, /* "adjacent edges" mesh pattern */
M_TRI_FAN, /* a simple polygon - fan filled */
+ M_CUTOFF, /* A triangulated face at the end of each profile */
} mesh_kind;
- // int _pad;
+
+ int _pad;
} VMesh;
/* Data for a vertex involved in a bevel */
@@ -196,6 +256,7 @@ typedef struct BevVert {
/** Used in graph traversal */
bool visited;
/** Array of size edgecount; CCW order from vertex normal side */
+ char _pad[6];
EdgeHalf *edges;
/** Array of size wirecount of wire edges */
BMEdge **wire_edges;
@@ -209,7 +270,7 @@ typedef enum {
F_NONE,
/** Original face, not touched */
F_ORIG,
- /** Face for construction aroun a vert */
+ /** Face for construction around a vert */
F_VERT,
/** Face for a beveled edge */
F_EDGE,
@@ -217,11 +278,21 @@ typedef enum {
F_RECON,
} FKind;
+/** Helper for keeping track of angle kind. */
+enum {
+ /** Angle less than 180 degrees */
+ ANGLE_SMALLER = -1,
+ /** 180 degree angle */
+ ANGLE_STRAIGHT = 0,
+ /** Angle greater than 180 degrees */
+ ANGLE_LARGER = 1,
+};
+
#if 0
static const char* fkind_names[] = {"F_NONE", "F_ORIG", "F_VERT", "F_EDGE", "F_RECON"}; /* DEBUG */
#endif
-/* Bevel parameters and state */
+/** Bevel parameters and state */
typedef struct BevelParams {
/** Records BevVerts made: key BMVert*, value BevVert* */
GHash *vert_hash;
@@ -229,9 +300,10 @@ typedef struct BevelParams {
GHash *face_hash;
/** Use for all allocs while bevel runs, if we need to free we can switch to mempool. */
MemArena *mem_arena;
- /** Parameter values for evenly spaced profiles. */
+ /** Profile vertex location and spacings */
ProfileSpacing pro_spacing;
-
+ /** Parameter values for evenly spaced profile points for the miter profiles */
+ ProfileSpacing pro_spacing_miter;
/** Blender units to offset each side of a beveled edge. */
float offset;
/** How offset is measured; enum defined in bmesh_operators.h */
@@ -258,6 +330,11 @@ typedef struct BevelParams {
bool mark_sharp;
/** Should we harden normals? */
bool harden_normals;
+ /** Should we use the custom profiles feature? */
+ bool use_custom_profile;
+ char _pad[3];
+ /** The struct used to store the custom profile input */
+ const struct CurveProfile *custom_profile;
/** Vertex group array, maybe set if vertex_only. */
const struct MDeformVert *dvert;
/** Vertex group index, maybe set if vertex_only. */
@@ -270,6 +347,8 @@ typedef struct BevelParams {
int miter_outer;
/** What kind of miter pattern to use on non-reflex angles. */
int miter_inner;
+ /** The method to use for vertex mesh creation */
+ int vmesh_method;
/** Amount to spread when doing inside miter. */
float spread;
/** Mesh's smoothresh, used if hardening. */
@@ -278,8 +357,6 @@ typedef struct BevelParams {
// #pragma GCC diagnostic ignored "-Wpadded"
-// #include "bevdebug.c"
-
/* Some flags to re-enable old behavior for a while,
* in case fixes broke things not caught by regression tests. */
static int bev_debug_flags = 0;
@@ -367,6 +444,7 @@ static BoundVert *add_new_bound_vert(MemArena *mem_arena, VMesh *vm, const float
ans->any_seam = false;
ans->is_arc_start = false;
ans->is_patch_start = false;
+ ans->is_profile_start = false;
vm->count++;
return ans;
}
@@ -628,7 +706,7 @@ static BMFace *bev_create_ngon(BMesh *bm,
}
if (mat_nr >= 0) {
- f->mat_nr = mat_nr;
+ f->mat_nr = (short)mat_nr;
}
return f;
}
@@ -812,7 +890,7 @@ static void bev_merge_edge_uvs(BMesh *bm, BMEdge *bme, BMVert *v)
}
/* 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])
+static void slide_dist(EdgeHalf *e, BMVert *v, float d, float r_slideco[3])
{
float dir[3], len;
@@ -821,8 +899,8 @@ static void slide_dist(EdgeHalf *e, BMVert *v, float d, float slideco[3])
if (d > len) {
d = len - (float)(50.0 * BEVEL_EPSILON_D);
}
- copy_v3_v3(slideco, v->co);
- madd_v3_v3fl(slideco, dir, -d);
+ copy_v3_v3(r_slideco, v->co);
+ madd_v3_v3fl(r_slideco, dir, -d);
}
/* Is co not on the edge e? if not, return the closer end of e in ret_closer_v */
@@ -873,13 +951,13 @@ static int edges_angle_kind(EdgeHalf *e1, EdgeHalf *e2, BMVert *v)
}
dot = dot_v3v3(cross, no);
if (fabsf(dot) < BEVEL_EPSILON_BIG) {
- return 0;
+ return ANGLE_STRAIGHT;
}
else if (dot < 0.0f) {
- return 1;
+ return ANGLE_LARGER;
}
else {
- return -1;
+ return ANGLE_SMALLER;
}
}
@@ -917,7 +995,7 @@ static bool point_between_edges(float co[3], BMVert *v, BMFace *f, EdgeHalf *e1,
/*
* Calculate the meeting point between the offset edges for e1 and e2, putting answer in meetco.
* e1 and e2 share vertex v and face f (may be NULL) and viewed from the normal side of
- * the bevel vertex, e1 precedes e2 in CCW order.
+ * the bevel vertex, e1 precedes e2 in CCW order.
* Except: if edges_between is true, there are edges between e1 and e2 in CCW order so they
* don't share a common face. We want the meeting point to be on an existing face so it
* should be dropped onto one of the intermediate faces, if possible.
@@ -978,7 +1056,7 @@ static void offset_meet(
normalize_v3(norm_perp1);
copy_v3_v3(off1a, v->co);
d = max_ff(e1->offset_r, e2->offset_l);
- d = d / cos(ang / 2.0f);
+ d = d / cosf(ang / 2.0f);
madd_v3_v3fl(off1a, norm_perp1, d);
copy_v3_v3(meetco, off1a);
}
@@ -1260,37 +1338,37 @@ static void set_profile_params(BevelParams *bp, BevVert *bv, BoundVert *bndv)
negate_v3(pro->proj_dir);
}
normalize_v3(pro->proj_dir);
- project_to_edge(e->e, co1, co2, pro->midco);
+ project_to_edge(e->e, co1, co2, pro->middle);
if (DEBUG_OLD_PROJ_TO_PERP_PLANE) {
- /* put arc endpoints on plane with normal proj_dir, containing midco */
+ /* put arc endpoints on plane with normal proj_dir, containing middle */
add_v3_v3v3(co3, co1, pro->proj_dir);
- if (!isect_line_plane_v3(pro->coa, co1, co3, pro->midco, pro->proj_dir)) {
+ if (!isect_line_plane_v3(pro->start, co1, co3, pro->middle, pro->proj_dir)) {
/* shouldn't happen */
- copy_v3_v3(pro->coa, co1);
+ copy_v3_v3(pro->start, co1);
}
add_v3_v3v3(co3, co2, pro->proj_dir);
- if (!isect_line_plane_v3(pro->cob, co2, co3, pro->midco, pro->proj_dir)) {
+ if (!isect_line_plane_v3(pro->end, co2, co3, pro->middle, pro->proj_dir)) {
/* shouldn't happen */
- copy_v3_v3(pro->cob, co2);
+ copy_v3_v3(pro->end, co2);
}
}
else {
- copy_v3_v3(pro->coa, co1);
- copy_v3_v3(pro->cob, co2);
+ copy_v3_v3(pro->start, co1);
+ copy_v3_v3(pro->end, co2);
}
- /* default plane to project onto is the one with triangle co1 - midco - co2 in it */
- sub_v3_v3v3(d1, pro->midco, co1);
- sub_v3_v3v3(d2, pro->midco, co2);
+ /* default plane to project onto is the one with triangle co1 - middle - co2 in it */
+ sub_v3_v3v3(d1, pro->middle, co1);
+ sub_v3_v3v3(d2, pro->middle, co2);
normalize_v3(d1);
normalize_v3(d2);
cross_v3_v3v3(pro->plane_no, d1, d2);
normalize_v3(pro->plane_no);
if (nearly_parallel(d1, d2)) {
- /* co1 - midco -co2 are collinear.
+ /* co1 - middle -co2 are collinear.
* Should be case that beveled edge is coplanar with two boundary verts.
* We want to move the profile to that common plane, if possible.
* That makes the multi-segment bevels curve nicely in that plane, as users expect.
- * The new midco should be either v (when neighbor edges are unbeveled)
+ * The new middle should be either v (when neighbor edges are unbeveled)
* or the intersection of the offset lines (if they are).
* If the profile is going to lead into unbeveled edges on each side
* (that is, both BoundVerts are "on-edge" points on non-beveled edges)
@@ -1300,14 +1378,14 @@ static void set_profile_params(BevelParams *bp, BevVert *bv, BoundVert *bndv)
}
else {
if (DEBUG_OLD_PROJ_TO_PERP_PLANE) {
- copy_v3_v3(pro->coa, co1);
- copy_v3_v3(pro->cob, co2);
+ copy_v3_v3(pro->start, co1);
+ copy_v3_v3(pro->end, co2);
}
if (DEBUG_OLD_FLAT_MID) {
- copy_v3_v3(pro->midco, bv->v->co);
+ copy_v3_v3(pro->middle, bv->v->co);
}
else {
- copy_v3_v3(pro->midco, bv->v->co);
+ copy_v3_v3(pro->middle, bv->v->co);
if (e->prev->is_bev && e->next->is_bev && bv->selcount >= 3) {
/* want mid at the meet point of next and prev offset edges */
float d3[3], d4[3], co4[3], meetco[3], isect2[3];
@@ -1319,7 +1397,7 @@ static void set_profile_params(BevelParams *bp, BevVert *bv, BoundVert *bndv)
normalize_v3(d4);
if (nearly_parallel(d3, d4)) {
/* offset lines are collinear - want linear interpolation */
- mid_v3_v3v3(pro->midco, co1, co2);
+ mid_v3_v3v3(pro->middle, co1, co2);
do_linear_interp = true;
}
else {
@@ -1327,20 +1405,20 @@ static void set_profile_params(BevelParams *bp, BevVert *bv, BoundVert *bndv)
add_v3_v3v3(co4, co2, d4);
isect_kind = isect_line_line_v3(co1, co3, co2, co4, meetco, isect2);
if (isect_kind != 0) {
- copy_v3_v3(pro->midco, meetco);
+ copy_v3_v3(pro->middle, meetco);
}
else {
/* offset lines don't intersect - want linear interpolation */
- mid_v3_v3v3(pro->midco, co1, co2);
+ mid_v3_v3v3(pro->middle, co1, co2);
do_linear_interp = true;
}
}
}
}
- copy_v3_v3(pro->cob, co2);
- sub_v3_v3v3(d1, pro->midco, co1);
+ copy_v3_v3(pro->end, co2);
+ sub_v3_v3v3(d1, pro->middle, co1);
normalize_v3(d1);
- sub_v3_v3v3(d2, pro->midco, co2);
+ sub_v3_v3v3(d2, pro->middle, co2);
normalize_v3(d2);
cross_v3_v3v3(pro->plane_no, d1, d2);
normalize_v3(pro->plane_no);
@@ -1357,9 +1435,9 @@ static void set_profile_params(BevelParams *bp, BevVert *bv, BoundVert *bndv)
copy_v3_v3(pro->plane_co, co1);
}
else if (bndv->is_arc_start) {
- /* assume pro->midco was already set */
- copy_v3_v3(pro->coa, co1);
- copy_v3_v3(pro->cob, co2);
+ /* assume pro->middle was alredy set */
+ copy_v3_v3(pro->start, co1);
+ copy_v3_v3(pro->end, co2);
pro->super_r = PRO_CIRCLE_R;
zero_v3(pro->plane_co);
zero_v3(pro->plane_no);
@@ -1368,9 +1446,9 @@ static void set_profile_params(BevelParams *bp, BevVert *bv, BoundVert *bndv)
}
if (do_linear_interp) {
pro->super_r = PRO_LINE_R;
- copy_v3_v3(pro->coa, co1);
- copy_v3_v3(pro->cob, co2);
- mid_v3_v3v3(pro->midco, co1, co2);
+ copy_v3_v3(pro->start, co1);
+ copy_v3_v3(pro->end, co2);
+ mid_v3_v3v3(pro->middle, co1, co2);
/* won't use projection for this line profile */
zero_v3(pro->plane_co);
zero_v3(pro->plane_no);
@@ -1378,22 +1456,23 @@ static void set_profile_params(BevelParams *bp, BevVert *bv, BoundVert *bndv)
}
}
-/* Maybe move the profile plane for bndv->ebev to the plane its profile's coa, cob and the
+/* Maybe move the profile plane for bndv->ebev to the plane its profile's start, and the
* original beveled vert, bmv. This will usually be the plane containing its adjacent
- * non-beveled edges, but sometimes coa and cob are not on those edges.
- */
-static void move_profile_plane(BoundVert *bndv, BMVert *bmv)
+ * non-beveled edges, but sometimes the start and the end are not on those edges.
+ *
+ * Currently just used in build boundary terminal edge */
+static void move_profile_plane(BoundVert *bndv, BMVert *bmvert)
{
float d1[3], d2[3], no[3], no2[3], no3[3], dot2, dot3;
Profile *pro = &bndv->profile;
- /* only do this if projecting, and coa, cob, and proj_dir are not coplanar */
+ /* only do this if projecting, and start, end, and proj_dir are not coplanar */
if (is_zero_v3(pro->proj_dir)) {
return;
}
- sub_v3_v3v3(d1, bmv->co, pro->coa);
+ sub_v3_v3v3(d1, bmvert->co, pro->start);
normalize_v3(d1);
- sub_v3_v3v3(d2, bmv->co, pro->cob);
+ sub_v3_v3v3(d2, bmvert->co, pro->end);
normalize_v3(d2);
cross_v3_v3v3(no, d1, d2);
cross_v3_v3v3(no2, d1, pro->proj_dir);
@@ -1584,35 +1663,35 @@ static double superellipse_co(double x, float r, bool rbig)
}
/* Find the point on given profile at parameter i which goes from 0 to n as
- * the profile is moved from pro->coa to pro->cob.
+ * the profile is moved from pro->start to pro->end.
* We assume that n is either the global seg number or a power of 2 less than
* or equal to the power of 2 >= seg.
* In the latter case, we subsample the profile for seg_2, which will not necessarily
* give equal spaced chords, but is in fact more what is desired by the cubic subdivision
* method used to make the vmesh pattern. */
-static void get_profile_point(BevelParams *bp, const Profile *pro, int i, int n, float r_co[3])
+static void get_profile_point(BevelParams *bp, const Profile *pro, int i, int nseg, float r_co[3])
{
- int d;
+ int subsample_spacing;
if (bp->seg == 1) {
if (i == 0) {
- copy_v3_v3(r_co, pro->coa);
+ copy_v3_v3(r_co, pro->start);
}
else {
- copy_v3_v3(r_co, pro->cob);
+ copy_v3_v3(r_co, pro->end);
}
}
else {
- if (n == bp->seg) {
+ if (nseg == bp->seg) {
BLI_assert(pro->prof_co != NULL);
copy_v3_v3(r_co, pro->prof_co + 3 * i);
}
else {
- BLI_assert(is_power_of_2_i(n) && n <= bp->pro_spacing.seg_2);
- /* set d to spacing in prof_co_2 between subsamples */
- d = bp->pro_spacing.seg_2 / n;
- copy_v3_v3(r_co, pro->prof_co_2 + 3 * i * d);
+ BLI_assert(is_power_of_2_i(nseg) && nseg <= bp->pro_spacing.seg_2);
+ /* Find spacing between subsamples in prof_co_2 */
+ subsample_spacing = bp->pro_spacing.seg_2 / nseg;
+ copy_v3_v3(r_co, pro->prof_co_2 + 3 * i * subsample_spacing);
}
}
}
@@ -1624,15 +1703,16 @@ static void get_profile_point(BevelParams *bp, const Profile *pro, int i, int n,
* the coordinate values for the power of 2 >= bp->seg,
* because the ADJ pattern needs power-of-2 boundaries
* during construction. */
-static void calculate_profile(BevelParams *bp, BoundVert *bndv)
+static void calculate_profile(BevelParams *bp, BoundVert *bndv, bool reversed, bool miter)
{
int i, k, ns;
const double *xvals, *yvals;
- float co[3], co2[3], p[3], m[4][4];
+ float co[3], co2[3], p[3], map[4][4], bottom_corner[3], top_corner[3];
float *prof_co, *prof_co_k;
float r;
bool need_2, map_ok;
Profile *pro = &bndv->profile;
+ ProfileSpacing *pro_spacing = (miter) ? &bp->pro_spacing_miter : &bp->pro_spacing;
if (bp->seg == 1) {
return;
@@ -1640,27 +1720,39 @@ static void calculate_profile(BevelParams *bp, BoundVert *bndv)
need_2 = bp->seg != bp->pro_spacing.seg_2;
if (!pro->prof_co) {
- pro->prof_co = (float *)BLI_memarena_alloc(bp->mem_arena, (bp->seg + 1) * 3 * sizeof(float));
+ pro->prof_co = (float *)BLI_memarena_alloc(bp->mem_arena,
+ ((size_t)bp->seg + 1) * 3 * sizeof(float));
if (need_2) {
pro->prof_co_2 = (float *)BLI_memarena_alloc(
- bp->mem_arena, (bp->pro_spacing.seg_2 + 1) * 3 * sizeof(float));
+ bp->mem_arena, ((size_t)bp->pro_spacing.seg_2 + 1) * 3 * sizeof(float));
}
else {
pro->prof_co_2 = pro->prof_co;
}
}
r = pro->super_r;
- if (r == PRO_LINE_R) {
+ if (!bp->use_custom_profile && r == PRO_LINE_R) {
map_ok = false;
}
else {
- map_ok = make_unit_square_map(pro->coa, pro->midco, pro->cob, m);
- }
+ map_ok = make_unit_square_map(pro->start, pro->middle, pro->end, map);
+ }
+ if (bp->vmesh_method == BEVEL_VMESH_CUTOFF && map_ok) {
+ /* Calculate the "height" of the profile by putting the (0,0) and (1,1) corners of the
+ * un-transformed profile throught the 2D->3D map and calculating the distance between them */
+ zero_v3(p);
+ mul_v3_m4v3(bottom_corner, map, p);
+ p[0] = 1.0f;
+ p[1] = 1.0f;
+ mul_v3_m4v3(top_corner, map, p);
+ pro->height = len_v3v3(bottom_corner, top_corner);
+ }
+ /* The first iteration is the nseg case, the second is the seg_2 case (if it's needed) */
for (i = 0; i < 2; i++) {
if (i == 0) {
ns = bp->seg;
- xvals = bp->pro_spacing.xvals;
- yvals = bp->pro_spacing.yvals;
+ xvals = pro_spacing->xvals;
+ yvals = pro_spacing->yvals;
prof_co = pro->prof_co;
}
else {
@@ -1668,33 +1760,42 @@ static void calculate_profile(BevelParams *bp, BoundVert *bndv)
break; /* shares coords with pro->prof_co */
}
ns = bp->pro_spacing.seg_2;
- xvals = bp->pro_spacing.xvals_2;
- yvals = bp->pro_spacing.yvals_2;
+ xvals = pro_spacing->xvals_2;
+ yvals = pro_spacing->yvals_2;
prof_co = pro->prof_co_2;
}
- BLI_assert((r == PRO_LINE_R || (xvals != NULL && yvals != NULL)) && prof_co != NULL);
+
+ /* Iterate over the vertices along the boundary arc */
for (k = 0; k <= ns; k++) {
if (k == 0) {
- copy_v3_v3(co, pro->coa);
+ copy_v3_v3(co, pro->start);
}
else if (k == ns) {
- copy_v3_v3(co, pro->cob);
+ copy_v3_v3(co, pro->end);
}
else {
if (map_ok) {
- p[0] = xvals[k];
- p[1] = yvals[k];
+ if (reversed) {
+ p[0] = (float)yvals[ns - k];
+ p[1] = (float)xvals[ns - k];
+ }
+ else {
+ p[0] = (float)xvals[k];
+ p[1] = (float)yvals[k];
+ }
p[2] = 0.0f;
- mul_v3_m4v3(co, m, p);
+ /* Do the 2D->3D transformation of the profile coordinates */
+ mul_v3_m4v3(co, map, p);
}
else {
- interp_v3_v3v3(co, pro->coa, pro->cob, (float)k / (float)ns);
+ interp_v3_v3v3(co, pro->start, pro->end, (float)k / (float)ns);
}
}
- /* project co onto final profile plane */
- prof_co_k = prof_co + 3 * k;
+ /* Finish the 2D->3D transformation by projecting onto the final profile plane */
+ prof_co_k = prof_co + 3 * k; /* Each coord takes up 3 spaces */
if (!is_zero_v3(pro->proj_dir)) {
add_v3_v3v3(co2, co, pro->proj_dir);
+ /* pro->plane_co and pro->plane_no are filled in "set_profile_params" */
if (!isect_line_plane_v3(prof_co_k, co, co2, pro->plane_co, pro->plane_no)) {
/* shouldn't happen */
copy_v3_v3(prof_co_k, co);
@@ -1708,11 +1809,12 @@ static void calculate_profile(BevelParams *bp, BoundVert *bndv)
}
/* Snap a direction co to a superellipsoid with parameter super_r.
- * For square profiles, midline says whether or not to snap to both planes. */
+ * For square profiles, midline says whether or not to snap to both planes.
+ *
+ * Only currently used for the pipe and cube corner special cases */
static void snap_to_superellipsoid(float co[3], const float super_r, bool midline)
{
float a, b, c, x, y, z, r, rinv, dx, dy;
-
r = super_r;
if (r == PRO_CIRCLE_R) {
normalize_v3(co);
@@ -1936,7 +2038,7 @@ static void bevel_extend_edge_data(BevVert *bv)
} while (bcur != start);
}
-/* Mark edges as sharp if they are between a smooth recon face and a new face. */
+/* Mark edges as sharp if they are between a smooth reconstructed face and a new face. */
static void bevel_edges_sharp_boundary(BMesh *bm, BevelParams *bp)
{
BMIter fiter, liter;
@@ -1972,7 +2074,7 @@ static void bevel_edges_sharp_boundary(BMesh *bm, BevelParams *bp)
* And at boundaries between #F_EDGE and #F_VERT faces, the normals should match the #F_EDGE ones.
* Assumes custom loop normals are in use.
*/
-static void bevel_harden_normals(BMesh *bm, BevelParams *bp)
+static void bevel_harden_normals(BevelParams *bp, BMesh *bm)
{
BMIter liter, fiter;
BMFace *f;
@@ -2207,13 +2309,17 @@ static bool eh_on_plane(EdgeHalf *e)
/* Calculate the profiles for all the BoundVerts of VMesh vm */
static void calculate_vm_profiles(BevelParams *bp, BevVert *bv, VMesh *vm)
{
- BoundVert *v;
+ BoundVert *bndv;
- v = vm->boundstart;
+ bndv = vm->boundstart;
do {
- set_profile_params(bp, bv, v);
- calculate_profile(bp, v);
- } while ((v = v->next) != vm->boundstart);
+ set_profile_params(bp, bv, bndv);
+ /* Use the miter profile spacing struct if the default is filled with the custom profile. */
+ bool miter_profile = bp->use_custom_profile && (bndv->is_arc_start || bndv->is_patch_start);
+ /* Don't bother reversing the profile if it's a miter profile */
+ bool reverse_profile = !bndv->is_profile_start && !miter_profile;
+ calculate_profile(bp, bndv, reverse_profile, miter_profile);
+ } while ((bndv = bndv->next) != vm->boundstart);
}
/* Implements build_boundary for vertex-only case */
@@ -2263,14 +2369,15 @@ static void build_boundary_vertex_only(BevelParams *bp, BevVert *bv, bool constr
static void build_boundary_terminal_edge(BevelParams *bp,
BevVert *bv,
EdgeHalf *efirst,
- bool construct)
+ const bool construct)
{
MemArena *mem_arena = bp->mem_arena;
VMesh *vm = bv->vmesh;
- BoundVert *v;
+ BoundVert *bndv;
EdgeHalf *e;
const float *no;
float co[3], d;
+ bool use_tri_fan;
e = efirst;
if (bv->edgecount == 2) {
@@ -2278,9 +2385,9 @@ static void build_boundary_terminal_edge(BevelParams *bp,
no = e->fprev ? e->fprev->no : (e->fnext ? e->fnext->no : NULL);
offset_in_plane(e, no, true, co);
if (construct) {
- v = add_new_bound_vert(mem_arena, vm, co);
- v->efirst = v->elast = v->ebev = e;
- e->leftv = v;
+ bndv = add_new_bound_vert(mem_arena, vm, co);
+ bndv->efirst = bndv->elast = bndv->ebev = e;
+ e->leftv = bndv;
}
else {
adjust_bound_vert(e->leftv, co);
@@ -2288,9 +2395,9 @@ static void build_boundary_terminal_edge(BevelParams *bp,
no = e->fnext ? e->fnext->no : (e->fprev ? e->fprev->no : NULL);
offset_in_plane(e, no, false, co);
if (construct) {
- v = add_new_bound_vert(mem_arena, vm, co);
- v->efirst = v->elast = e;
- e->rightv = v;
+ bndv = add_new_bound_vert(mem_arena, vm, co);
+ bndv->efirst = bndv->elast = e;
+ e->rightv = bndv;
}
else {
adjust_bound_vert(e->rightv, co);
@@ -2298,11 +2405,9 @@ static void build_boundary_terminal_edge(BevelParams *bp,
/* make artificial extra point along unbeveled edge, and form triangle */
slide_dist(e->next, bv->v, e->offset_l, co);
if (construct) {
- v = add_new_bound_vert(mem_arena, vm, co);
- v->efirst = v->elast = e->next;
- e->next->leftv = e->next->rightv = v;
- /* could use M_POLY too, but tri-fan looks nicer)*/
- vm->mesh_kind = M_TRI_FAN;
+ bndv = add_new_bound_vert(mem_arena, vm, co);
+ bndv->efirst = bndv->elast = e->next;
+ e->next->leftv = e->next->rightv = bndv;
set_bound_vert_seams(bv, bp->mark_seam, bp->mark_sharp);
}
else {
@@ -2316,11 +2421,11 @@ static void build_boundary_terminal_edge(BevelParams *bp,
/* TODO: should do something else if angle between e and e->prev > 180 */
offset_meet(e->prev, e, bv->v, e->fprev, false, co);
if (construct) {
- v = add_new_bound_vert(mem_arena, vm, co);
- v->efirst = e->prev;
- v->elast = v->ebev = e;
- e->leftv = v;
- e->prev->leftv = e->prev->rightv = v;
+ bndv = add_new_bound_vert(mem_arena, vm, co);
+ bndv->efirst = e->prev;
+ bndv->elast = bndv->ebev = e;
+ e->leftv = bndv;
+ e->prev->leftv = e->prev->rightv = bndv;
}
else {
adjust_bound_vert(e->leftv, co);
@@ -2328,23 +2433,26 @@ static void build_boundary_terminal_edge(BevelParams *bp,
e = e->next;
offset_meet(e->prev, e, bv->v, e->fprev, false, co);
if (construct) {
- v = add_new_bound_vert(mem_arena, vm, co);
- v->efirst = e->prev;
- v->elast = e;
- e->leftv = e->rightv = v;
- e->prev->rightv = v;
+ bndv = add_new_bound_vert(mem_arena, vm, co);
+ bndv->efirst = e->prev;
+ bndv->elast = e;
+ e->leftv = e->rightv = bndv;
+ e->prev->rightv = bndv;
}
else {
adjust_bound_vert(e->leftv, co);
}
/* For the edges not adjacent to the beveled edge, slide the bevel amount along. */
d = efirst->offset_l_spec;
+ if (bp->use_custom_profile || bp->profile < 0.25f) {
+ d *= sqrtf(2.0f); /* Need to go further along the edge to make room for full profile area. */
+ }
for (e = e->next; e->next != efirst; e = e->next) {
slide_dist(e, bv->v, d, co);
if (construct) {
- v = add_new_bound_vert(mem_arena, vm, co);
- v->efirst = v->elast = e;
- e->leftv = e->rightv = v;
+ bndv = add_new_bound_vert(mem_arena, vm, co);
+ bndv->efirst = bndv->elast = e;
+ e->leftv = e->rightv = bndv;
}
else {
adjust_bound_vert(e->leftv, co);
@@ -2354,11 +2462,12 @@ static void build_boundary_terminal_edge(BevelParams *bp,
calculate_vm_profiles(bp, bv, vm);
if (bv->edgecount >= 3) {
- /* special case: snap profile to plane of adjacent two edges */
- v = vm->boundstart;
- BLI_assert(v->ebev != NULL);
- move_profile_plane(v, bv->v);
- calculate_profile(bp, v);
+ /* Special case: snap profile to plane of adjacent two edges. */
+ bndv = vm->boundstart;
+ BLI_assert(bndv->ebev != NULL);
+ move_profile_plane(bndv, bv->v);
+ /* This step happens before the profile orientation pass so don't reverse the profile. */
+ calculate_profile(bp, bndv, false, false);
}
if (construct) {
@@ -2368,12 +2477,34 @@ static void build_boundary_terminal_edge(BevelParams *bp,
vm->mesh_kind = M_NONE;
}
else if (vm->count == 3) {
- vm->mesh_kind = M_TRI_FAN;
+ use_tri_fan = true;
+ if (bp->use_custom_profile) {
+ /* Use M_POLY if the extra point is planar with the profile to prevent overhanging edges */
+ bndv = efirst->leftv;
+ float profile_plane[4];
+ plane_from_point_normal_v3(profile_plane, bndv->profile.plane_co, bndv->profile.plane_no);
+ bndv = efirst->rightv->next; /* The added boundvert placed along the non-adjacent edge */
+ if (dist_squared_to_plane_v3(bndv->nv.co, profile_plane) < BEVEL_EPSILON_BIG) {
+ use_tri_fan = false;
+ }
+ }
+ vm->mesh_kind = (use_tri_fan) ? M_TRI_FAN : M_POLY;
}
else {
vm->mesh_kind = M_POLY;
}
}
+#ifdef DEBUG_CUSTOM_PROFILE_WELD
+ if (bp->seg > 1) {
+ printf("Terminal Edge Profile Coordinates:\n");
+ for (int k = 0; k < bp->seg; k++) {
+ printf("%0.4f, %0.4f, %0.4f\n",
+ (double)vm->boundstart->profile.prof_co[3 * k],
+ (double)vm->boundstart->profile.prof_co[3 * k + 1],
+ (double)vm->boundstart->profile.prof_co[3 * k + 2]);
+ }
+ }
+#endif
}
/* Helper for build_boundary to handle special miters */
@@ -2399,7 +2530,7 @@ static void adjust_miter_coords(BevelParams *bp, BevVert *bv, EdgeHalf *emiter)
v3next = v3->next;
copy_v3_v3(co2, v1->nv.co);
if (v1->is_arc_start) {
- copy_v3_v3(v1->profile.midco, co2);
+ copy_v3_v3(v1->profile.middle, co2);
}
/* co1 is intersection of line through co2 in dir of emiter->e
@@ -2477,7 +2608,7 @@ static void build_boundary(BevelParams *bp, BevVert *bv, bool construct)
BoundVert *v, *v1, *v2, *v3;
VMesh *vm;
float co[3], r;
- int nip, nnip, miter_outer, miter_inner;
+ int in_plane, not_in_plane, miter_outer, miter_inner;
int ang_kind;
/* Current bevel does nothing if only one edge into a vertex */
@@ -2497,7 +2628,7 @@ static void build_boundary(BevelParams *bp, BevVert *bv, bool construct)
BLI_assert(e->is_bev);
if (bv->selcount == 1) {
- /* special case: only one beveled edge in */
+ /* Special case: only one beveled edge in */
build_boundary_terminal_edge(bp, bv, efirst, construct);
return;
}
@@ -2509,7 +2640,7 @@ static void build_boundary(BevelParams *bp, BevVert *bv, bool construct)
/* keep track of the first beveled edge of an outside miter (there can be at most 1 per bv */
emiter = NULL;
- /* Here: there is more than one beveled edge.
+ /* There is more than one beveled edge.
* We make BoundVerts to connect the sides of the beveled edges.
* Non-beveled edges in between will just join to the appropriate juncture point. */
e = efirst;
@@ -2519,26 +2650,26 @@ static void build_boundary(BevelParams *bp, BevVert *bv, bool construct)
/* Make the BoundVert for the right side of e; other side will be made
* when the beveled edge to the left of e is handled.
* Analyze edges until next beveled edge.
- * They are either "in plane" (preceding and subsequent faces are coplanar)
- * or not. The "non-in-plane" edges effect silhouette and we prefer to slide
- * along one of those if possible. */
- nip = nnip = 0; /* counts of in-plane / not-in-plane */
- enip = eip = NULL; /* representatives of each */
+ * They are either "in plane" (preceding and subsequent faces are coplanar) or not.
+ * The "non-in-plane" edges affect the silhouette and we prefer to slide along one of those if
+ * possible. */
+ in_plane = not_in_plane = 0; /* Counts of in-plane / not-in-plane */
+ enip = eip = NULL; /* representatives of each */
for (e2 = e->next; !e2->is_bev; e2 = e2->next) {
if (eh_on_plane(e2)) {
- nip++;
+ in_plane++;
eip = e2;
}
else {
- nnip++;
+ not_in_plane++;
enip = e2;
}
}
- if (nip == 0 && nnip == 0) {
+ if (in_plane == 0 && not_in_plane == 0) {
offset_meet(e, e2, bv->v, e->fnext, false, co);
}
- else if (nnip > 0) {
- if (bp->loop_slide && nnip == 1 && good_offset_on_edge_between(e, e2, enip, bv->v)) {
+ else if (not_in_plane > 0) {
+ if (bp->loop_slide && not_in_plane == 1 && good_offset_on_edge_between(e, e2, enip, bv->v)) {
if (offset_on_edge_between(e, e2, enip, bv->v, co, &r)) {
eon = enip;
}
@@ -2548,8 +2679,8 @@ static void build_boundary(BevelParams *bp, BevVert *bv, bool construct)
}
}
else {
- /* nip > 0 and nnip == 0 */
- if (bp->loop_slide && nip == 1 && good_offset_on_edge_between(e, e2, eip, bv->v)) {
+ /* n_in_plane > 0 and n_not_in_plane == 0 */
+ if (bp->loop_slide && in_plane == 1 && good_offset_on_edge_between(e, e2, eip, bv->v)) {
if (offset_on_edge_between(e, e2, eip, bv->v, co, &r)) {
eon = eip;
}
@@ -2579,16 +2710,15 @@ static void build_boundary(BevelParams *bp, BevVert *bv, bool construct)
* There can only be one outer reflex angle, so only one outer miter,
* and emiter will be set to the first edge of such an edge.
* A miter kind of BEVEL_MITER_SHARP means no special miter */
-
- if ((miter_outer != BEVEL_MITER_SHARP && !emiter && ang_kind == 1) ||
- (miter_inner != BEVEL_MITER_SHARP && ang_kind == -1)) {
- if (ang_kind == 1) {
+ if ((miter_outer != BEVEL_MITER_SHARP && !emiter && ang_kind == ANGLE_LARGER) ||
+ (miter_inner != BEVEL_MITER_SHARP && ang_kind == ANGLE_SMALLER)) {
+ if (ang_kind == ANGLE_LARGER) {
emiter = e;
}
/* make one or two more boundverts; for now all will have same co */
v1 = v;
v1->ebev = NULL;
- if (ang_kind == 1 && miter_outer == BEVEL_MITER_PATCH) {
+ if (ang_kind == ANGLE_LARGER && miter_outer == BEVEL_MITER_PATCH) {
v2 = add_new_bound_vert(mem_arena, vm, co);
}
else {
@@ -2600,7 +2730,7 @@ static void build_boundary(BevelParams *bp, BevVert *bv, bool construct)
v3->elast = e2;
v3->eon = NULL;
e2->leftv = v3;
- if (ang_kind == 1 && miter_outer == BEVEL_MITER_PATCH) {
+ if (ang_kind == ANGLE_LARGER && miter_outer == BEVEL_MITER_PATCH) {
v1->is_patch_start = true;
v2->eon = v1->eon;
v2->sinratio = v1->sinratio;
@@ -2622,17 +2752,16 @@ static void build_boundary(BevelParams *bp, BevVert *bv, bool construct)
}
else {
v1->is_arc_start = true;
- copy_v3_v3(v1->profile.midco, co);
+ copy_v3_v3(v1->profile.middle, co);
if (e->next == e2) {
v1->elast = v1->efirst;
}
else {
- int between = nip + nnip;
+ int between = in_plane + not_in_plane;
int bet2 = between / 2;
bool betodd = (between % 2) == 1;
int i = 0;
- /* Put first half of in-between edges at index 0,
- * second half at index bp->seg.
+ /* Put first half of in-between edges at index 0, second half at index bp->seg.
* If between is odd, put middle one at midindex */
for (e3 = e->next; e3 != e2; e3 = e3->next) {
v1->elast = e3;
@@ -2651,15 +2780,15 @@ static void build_boundary(BevelParams *bp, BevVert *bv, bool construct)
}
}
}
- else {
+ else { /* construct == false */
ang_kind = edges_angle_kind(e, e2, bv->v);
- if ((miter_outer != BEVEL_MITER_SHARP && !emiter && ang_kind == 1) ||
- (miter_inner != BEVEL_MITER_SHARP && ang_kind == -1)) {
- if (ang_kind == 1) {
+ if ((miter_outer != BEVEL_MITER_SHARP && !emiter && ang_kind == ANGLE_LARGER) ||
+ (miter_inner != BEVEL_MITER_SHARP && ang_kind == ANGLE_SMALLER)) {
+ if (ang_kind == ANGLE_LARGER) {
emiter = e;
}
v1 = e->rightv;
- if (ang_kind == 1 && miter_outer == BEVEL_MITER_PATCH) {
+ if (ang_kind == ANGLE_LARGER && miter_outer == BEVEL_MITER_PATCH) {
v2 = v1->next;
v3 = v2->next;
}
@@ -2699,7 +2828,14 @@ static void build_boundary(BevelParams *bp, BevVert *bv, bool construct)
vm->mesh_kind = M_POLY;
}
else {
- vm->mesh_kind = M_ADJ;
+ switch (bp->vmesh_method) {
+ case BEVEL_VMESH_ADJ:
+ vm->mesh_kind = M_ADJ;
+ break;
+ case BEVEL_VMESH_CUTOFF:
+ vm->mesh_kind = M_CUTOFF;
+ break;
+ }
}
}
}
@@ -2885,6 +3021,194 @@ static bool adjust_the_cycle_or_chain_fast(BoundVert *vstart, int np, bool iscyc
}
#endif
+/** Helper function to return the next Beveled EdgeHalf along a path.
+ * \param toward_bv Whether the direction to travel points toward or away from the BevVert
+ * connected to the current EdgeHalf
+ * \param r_bv The BevVert conencted to the EdgeHalf-- updated if we're travelling to the other
+ * EdgeHalf of an original edge
+ * \note This only returns the most parallel edge if it's the most parallel by
+ * at least 10 degrees. This is a somewhat arbitrary choice, but it makes sure that consistent
+ * orientation paths only continue in obvious ways. */
+static EdgeHalf *next_edgehalf_bev(BevelParams *bp,
+ EdgeHalf *start_edge,
+ bool toward_bv,
+ BevVert **r_bv)
+{
+ EdgeHalf *new_edge;
+ EdgeHalf *next_edge = NULL;
+ float dir_start_edge[3], dir_new_edge[3];
+ float second_best_dot = 0.0f, best_dot = 0.0f;
+ float new_dot;
+
+ /* Case 1: The next EdgeHalf is across a BevVert from the current EdgeHalf. */
+ if (toward_bv) {
+ /* Skip all the logic if there's only one beveled edge at the vertex, we're at an end. */
+ if ((*r_bv)->selcount == 1) {
+ return NULL; /* No other edges to go to. */
+ }
+
+ /* The case with only one other edge connected to the vertex is special too. */
+ if ((*r_bv)->selcount == 2) {
+ /* Just find the next beveled edge, that's the only other option. */
+ new_edge = start_edge;
+ do {
+ new_edge = new_edge->next;
+ } while (!new_edge->is_bev);
+
+ return new_edge;
+ }
+
+ /* Find the direction vector of the current edge (pointing INTO the BevVert).
+ * v1 and v2 don't necessarily have an order, so we need to check which is closer to bv. */
+ if (start_edge->e->v1 == (*r_bv)->v) {
+ sub_v3_v3v3(dir_start_edge, start_edge->e->v1->co, start_edge->e->v2->co);
+ }
+ else {
+ sub_v3_v3v3(dir_start_edge, start_edge->e->v2->co, start_edge->e->v1->co);
+ }
+ normalize_v3(dir_start_edge);
+
+ /* Find the beveled edge coming out of the BevVert that's most parallel to the current edge. */
+ new_edge = start_edge->next;
+ while (new_edge != start_edge) {
+ if (!new_edge->is_bev) {
+ new_edge = new_edge->next;
+ continue;
+ }
+ /* Find direction vector of the possible next edge (pointing OUT of the BevVert). */
+ if (new_edge->e->v2 == (*r_bv)->v) {
+ sub_v3_v3v3(dir_new_edge, new_edge->e->v1->co, new_edge->e->v2->co);
+ }
+ else {
+ sub_v3_v3v3(dir_new_edge, new_edge->e->v2->co, new_edge->e->v1->co);
+ }
+ normalize_v3(dir_new_edge);
+
+ /* Use this edge if it is the most parallel to the orignial so far */
+ new_dot = dot_v3v3(dir_new_edge, dir_start_edge);
+ if (new_dot > best_dot) {
+ second_best_dot = best_dot; /* For remembering if the choice was too close. */
+ best_dot = new_dot;
+ next_edge = new_edge;
+ }
+ else if (new_dot > second_best_dot) {
+ second_best_dot = new_dot;
+ }
+
+ new_edge = new_edge->next;
+ }
+
+ /* Only return a new Edge if one was found and if the choice of next edge was not too close. */
+ if ((next_edge != NULL) && compare_ff(best_dot, second_best_dot, BEVEL_SMALL_ANG_DOT)) {
+ return NULL;
+ }
+ else {
+ return next_edge;
+ }
+ }
+ else {
+ /* Case 2: The next EdgeHalf is the other side of the BMEdge.
+ * It's part of the same BMEdge, so we know the other EdgeHalf is also beveled. */
+ next_edge = find_other_end_edge_half(bp, start_edge, r_bv);
+ return next_edge;
+ }
+}
+
+/** Starting along any beveled edge, travel along the chain / cycle of beveled edges including that
+ * edge, marking consistent profile orientations along the way. Orientations are marked by setting
+ * whether the BoundVert that contains each profile's information is the side of the profile's
+ * start or not. */
+static void regularize_profile_orientation(BevelParams *bp, BMEdge *bme)
+{
+ BevVert *start_bv;
+ BevVert *bv;
+ EdgeHalf *start_edgehalf, *edgehalf;
+ bool toward_bv;
+
+ start_bv = find_bevvert(bp, bme->v1);
+ start_edgehalf = find_edge_half(start_bv, bme);
+ if (!start_edgehalf->is_bev || start_edgehalf->visited_rpo) {
+ return;
+ }
+
+ /* Pick a BoundVert on one side of the profile to use for the start of the profile. */
+ start_edgehalf->leftv->is_profile_start = false;
+ start_edgehalf->visited_rpo = true;
+
+ /* First loop starts in the away from BevVert direction and the second starts toward it. */
+ for (int i = 0; i < 2; i++) {
+ edgehalf = start_edgehalf;
+ bv = start_bv;
+ toward_bv = (i == 0);
+ edgehalf = next_edgehalf_bev(bp, edgehalf, toward_bv, &bv);
+
+ /* Keep traveling until there is no unvisited beveled edgehalf to visit next. */
+ while (edgehalf && !edgehalf->visited_rpo) {
+ /* Mark the correct BoundVert as the start of the newly visited profile.
+ * The direction relative to the BevVert switches every step, so also switch
+ * the orientation every step. */
+ if (i == 0) {
+ edgehalf->leftv->is_profile_start = toward_bv;
+ }
+ else {
+ /* The opposite side as the first direction because we're moving the other way. */
+ edgehalf->leftv->is_profile_start = !toward_bv;
+ }
+
+ /* The next jump will in the opposite direction relative to the BevVert. */
+ toward_bv = !toward_bv;
+
+ edgehalf->visited_rpo = true;
+ edgehalf = next_edgehalf_bev(bp, edgehalf, toward_bv, &bv);
+ }
+ }
+}
+
+#ifdef DEBUG_PROFILE_ORIENTATION_DRAW
+/** Draws markers on beveled edges showing the side that the profile starts on. A sphere shows
+ * the start side of the profile where it starts, and the lines connected to the sphere show which
+ * edge the orientation corresponds to.
+ * \note Only drawn while bevel is calculating, the debug geometry is not persistent. */
+static void debug_draw_profile_orientation(BevelParams *bp, BMesh *bm)
+{
+ BMIter iter;
+ BMEdge *bmedge;
+ float middle[3];
+
+ BM_ITER_MESH (bmedge, &iter, bm, BM_EDGES_OF_MESH) {
+ if (BM_elem_flag_test(bmedge, BM_ELEM_TAG)) {
+ mid_v3_v3v3(middle, bmedge->v1->co, bmedge->v2->co);
+
+ /* Draw the orientation for the first side of the edge. */
+ EdgeHalf *edge_half = find_edge_half(find_bevvert(bp, bmedge->v1), bmedge);
+ if (edge_half->leftv->is_profile_start) { /* The left boundvert defines the profiles. */
+ DRW_debug_sphere(edge_half->leftv->nv.co, 0.04f, debug_color_red);
+ DRW_debug_line_v3v3(middle, edge_half->leftv->nv.co, debug_color_red);
+ DRW_debug_line_v3v3(bmedge->v1->co, edge_half->leftv->nv.co, debug_color_red);
+ }
+ else {
+ DRW_debug_sphere(edge_half->rightv->nv.co, 0.04f, debug_color_red);
+ DRW_debug_line_v3v3(middle, edge_half->rightv->nv.co, debug_color_red);
+ DRW_debug_line_v3v3(bmedge->v1->co, edge_half->rightv->nv.co, debug_color_red);
+ }
+
+ /* Draw the orientation for the second side of the edge. */
+ edge_half = find_edge_half(find_bevvert(bp, bmedge->v2), bmedge);
+ if (edge_half->leftv->is_profile_start) {
+ DRW_debug_sphere(edge_half->leftv->nv.co, 0.05f, debug_color_blue);
+ DRW_debug_line_v3v3(middle, edge_half->leftv->nv.co, debug_color_blue);
+ DRW_debug_line_v3v3(bmedge->v2->co, edge_half->leftv->nv.co, debug_color_blue);
+ }
+ else {
+ DRW_debug_sphere(edge_half->rightv->nv.co, 0.05f, debug_color_blue);
+ DRW_debug_line_v3v3(middle, edge_half->rightv->nv.co, debug_color_blue);
+ DRW_debug_line_v3v3(bmedge->v2->co, edge_half->rightv->nv.co, debug_color_blue);
+ }
+ }
+ }
+}
+#endif
+
/* Adjust the offsets for a single cycle or chain.
* For chains and some cycles, a fast solution exists.
* Otherwise, we set up and solve a linear least squares problem
@@ -3218,8 +3542,8 @@ static VMesh *new_adj_vmesh(MemArena *mem_arena, int count, int seg, BoundVert *
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 = (NewVert *)BLI_memarena_alloc(
+ mem_arena, (size_t)(count * (1 + seg / 2) * (1 + seg)) * sizeof(NewVert));
vm->mesh_kind = M_ADJ;
return vm;
}
@@ -3261,10 +3585,10 @@ static NewVert *mesh_vert_canon(VMesh *vm, int i, int j, int k)
static bool is_canon(VMesh *vm, int i, int j, int k)
{
int ns2 = vm->seg / 2;
- if (vm->seg % 2 == 1) {
+ if (vm->seg % 2 == 1) { /* odd */
return (j <= ns2 && k <= ns2);
}
- else {
+ else { /* even */
return ((j < ns2 && k <= ns2) || (j == ns2 && k == ns2 && i == 0));
}
}
@@ -3296,6 +3620,9 @@ static void vmesh_copy_equiv_verts(VMesh *vm)
/* Calculate and return in r_cent the centroid of the center poly */
static void vmesh_center(VMesh *vm, float r_cent[3])
{
+#ifdef DEBUG_CUSTOM_PROFILE_ADJ
+ printf("VMESH CENTER\n");
+#endif
int n, ns2, i;
n = vm->count;
@@ -3432,182 +3759,193 @@ static int interp_range(const float *frac, int n, const float f, float *r_rest)
}
/* Interpolate given vmesh to make one with target nseg border vertices on the profiles */
-static VMesh *interp_vmesh(BevelParams *bp, VMesh *vm0, int nseg)
+/* HANS-TODO: This puts the center mesh vert at a slightly off location sometimes, which seems to
+ * be associated with the rest of that ring being shifted or connected slightly incorrectly to its
+ * neighbors */
+static VMesh *interp_vmesh(BevelParams *bp, VMesh *vm_in, int nseg)
{
- int n, ns0, nseg2, odd, i, j, k, j0, k0, k0prev, j0inc, k0inc;
+ int n_bndv, ns_in, nseg2, odd, i, j, k, j_in, k_in, k_in_prev, j0inc, k0inc;
float *prev_frac, *frac, *new_frac, *prev_new_frac;
- float f, restj, restk, restkprev;
+ float fraction, restj, restk, restkprev;
float quad[4][3], co[3], center[3];
- VMesh *vm1;
+ VMesh *vm_out;
BoundVert *bndv;
- n = vm0->count;
- ns0 = vm0->seg;
+ n_bndv = vm_in->count;
+ ns_in = vm_in->seg;
nseg2 = nseg / 2;
odd = nseg % 2;
- vm1 = new_adj_vmesh(bp->mem_arena, n, nseg, vm0->boundstart);
+ vm_out = new_adj_vmesh(bp->mem_arena, n_bndv, nseg, vm_in->boundstart);
- prev_frac = BLI_array_alloca(prev_frac, (ns0 + 1));
- frac = BLI_array_alloca(frac, (ns0 + 1));
+ prev_frac = BLI_array_alloca(prev_frac, (ns_in + 1));
+ frac = BLI_array_alloca(frac, (ns_in + 1));
new_frac = BLI_array_alloca(new_frac, (nseg + 1));
prev_new_frac = BLI_array_alloca(prev_new_frac, (nseg + 1));
- fill_vmesh_fracs(vm0, prev_frac, n - 1);
- bndv = vm0->boundstart;
+ fill_vmesh_fracs(vm_in, prev_frac, n_bndv - 1);
+ bndv = vm_in->boundstart;
fill_profile_fracs(bp, bndv->prev, prev_new_frac, nseg);
- for (i = 0; i < n; i++) {
- fill_vmesh_fracs(vm0, frac, i);
+ for (i = 0; i < n_bndv; i++) {
+ fill_vmesh_fracs(vm_in, frac, i);
fill_profile_fracs(bp, bndv, new_frac, nseg);
for (j = 0; j <= nseg2 - 1 + odd; j++) {
for (k = 0; k <= nseg2; k++) {
- f = new_frac[k];
- k0 = interp_range(frac, ns0, f, &restk);
- f = prev_new_frac[nseg - j];
- k0prev = interp_range(prev_frac, ns0, f, &restkprev);
- j0 = ns0 - k0prev;
+ /* Finding the locations where "fraction" fits into previous and current "frac" */
+ fraction = new_frac[k];
+ k_in = interp_range(frac, ns_in, fraction, &restk);
+ fraction = prev_new_frac[nseg - j];
+ k_in_prev = interp_range(prev_frac, ns_in, fraction, &restkprev);
+ j_in = ns_in - k_in_prev;
restj = -restkprev;
if (restj > -BEVEL_EPSILON) {
restj = 0.0f;
}
else {
- j0 = j0 - 1;
+ j_in = j_in - 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);
+ copy_v3_v3(co, mesh_vert_canon(vm_in, i, j_in, k_in)->co);
}
else {
- j0inc = (restj < BEVEL_EPSILON || j0 == ns0) ? 0 : 1;
- k0inc = (restk < BEVEL_EPSILON || k0 == ns0) ? 0 : 1;
- 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 + k0inc)->co);
- copy_v3_v3(quad[2], mesh_vert_canon(vm0, i, j0 + j0inc, k0 + k0inc)->co);
- copy_v3_v3(quad[3], mesh_vert_canon(vm0, i, j0 + j0inc, k0)->co);
+ j0inc = (restj < BEVEL_EPSILON || j_in == ns_in) ? 0 : 1;
+ k0inc = (restk < BEVEL_EPSILON || k_in == ns_in) ? 0 : 1;
+ copy_v3_v3(quad[0], mesh_vert_canon(vm_in, i, j_in, k_in)->co);
+ copy_v3_v3(quad[1], mesh_vert_canon(vm_in, i, j_in, k_in + k0inc)->co);
+ copy_v3_v3(quad[2], mesh_vert_canon(vm_in, i, j_in + j0inc, k_in + k0inc)->co);
+ copy_v3_v3(quad[3], mesh_vert_canon(vm_in, i, j_in + j0inc, k_in)->co);
interp_bilinear_quad_v3(quad, restk, restj, co);
}
- copy_v3_v3(mesh_vert(vm1, i, j, k)->co, co);
+ copy_v3_v3(mesh_vert(vm_out, i, j, k)->co, co);
}
}
bndv = bndv->next;
- memcpy(prev_frac, frac, (ns0 + 1) * sizeof(float));
- memcpy(prev_new_frac, new_frac, (nseg + 1) * sizeof(float));
+ memcpy(prev_frac, frac, (size_t)(ns_in + 1) * sizeof(float));
+ memcpy(prev_new_frac, new_frac, (size_t)(nseg + 1) * sizeof(float));
}
if (!odd) {
- vmesh_center(vm0, center);
- copy_v3_v3(mesh_vert(vm1, 0, nseg2, nseg2)->co, center);
+ vmesh_center(vm_in, center);
+ copy_v3_v3(mesh_vert(vm_out, 0, nseg2, nseg2)->co, center);
}
- vmesh_copy_equiv_verts(vm1);
- return vm1;
+ vmesh_copy_equiv_verts(vm_out);
+ return vm_out;
}
/* Do one step of cubic subdivision (Catmull-Clark), with special rules at boundaries.
* For now, this is written assuming vm0->nseg is even and > 0.
- * We are allowed to modify vm0, as it will not be used after this call.
+ * We are allowed to modify vm_in, as it will not be used after this call.
* See Levin 1999 paper: "Filling an N-sided hole using combined subdivision schemes". */
-static VMesh *cubic_subdiv(BevelParams *bp, VMesh *vm0)
+static VMesh *cubic_subdiv(BevelParams *bp, VMesh *vm_in)
{
- int n, ns0, ns20, ns1;
+ int n_boundary, ns_in, ns_in2, ns_out;
int i, j, k, inext;
float co[3], co1[3], co2[3], acc[3];
float beta, gamma;
- VMesh *vm1;
+ VMesh *vm_out;
BoundVert *bndv;
- n = vm0->count;
- ns0 = vm0->seg;
- ns20 = ns0 / 2;
- BLI_assert(ns0 % 2 == 0);
- ns1 = 2 * ns0;
- vm1 = new_adj_vmesh(bp->mem_arena, n, ns1, vm0->boundstart);
+ n_boundary = vm_in->count;
+ ns_in = vm_in->seg;
+ ns_in2 = ns_in / 2;
+ BLI_assert(ns_in % 2 == 0);
+ ns_out = 2 * ns_in;
+ vm_out = new_adj_vmesh(bp->mem_arena, n_boundary, ns_out, vm_in->boundstart);
- /* First we adjust the boundary vertices of the input mesh, storing in output mesh */
- for (i = 0; i < n; i++) {
- copy_v3_v3(mesh_vert(vm1, i, 0, 0)->co, mesh_vert(vm0, i, 0, 0)->co);
- for (k = 1; k < ns0; k++) {
- /* smooth boundary rule */
- copy_v3_v3(co, mesh_vert(vm0, i, 0, k)->co);
- copy_v3_v3(co1, mesh_vert(vm0, i, 0, k - 1)->co);
- copy_v3_v3(co2, mesh_vert(vm0, i, 0, k + 1)->co);
+ /* First we adjust the boundary vertices of the input mesh, storing in output mesh. */
+ for (i = 0; i < n_boundary; i++) {
+ copy_v3_v3(mesh_vert(vm_out, i, 0, 0)->co, mesh_vert(vm_in, i, 0, 0)->co);
+ for (k = 1; k < ns_in; k++) {
+ copy_v3_v3(co, mesh_vert(vm_in, i, 0, k)->co);
- add_v3_v3v3(acc, co1, co2);
- madd_v3_v3fl(acc, co, -2.0f);
- madd_v3_v3fl(co, acc, -1.0f / 6.0f);
+ /* Smooth boundary rule. Custom profiles shouldn't be smoothed. */
+ if (!bp->use_custom_profile) {
+ copy_v3_v3(co1, mesh_vert(vm_in, i, 0, k - 1)->co);
+ copy_v3_v3(co2, mesh_vert(vm_in, i, 0, k + 1)->co);
- copy_v3_v3(mesh_vert_canon(vm1, i, 0, 2 * k)->co, co);
+ add_v3_v3v3(acc, co1, co2);
+ madd_v3_v3fl(acc, co, -2.0f);
+ madd_v3_v3fl(co, acc, -1.0f / 6.0f);
+ }
+
+ copy_v3_v3(mesh_vert_canon(vm_out, i, 0, 2 * k)->co, co);
}
}
- /* now do odd ones in output mesh, based on even ones */
- bndv = vm1->boundstart;
- for (i = 0; i < n; i++) {
- for (k = 1; k < ns1; k += 2) {
- get_profile_point(bp, &bndv->profile, k, ns1, co);
- copy_v3_v3(co1, mesh_vert_canon(vm1, i, 0, k - 1)->co);
- copy_v3_v3(co2, mesh_vert_canon(vm1, i, 0, k + 1)->co);
+ /* Now adjust odd boundary vertices in output mesh, based on even ones. */
+ bndv = vm_out->boundstart;
+ for (i = 0; i < n_boundary; i++) {
+ for (k = 1; k < ns_out; k += 2) {
+ get_profile_point(bp, &bndv->profile, k, ns_out, co);
- add_v3_v3v3(acc, co1, co2);
- madd_v3_v3fl(acc, co, -2.0f);
- madd_v3_v3fl(co, acc, -1.0f / 6.0f);
+ /* Smooth if using a non-custom profile. */
+ if (!bp->use_custom_profile) {
+ copy_v3_v3(co1, mesh_vert_canon(vm_out, i, 0, k - 1)->co);
+ copy_v3_v3(co2, mesh_vert_canon(vm_out, i, 0, k + 1)->co);
- copy_v3_v3(mesh_vert_canon(vm1, i, 0, k)->co, co);
+ add_v3_v3v3(acc, co1, co2);
+ madd_v3_v3fl(acc, co, -2.0f);
+ madd_v3_v3fl(co, acc, -1.0f / 6.0f);
+ }
+
+ copy_v3_v3(mesh_vert_canon(vm_out, i, 0, k)->co, co);
}
bndv = bndv->next;
}
- vmesh_copy_equiv_verts(vm1);
+ vmesh_copy_equiv_verts(vm_out);
- /* Copy adjusted verts back into vm0 */
- for (i = 0; i < n; i++) {
- for (k = 0; k < ns0; k++) {
- copy_v3_v3(mesh_vert(vm0, i, 0, k)->co, mesh_vert(vm1, i, 0, 2 * k)->co);
+ /* Copy adjusted verts back into vm_in. */
+ for (i = 0; i < n_boundary; i++) {
+ for (k = 0; k < ns_in; k++) {
+ copy_v3_v3(mesh_vert(vm_in, i, 0, k)->co, mesh_vert(vm_out, i, 0, 2 * k)->co);
}
}
- vmesh_copy_equiv_verts(vm0);
+ vmesh_copy_equiv_verts(vm_in);
/* Now we do the internal vertices, using standard Catmull-Clark
* and assuming all boundary vertices have valence 4 */
/* The new face vertices */
- for (i = 0; i < n; i++) {
- for (j = 0; j < ns20; j++) {
- for (k = 0; k < ns20; k++) {
+ for (i = 0; i < n_boundary; i++) {
+ for (j = 0; j < ns_in2; j++) {
+ for (k = 0; k < ns_in2; k++) {
/* face up and right from (j, k) */
avg4(co,
- mesh_vert(vm0, i, j, k),
- mesh_vert(vm0, i, j, k + 1),
- mesh_vert(vm0, i, j + 1, k),
- mesh_vert(vm0, i, j + 1, k + 1));
- copy_v3_v3(mesh_vert(vm1, i, 2 * j + 1, 2 * k + 1)->co, co);
+ mesh_vert(vm_in, i, j, k),
+ mesh_vert(vm_in, i, j, k + 1),
+ mesh_vert(vm_in, i, j + 1, k),
+ mesh_vert(vm_in, i, j + 1, k + 1));
+ copy_v3_v3(mesh_vert(vm_out, i, 2 * j + 1, 2 * k + 1)->co, co);
}
}
}
- /* The new vertical edge vertices */
- for (i = 0; i < n; i++) {
- for (j = 0; j < ns20; j++) {
- for (k = 1; k <= ns20; k++) {
+ /* The new vertical edge vertices */
+ for (i = 0; i < n_boundary; i++) {
+ for (j = 0; j < ns_in2; j++) {
+ for (k = 1; k <= ns_in2; k++) {
/* vertical edge between (j, k) and (j+1, k) */
avg4(co,
- mesh_vert(vm0, i, j, k),
- mesh_vert(vm0, i, j + 1, k),
- mesh_vert_canon(vm1, i, 2 * j + 1, 2 * k - 1),
- mesh_vert_canon(vm1, i, 2 * j + 1, 2 * k + 1));
- copy_v3_v3(mesh_vert(vm1, i, 2 * j + 1, 2 * k)->co, co);
+ mesh_vert(vm_in, i, j, k),
+ mesh_vert(vm_in, i, j + 1, k),
+ mesh_vert_canon(vm_out, i, 2 * j + 1, 2 * k - 1),
+ mesh_vert_canon(vm_out, i, 2 * j + 1, 2 * k + 1));
+ copy_v3_v3(mesh_vert(vm_out, i, 2 * j + 1, 2 * k)->co, co);
}
}
}
- /* The new horizontal edge vertices */
- for (i = 0; i < n; i++) {
- for (j = 1; j < ns20; j++) {
- for (k = 0; k < ns20; k++) {
+ /* The new horizontal edge vertices */
+ for (i = 0; i < n_boundary; i++) {
+ for (j = 1; j < ns_in2; j++) {
+ for (k = 0; k < ns_in2; k++) {
/* horizontal edge between (j, k) and (j, k+1) */
avg4(co,
- mesh_vert(vm0, i, j, k),
- mesh_vert(vm0, i, j, k + 1),
- mesh_vert_canon(vm1, i, 2 * j - 1, 2 * k + 1),
- mesh_vert_canon(vm1, i, 2 * j + 1, 2 * k + 1));
- copy_v3_v3(mesh_vert(vm1, i, 2 * j, 2 * k + 1)->co, co);
+ mesh_vert(vm_in, i, j, k),
+ mesh_vert(vm_in, i, j, k + 1),
+ mesh_vert_canon(vm_out, i, 2 * j - 1, 2 * k + 1),
+ mesh_vert_canon(vm_out, i, 2 * j + 1, 2 * k + 1));
+ copy_v3_v3(mesh_vert(vm_out, i, 2 * j, 2 * k + 1)->co, co);
}
}
}
@@ -3615,66 +3953,66 @@ static VMesh *cubic_subdiv(BevelParams *bp, VMesh *vm0)
/* The new vertices, not on border */
gamma = 0.25f;
beta = -gamma;
- for (i = 0; i < n; i++) {
- for (j = 1; j < ns20; j++) {
- for (k = 1; k <= ns20; k++) {
+ for (i = 0; i < n_boundary; i++) {
+ for (j = 1; j < ns_in2; j++) {
+ for (k = 1; k <= ns_in2; k++) {
/* co1 = centroid of adjacent new edge verts */
avg4(co1,
- mesh_vert_canon(vm1, i, 2 * j, 2 * k - 1),
- mesh_vert_canon(vm1, i, 2 * j, 2 * k + 1),
- mesh_vert_canon(vm1, i, 2 * j - 1, 2 * k),
- mesh_vert_canon(vm1, i, 2 * j + 1, 2 * k));
+ mesh_vert_canon(vm_out, i, 2 * j, 2 * k - 1),
+ mesh_vert_canon(vm_out, i, 2 * j, 2 * k + 1),
+ mesh_vert_canon(vm_out, i, 2 * j - 1, 2 * k),
+ mesh_vert_canon(vm_out, i, 2 * j + 1, 2 * k));
/* co2 = centroid of adjacent new face verts */
avg4(co2,
- mesh_vert_canon(vm1, i, 2 * j - 1, 2 * k - 1),
- mesh_vert_canon(vm1, i, 2 * j + 1, 2 * k - 1),
- mesh_vert_canon(vm1, i, 2 * j - 1, 2 * k + 1),
- mesh_vert_canon(vm1, i, 2 * j + 1, 2 * k + 1));
+ mesh_vert_canon(vm_out, i, 2 * j - 1, 2 * k - 1),
+ mesh_vert_canon(vm_out, i, 2 * j + 1, 2 * k - 1),
+ mesh_vert_canon(vm_out, i, 2 * j - 1, 2 * k + 1),
+ mesh_vert_canon(vm_out, i, 2 * j + 1, 2 * k + 1));
/* combine with original vert with alpha, beta, gamma factors */
copy_v3_v3(co, co1); /* alpha = 1.0 */
madd_v3_v3fl(co, co2, beta);
- madd_v3_v3fl(co, mesh_vert(vm0, i, j, k)->co, gamma);
- copy_v3_v3(mesh_vert(vm1, i, 2 * j, 2 * k)->co, co);
+ madd_v3_v3fl(co, mesh_vert(vm_in, i, j, k)->co, gamma);
+ copy_v3_v3(mesh_vert(vm_out, i, 2 * j, 2 * k)->co, co);
}
}
}
- vmesh_copy_equiv_verts(vm1);
+ vmesh_copy_equiv_verts(vm_out);
/* The center vertex is special */
- gamma = sabin_gamma(n);
+ gamma = sabin_gamma(n_boundary);
beta = -gamma;
/* accumulate edge verts in co1, face verts in co2 */
zero_v3(co1);
zero_v3(co2);
- for (i = 0; i < n; i++) {
- add_v3_v3(co1, mesh_vert(vm1, i, ns0, ns0 - 1)->co);
- add_v3_v3(co2, mesh_vert(vm1, i, ns0 - 1, ns0 - 1)->co);
- add_v3_v3(co2, mesh_vert(vm1, i, ns0 - 1, ns0 + 1)->co);
+ for (i = 0; i < n_boundary; i++) {
+ add_v3_v3(co1, mesh_vert(vm_out, i, ns_in, ns_in - 1)->co);
+ add_v3_v3(co2, mesh_vert(vm_out, i, ns_in - 1, ns_in - 1)->co);
+ add_v3_v3(co2, mesh_vert(vm_out, i, ns_in - 1, ns_in + 1)->co);
}
copy_v3_v3(co, co1);
- mul_v3_fl(co, 1.0f / (float)n);
- madd_v3_v3fl(co, co2, beta / (2.0f * (float)n));
- madd_v3_v3fl(co, mesh_vert(vm0, 0, ns20, ns20)->co, gamma);
- for (i = 0; i < n; i++) {
- copy_v3_v3(mesh_vert(vm1, i, ns0, ns0)->co, co);
+ mul_v3_fl(co, 1.0f / (float)n_boundary);
+ madd_v3_v3fl(co, co2, beta / (2.0f * (float)n_boundary));
+ madd_v3_v3fl(co, mesh_vert(vm_in, 0, ns_in2, ns_in2)->co, gamma);
+ for (i = 0; i < n_boundary; i++) {
+ copy_v3_v3(mesh_vert(vm_out, i, ns_in, ns_in)->co, co);
}
- /* Final step: sample the boundary vertices at even parameter spacing */
- bndv = vm1->boundstart;
- for (i = 0; i < n; i++) {
- inext = (i + 1) % n;
- for (k = 0; k <= ns1; k++) {
- get_profile_point(bp, &bndv->profile, k, ns1, co);
- copy_v3_v3(mesh_vert(vm1, i, 0, k)->co, co);
- if (k >= ns0 && k < ns1) {
- copy_v3_v3(mesh_vert(vm1, inext, ns1 - k, 0)->co, co);
+ /* Final step: Copy the profile vertices to the VMesh's boundary */
+ bndv = vm_out->boundstart;
+ for (i = 0; i < n_boundary; i++) {
+ inext = (i + 1) % n_boundary;
+ for (k = 0; k <= ns_out; k++) {
+ get_profile_point(bp, &bndv->profile, k, ns_out, co);
+ copy_v3_v3(mesh_vert(vm_out, i, 0, k)->co, co);
+ if (k >= ns_in && k < ns_out) {
+ copy_v3_v3(mesh_vert(vm_out, inext, ns_out - k, 0)->co, co);
}
}
bndv = bndv->next;
}
- return vm1;
+ return vm_out;
}
/* Special case for cube corner, when r is PRO_SQUARE_R, meaning straight sides */
@@ -3686,7 +4024,7 @@ static VMesh *make_cube_corner_square(MemArena *mem_arena, int nseg)
ns2 = nseg / 2;
vm = new_adj_vmesh(mem_arena, 3, nseg, NULL);
- vm->count = 0; // reset, so following loop will end up with correct count
+ vm->count = 0; /* Reset, so the following loop will end up with correct count. */
for (i = 0; i < 3; i++) {
zero_v3(co);
co[i] = 1.0f;
@@ -3765,16 +4103,18 @@ static VMesh *make_cube_corner_adj_vmesh(BevelParams *bp)
int i, j, k, ns2;
float co[3], coc[3];
- if (r == PRO_SQUARE_R) {
- return make_cube_corner_square(mem_arena, nseg);
- }
- else if (r == PRO_SQUARE_IN_R) {
- return make_cube_corner_square_in(mem_arena, nseg);
+ if (!bp->use_custom_profile) {
+ if (r == PRO_SQUARE_R) {
+ return make_cube_corner_square(mem_arena, nseg);
+ }
+ else if (r == PRO_SQUARE_IN_R) {
+ return make_cube_corner_square_in(mem_arena, nseg);
+ }
}
- /* initial mesh has 3 sides, 2 segments */
+ /* Initial mesh has 3 sides and 2 segments on each side */
vm0 = new_adj_vmesh(mem_arena, 3, 2, NULL);
- vm0->count = 0; // reset, so following loop will end up with correct count
+ vm0->count = 0; /* Reset, so the following loop will end up with correct count. */
for (i = 0; i < 3; i++) {
zero_v3(co);
co[i] = 1.0f;
@@ -3787,20 +4127,24 @@ static VMesh *make_cube_corner_adj_vmesh(BevelParams *bp)
coc[(i + 1) % 3] = 1.0f;
coc[(i + 2) % 3] = 0.0f;
bndv->profile.super_r = r;
- copy_v3_v3(bndv->profile.coa, bndv->nv.co);
- copy_v3_v3(bndv->profile.cob, bndv->next->nv.co);
- copy_v3_v3(bndv->profile.midco, coc);
- copy_v3_v3(mesh_vert(vm0, i, 0, 0)->co, bndv->profile.coa);
- copy_v3_v3(bndv->profile.plane_co, bndv->profile.coa);
- cross_v3_v3v3(bndv->profile.plane_no, bndv->profile.coa, bndv->profile.cob);
+ copy_v3_v3(bndv->profile.start, bndv->nv.co);
+ copy_v3_v3(bndv->profile.end, bndv->next->nv.co);
+ copy_v3_v3(bndv->profile.middle, coc);
+ copy_v3_v3(mesh_vert(vm0, i, 0, 0)->co, bndv->profile.start);
+ copy_v3_v3(bndv->profile.plane_co, bndv->profile.start);
+ cross_v3_v3v3(bndv->profile.plane_no, bndv->profile.start, bndv->profile.end);
copy_v3_v3(bndv->profile.proj_dir, bndv->profile.plane_no);
- calculate_profile(bp, bndv);
+ /* No need to reverse the profile or use the miter profile spacing struct because this case
+ * isn't used with custom profiles. */
+ calculate_profile(bp, bndv, false, false);
+
+ /* Just building the boundaries here, so sample the profile halfway through */
get_profile_point(bp, &bndv->profile, 1, 2, mesh_vert(vm0, i, 0, 1)->co);
bndv = bndv->next;
}
/* center vertex */
- copy_v3_fl(co, M_SQRT1_3);
+ copy_v3_fl(co, (float)M_SQRT1_3);
if (nseg > 2) {
if (r > 1.5f) {
@@ -3843,7 +4187,8 @@ static int tri_corner_test(BevelParams *bp, BevVert *bv)
int i;
int in_plane_e = 0;
- if (bp->vertex_only) {
+ /* The superellipse snapping of this case isn't helpful with custom profiles enabled. */
+ if (bp->vertex_only || bp->use_custom_profile) {
return -1;
}
if (bv->vmesh->count != 3) {
@@ -3909,94 +4254,71 @@ static VMesh *tri_corner_adj_vmesh(BevelParams *bp, BevVert *bv)
return vm;
}
+/* Makes the mesh that replaces the original vertex, bounded by the profiles on the sides */
static VMesh *adj_vmesh(BevelParams *bp, BevVert *bv)
{
- int n, ns, i;
+ int n_bndv, nseg, i;
VMesh *vm0, *vm1;
- float co[3], coa[3], cob[3], dir[3];
+ float boundverts_center[3], original_vertex[3], negative_fullest[3], center_direction[3];
BoundVert *bndv;
MemArena *mem_arena = bp->mem_arena;
- float r, p, fullness;
- /* best fullness for circles, segs = 2,4,6,8,10 */
-#define CIRCLE_FULLNESS_SEGS 11
- static const float circle_fullness[CIRCLE_FULLNESS_SEGS] = {
- 0.0f, /* nsegs ==1 */
- 0.559f, /* 2 */
- 0.642f, /* 3 */
- 0.551f, /* 4 */
- 0.646f, /* 5 */
- 0.624f, /* 6 */
- 0.646f, /* 7 */
- 0.619f, /* 8 */
- 0.647f, /* 9 */
- 0.639f, /* 10 */
- 0.647f, /* 11 */
- };
+ float fullness;
- n = bv->vmesh->count;
+ n_bndv = bv->vmesh->count;
/* Same bevel as that of 3 edges of vert in a cube */
- if (n == 3 && tri_corner_test(bp, bv) != -1 && bp->pro_super_r != PRO_SQUARE_IN_R) {
+ if (n_bndv == 3 && tri_corner_test(bp, bv) != -1 && bp->pro_super_r != PRO_SQUARE_IN_R) {
return tri_corner_adj_vmesh(bp, bv);
}
- /* First construct an initial control mesh, with nseg==2 */
- ns = bv->vmesh->seg;
- vm0 = new_adj_vmesh(mem_arena, n, 2, bv->vmesh->boundstart);
+ /* First construct an initial control mesh, with nseg == 2. */
+ nseg = bv->vmesh->seg;
+ vm0 = new_adj_vmesh(mem_arena, n_bndv, 2, bv->vmesh->boundstart);
+ /* Find the center of the boundverts that make up the vmesh. */
bndv = vm0->boundstart;
- zero_v3(co);
- for (i = 0; i < n; i++) {
- /* Boundaries just divide input polygon edges into 2 even segments */
+ zero_v3(boundverts_center);
+ for (i = 0; i < n_bndv; i++) {
+ /* Boundaries just divide input polygon edges into 2 even segments. */
copy_v3_v3(mesh_vert(vm0, i, 0, 0)->co, bndv->nv.co);
get_profile_point(bp, &bndv->profile, 1, 2, mesh_vert(vm0, i, 0, 1)->co);
- add_v3_v3(co, bndv->nv.co);
+ add_v3_v3(boundverts_center, bndv->nv.co);
bndv = bndv->next;
}
- /* To place center vertex:
- * coa is original vertex
- * co is centroid of boundary corners
- * cob is reflection of coa in across co.
- * Calculate 'fullness' = fraction of way
- * from co to coa (if positive) or to cob (if negative).
- */
- copy_v3_v3(coa, bv->v->co);
- mul_v3_fl(co, 1.0f / (float)n);
- sub_v3_v3v3(cob, co, coa);
- add_v3_v3(cob, co);
-
- /* An offline optimization process found fullness that let to closest fit to sphere as
- * a function of r and ns (for case of cube corner) */
- r = bp->pro_super_r;
- p = bp->profile;
- if (r == PRO_LINE_R) {
- fullness = 0.0f;
- }
- else if (r == PRO_CIRCLE_R && ns > 0 && ns <= CIRCLE_FULLNESS_SEGS) {
- fullness = circle_fullness[ns - 1];
- }
- else {
- /* linear regression fit found best linear function, separately for even/odd segs */
- if (ns % 2 == 0) {
- fullness = 2.4506f * p - 0.00000300f * ns - 0.6266f;
+ mul_v3_fl(boundverts_center, 1.0f / (float)n_bndv);
+
+ /* To place the center vertex:
+ * 'negative_fullest' is the reflection of the original vertex across the boundverts' center.
+ * 'fullness' is the fraction of the way from the boundvert's centroid to to the original vertex
+ * (if positive) or to negative_fullest (if negative). */
+ copy_v3_v3(original_vertex, bv->v->co);
+ sub_v3_v3v3(negative_fullest, boundverts_center, original_vertex);
+ add_v3_v3(negative_fullest, boundverts_center);
+
+ /* Find the vertex mesh's start center with the profile's fullness */
+ fullness = bp->pro_spacing.fullness;
+ sub_v3_v3v3(center_direction, original_vertex, boundverts_center);
+ if (len_squared_v3(center_direction) > BEVEL_EPSILON_SQ) {
+ if (bp->use_custom_profile) {
+ fullness *= 2.0f;
+ madd_v3_v3v3fl(mesh_vert(vm0, 0, 1, 1)->co, negative_fullest, center_direction, fullness);
}
else {
- fullness = 2.3635f * p + 0.000152f * ns - 0.6060f;
+ madd_v3_v3v3fl(mesh_vert(vm0, 0, 1, 1)->co, boundverts_center, center_direction, fullness);
}
}
- sub_v3_v3v3(dir, coa, co);
- if (len_squared_v3(dir) > BEVEL_EPSILON_SQ) {
- madd_v3_v3fl(co, dir, fullness);
+ else {
+ copy_v3_v3(mesh_vert(vm0, 0, 1, 1)->co, boundverts_center);
}
- copy_v3_v3(mesh_vert(vm0, 0, 1, 1)->co, co);
vmesh_copy_equiv_verts(vm0);
+ /* Do the subdivision process to go from the two segment start mesh to the final vertex mesh. */
vm1 = vm0;
do {
vm1 = cubic_subdiv(bp, vm1);
- } while (vm1->seg < ns);
- if (vm1->seg != ns) {
- vm1 = interp_vmesh(bp, vm1, ns);
+ } while (vm1->seg < nseg);
+ if (vm1->seg != nseg) {
+ vm1 = interp_vmesh(bp, vm1, nseg);
}
return vm1;
}
@@ -4012,15 +4334,16 @@ static void snap_to_pipe_profile(BoundVert *vpipe, bool midline, float co[3])
Profile *pro = &vpipe->profile;
EdgeHalf *e = vpipe->ebev;
- copy_v3_v3(va, pro->coa);
- copy_v3_v3(vb, pro->cob);
+ copy_v3_v3(va, pro->start);
+ copy_v3_v3(vb, pro->end);
+ /* Get a plane with the normal pointing along the beveled edge */
sub_v3_v3v3(edir, e->e->v1->co, e->e->v2->co);
-
plane_from_point_normal_v3(plane, co, edir);
+
closest_to_plane_v3(va0, plane, va);
closest_to_plane_v3(vb0, plane, vb);
- closest_to_plane_v3(vmid0, plane, pro->midco);
+ closest_to_plane_v3(vmid0, plane, pro->middle);
if (make_unit_square_map(va0, vmid0, vb0, m)) {
/* Transform co and project it onto superellipse */
if (!invert_m4_m4(minv, m)) {
@@ -4030,6 +4353,7 @@ static void snap_to_pipe_profile(BoundVert *vpipe, bool midline, float co[3])
}
mul_v3_m4v3(p, minv, co);
snap_to_superellipsoid(p, pro->super_r, midline);
+
mul_v3_m4v3(snap, m, p);
copy_v3_v3(co, snap);
}
@@ -4040,38 +4364,123 @@ static void snap_to_pipe_profile(BoundVert *vpipe, bool midline, float co[3])
}
}
-/* See pipe_test for conditions that make 'pipe'; vpipe is the return value from that.
+/** See pipe_test for conditions that make 'pipe'; vpipe is the return value from that.
* We want to make an ADJ mesh but then snap the vertices to the profile in a plane
- * perpendicular to the pipes.
- * A tricky case is for the 'square' profiles and an even nseg: we want certain vertices
- * to snap to the midline on the pipe, not just to one plane or the other. */
+ * perpendicular to the pipes. */
static VMesh *pipe_adj_vmesh(BevelParams *bp, BevVert *bv, BoundVert *vpipe)
{
- int i, j, k, n, ns, ns2, ipipe1, ipipe2;
+#ifdef DEBUG_CUSTOM_PROFILE_PIPE
+ printf("PIPE ADJ VMESH\n");
+ float green[4] = {0.0f, 1.0f, 0.0f, 1.0f};
+ float blue[4] = {0.0f, 0.0f, 1.0f, 1.0f};
+ float red[4] = {1.0f, 0.0f, 0.0f, 1.0f};
+ float white[4] = {1.0f, 1.0f, 1.0f, 1.0f};
+ float *color;
+#endif
+ int i, j, k, n_bndv, ns, half_ns, ipipe1, ipipe2, ring;
VMesh *vm;
bool even, midline;
+ float *profile_point_pipe1, *profile_point_pipe2, f;
+ /* Some unecessary overhead running this subdivision with custom profile snapping later on. */
vm = adj_vmesh(bp, bv);
- /* Now snap all interior coordinates to be on the epipe profile */
- n = bv->vmesh->count;
+ /* Now snap all interior coordinates to be on the epipe profile. */
+ n_bndv = bv->vmesh->count;
ns = bv->vmesh->seg;
- ns2 = ns / 2;
+ half_ns = ns / 2;
even = (ns % 2) == 0;
ipipe1 = vpipe->index;
ipipe2 = vpipe->next->next->index;
- for (i = 0; i < n; i++) {
- for (j = 1; j <= ns2; j++) {
- for (k = 0; k <= ns2; k++) {
+
+#ifdef DEBUG_CUSTOM_PROFILE_PIPE
+ printf("ipipe1: %d\n", ipipe1);
+ printf("ipipe2: %d\n", ipipe2);
+#endif
+
+ for (i = 0; i < n_bndv; i++) {
+ for (j = 1; j <= half_ns; j++) {
+ for (k = 0; k <= half_ns; k++) {
if (!is_canon(vm, i, j, k)) {
continue;
}
- midline = even && k == ns2 && ((i == 0 && j == ns2) || (i == ipipe1 || i == ipipe2));
- snap_to_pipe_profile(vpipe, midline, mesh_vert(vm, i, j, k)->co);
+ if (bp->use_custom_profile) {
+ /* Find both profile vertices that correspond to this point. */
+ if (i == ipipe1 || i == ipipe2) {
+ if (n_bndv == 3 && i == ipipe1) {
+ /* This part of the vmesh is the triangular corner between the two pipe profiles. */
+ ring = max_ii(j, k);
+ profile_point_pipe2 = mesh_vert(vm, i, 0, ring)->co;
+ profile_point_pipe1 = mesh_vert(vm, i, ring, 0)->co;
+ /* End profile index increases with k on one side and j on the other. */
+ f = ((k < j) ? min_ff(j, k) : ((2.0f * ring) - j)) / (2.0f * ring);
+ }
+ else {
+ /* This is part of either pipe profile boundvert area in the 4-way intersection. */
+ profile_point_pipe1 = mesh_vert(vm, i, 0, k)->co;
+ profile_point_pipe2 = mesh_vert(vm, (i == ipipe1) ? ipipe2 : ipipe1, 0, ns - k)->co;
+ f = (float)j / (float)ns; /* The ring index brings us closer to the other side. */
+ }
+ }
+ else {
+ /* The profile vertices are on both ends of each of the side profile's rings. */
+ profile_point_pipe1 = mesh_vert(vm, i, j, 0)->co;
+ profile_point_pipe2 = mesh_vert(vm, i, j, ns)->co;
+ f = (float)k / (float)ns; /* Ring runs along the pipe, so segment is used here. */
+ }
+
+ /* Place the vertex by interpolatin between the two profile points using the factor. */
+ interp_v3_v3v3(mesh_vert(vm, i, j, k)->co, profile_point_pipe1, profile_point_pipe2, f);
+#ifdef DEBUG_CUSTOM_PROFILE_PIPE
+ printf("(%d, %d, %d)\n", i, j, k);
+ printf("f: %.3f\n", f);
+ printf("point 1: (%.3f, %.3f, %.3f)\n",
+ profile_point_pipe1[0],
+ profile_point_pipe1[1],
+ profile_point_pipe1[2]);
+ printf("point 2: (%.3f, %.3f, %.3f)\n",
+ profile_point_pipe2[0],
+ profile_point_pipe2[1],
+ profile_point_pipe2[2]);
+#endif
+ }
+ else {
+ /* A tricky case is for the 'square' profiles and an even nseg: we want certain
+ * vertices to snap to the midline on the pipe, not just to one plane or the other. */
+ midline = even && k == half_ns &&
+ ((i == 0 && j == half_ns) || (i == ipipe1 || i == ipipe2));
+ snap_to_pipe_profile(vpipe, midline, mesh_vert(vm, i, j, k)->co);
+ }
}
}
}
-
+#ifdef DEBUG_CUSTOM_PROFILE_PIPE
+ /* Draw the locations of all the vertices after the "snapping" process */
+ for (i = 0; i < n_bndv; i++) {
+ for (j = 1; j <= half_ns; j++) {
+ for (k = 1; k <= ns; k++) {
+ if (!is_canon(vm, i, j, k)) {
+ continue;
+ }
+ switch (i) {
+ case 0:
+ color = red;
+ break;
+ case 1:
+ color = green;
+ break;
+ case 2:
+ color = blue;
+ break;
+ case 3:
+ color = white;
+ break;
+ }
+ DRW_debug_sphere(mesh_vert(vm, i, j, k)->co, 0.01f, color);
+ }
+ }
+ }
+#endif
return vm;
}
@@ -4123,7 +4532,7 @@ static float snap_face_dist_squared(float *co, BMFace *f, BMEdge **r_snap_e, flo
BMEdge *e;
float closest[3];
- beste_d2 = 1e20;
+ beste_d2 = 1e20f;
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);
@@ -4243,7 +4652,7 @@ static void closer_v3_v3v3v3(float r[3], float a[3], float b[3], float v[3])
*/
static VMesh *square_out_adj_vmesh(BevelParams *bp, BevVert *bv)
{
- int n, ns, ns2, odd, i, j, k, ikind, im1, clstride, iprev, akind;
+ int n_bndv, ns, ns2, odd, i, j, k, ikind, im1, clstride, iprev, ang_kind;
float bndco[3], dir1[3], dir2[3], co1[3], co2[3], meet1[3], meet2[3], v1co[3], v2co[3];
float *on_edge_cur, *on_edge_prev, *p;
float ns2inv, finalfrac, ang;
@@ -4253,26 +4662,26 @@ static VMesh *square_out_adj_vmesh(BevelParams *bp, BevVert *bv)
float *centerline;
bool *cset, v1set, v2set;
- n = bv->vmesh->count;
+ n_bndv = bv->vmesh->count;
ns = bv->vmesh->seg;
ns2 = ns / 2;
odd = ns % 2;
ns2inv = 1.0f / (float)ns2;
- vm = new_adj_vmesh(bp->mem_arena, n, ns, bv->vmesh->boundstart);
+ vm = new_adj_vmesh(bp->mem_arena, n_bndv, ns, bv->vmesh->boundstart);
clstride = 3 * (ns2 + 1);
- centerline = MEM_mallocN(clstride * n * sizeof(float), "bevel");
- cset = MEM_callocN(n * sizeof(bool), "bevel");
+ centerline = MEM_mallocN((size_t)(clstride * n_bndv) * sizeof(float), "bevel");
+ cset = MEM_callocN((size_t)n_bndv * sizeof(bool), "bevel");
/* find on_edge, place on bndv[i]'s elast where offset line would meet,
* taking min-distance-to bv->v with position where next sector's offset line would meet */
bndv = vm->boundstart;
- for (i = 0; i < n; i++) {
+ for (i = 0; i < n_bndv; i++) {
copy_v3_v3(bndco, bndv->nv.co);
e1 = bndv->efirst;
e2 = bndv->elast;
- akind = 0;
+ ang_kind = ANGLE_STRAIGHT;
if (e1 && e2) {
- akind = edges_angle_kind(e1, e2, bv->v);
+ ang_kind = edges_angle_kind(e1, e2, bv->v);
}
if (bndv->is_patch_start) {
mid_v3_v3v3(centerline + clstride * i, bndv->nv.co, bndv->next->nv.co);
@@ -4288,13 +4697,13 @@ static VMesh *square_out_adj_vmesh(BevelParams *bp, BevVert *bv)
else if (bndv->is_arc_start) {
e1 = bndv->efirst;
e2 = bndv->next->efirst;
- copy_v3_v3(centerline + clstride * i, bndv->profile.midco);
+ copy_v3_v3(centerline + clstride * i, bndv->profile.middle);
bndv = bndv->next;
cset[i] = true;
i++;
/* leave cset[i] where it was - probably false, unless i == n - 1 */
}
- else if (akind < 0) {
+ else if (ang_kind == ANGLE_SMALLER) {
sub_v3_v3v3(dir1, e1->e->v1->co, e1->e->v2->co);
sub_v3_v3v3(dir2, e2->e->v1->co, e2->e->v2->co);
add_v3_v3v3(co1, bndco, dir1);
@@ -4321,7 +4730,7 @@ static VMesh *square_out_adj_vmesh(BevelParams *bp, BevVert *bv)
/* want on_edge[i] to be min dist to bv->v of v2co and the v1co of next iteration */
on_edge_cur = centerline + clstride * i;
- iprev = (i == 0) ? n - 1 : i - 1;
+ iprev = (i == 0) ? n_bndv - 1 : i - 1;
on_edge_prev = centerline + clstride * iprev;
if (v2set) {
if (cset[i]) {
@@ -4346,7 +4755,7 @@ static VMesh *square_out_adj_vmesh(BevelParams *bp, BevVert *bv)
}
/* Maybe not everything was set by the previous loop */
bndv = vm->boundstart;
- for (i = 0; i < n; i++) {
+ for (i = 0; i < n_bndv; i++) {
if (!cset[i]) {
on_edge_cur = centerline + clstride * i;
e1 = bndv->next->efirst;
@@ -4381,7 +4790,7 @@ static VMesh *square_out_adj_vmesh(BevelParams *bp, BevVert *bv)
/* fill in rest of centerlines by interpolation */
copy_v3_v3(co2, bv->v->co);
bndv = vm->boundstart;
- for (i = 0; i < n; i++) {
+ for (i = 0; i < n_bndv; i++) {
if (odd) {
ang = 0.5f * angle_v3v3v3(bndv->nv.co, co1, bndv->next->nv.co);
if (ang > BEVEL_SMALL_ANG) {
@@ -4389,7 +4798,7 @@ static VMesh *square_out_adj_vmesh(BevelParams *bp, BevVert *bv)
* such that the base of the triangle is 1.
* This is used in interpolation along centerline in odd case.
* To avoid too big a drop from bv, cap finalfrac a 0.8 arbitrarily */
- finalfrac = 0.5f / sin(ang);
+ finalfrac = 0.5f / sinf(ang);
if (finalfrac > 0.8f) {
finalfrac = 0.8f;
}
@@ -4412,9 +4821,9 @@ static VMesh *square_out_adj_vmesh(BevelParams *bp, BevVert *bv)
/* coords of edges and mid or near-mid line */
bndv = vm->boundstart;
- for (i = 0; i < n; i++) {
+ for (i = 0; i < n_bndv; i++) {
copy_v3_v3(co1, bndv->nv.co);
- copy_v3_v3(co2, centerline + clstride * (i == 0 ? n - 1 : i - 1));
+ copy_v3_v3(co2, centerline + clstride * (i == 0 ? n_bndv - 1 : i - 1));
for (j = 0; j < ns2 + odd; j++) {
interp_v3_v3v3(mesh_vert(vm, i, j, 0)->co, co1, co2, j * ns2inv);
}
@@ -4431,8 +4840,8 @@ static VMesh *square_out_adj_vmesh(BevelParams *bp, BevVert *bv)
/* fill in interior points by interpolation from edges to centerlines */
bndv = vm->boundstart;
- for (i = 0; i < n; i++) {
- im1 = (i == 0) ? n - 1 : i - 1;
+ for (i = 0; i < n_bndv; i++) {
+ im1 = (i == 0) ? n_bndv - 1 : i - 1;
for (j = 1; j < ns2 + odd; j++) {
for (k = 1; k <= ns2; k++) {
ikind = isect_line_line_v3(mesh_vert(vm, i, 0, k)->co,
@@ -4466,43 +4875,43 @@ static VMesh *square_out_adj_vmesh(BevelParams *bp, BevVert *bv)
return vm;
}
-/*
+/**
* 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,
- * using cubic subdivision, then make the BMVerts and the new faces. */
-static void bevel_build_rings(BevelParams *bp, BMesh *bm, BevVert *bv)
+ * using cubic subdivision, then make the BMVerts and the new faces.
+ */
+static void bevel_build_rings(BevelParams *bp, BMesh *bm, BevVert *bv, BoundVert *vpipe)
{
- int n, ns, ns2, odd, i, j, k, ring;
+ int n_bndv, ns, ns2, odd, i, j, k, ring;
VMesh *vm1, *vm;
- BoundVert *v;
+ BoundVert *bndv;
BMVert *bmv1, *bmv2, *bmv3, *bmv4;
BMFace *f, *f2, *r_f;
BMEdge *bme, *bme1, *bme2, *bme3;
EdgeHalf *e;
- BoundVert *vpipe;
int mat_nr = bp->mat_nr;
- n = bv->vmesh->count;
+ n_bndv = bv->vmesh->count;
ns = bv->vmesh->seg;
ns2 = ns / 2;
odd = ns % 2;
- BLI_assert(n >= 3 && ns > 1);
+ BLI_assert(n_bndv >= 3 && ns > 1);
- /* Add support for profiles in vertex only in-plane bevels */
+ /* Add support for profiles in vertex only in-plane bevels. */
if (bp->vertex_only) {
- v = bv->vmesh->boundstart;
+ bndv = bv->vmesh->boundstart;
do {
- Profile *pro = &v->profile;
+ Profile *pro = &bndv->profile;
+ copy_v3_v3(pro->middle, bv->v->co);
pro->super_r = bp->pro_super_r;
- copy_v3_v3(pro->midco, bv->v->co);
- calculate_profile(bp, v);
- v = v->next;
- } while (v != bv->vmesh->boundstart);
+ bool miter_profile = bp->use_custom_profile && (bndv->is_arc_start || bndv->is_patch_start);
+ /* Orientation doesn't matter when only beveling vertices */
+ calculate_profile(bp, bndv, false, miter_profile);
+ bndv = bndv->next;
+ } while (bndv != bv->vmesh->boundstart);
}
- vpipe = pipe_test(bv);
-
- if (bp->pro_super_r == PRO_SQUARE_R && bv->selcount >= 3 && !odd) {
+ if (bp->pro_super_r == PRO_SQUARE_R && bv->selcount >= 3 && !odd && !bp->use_custom_profile) {
vm1 = square_out_adj_vmesh(bp, bv);
}
else if (vpipe) {
@@ -4512,7 +4921,7 @@ static void bevel_build_rings(BevelParams *bp, BMesh *bm, BevVert *bv)
vm1 = tri_corner_adj_vmesh(bp, bv);
/* the PRO_SQUARE_IN_R profile has boundary edges that merge
* and no internal ring polys except possibly center ngon */
- if (bp->pro_super_r == PRO_SQUARE_IN_R) {
+ if (bp->pro_super_r == PRO_SQUARE_IN_R && !bp->use_custom_profile) {
build_square_in_vmesh(bp, bm, bv, vm1);
return;
}
@@ -4523,7 +4932,7 @@ static void bevel_build_rings(BevelParams *bp, BMesh *bm, BevVert *bv)
/* copy final vmesh into bv->vmesh, make BMVerts and BMFaces */
vm = bv->vmesh;
- for (i = 0; i < n; i++) {
+ for (i = 0; i < n_bndv; i++) {
for (j = 0; j <= ns2; j++) {
for (k = 0; k <= ns; k++) {
if (j == 0 && (k == 0 || k == ns)) {
@@ -4539,16 +4948,16 @@ static void bevel_build_rings(BevelParams *bp, BMesh *bm, BevVert *bv)
}
vmesh_copy_equiv_verts(vm);
/* make the polygons */
- v = vm->boundstart;
+ bndv = vm->boundstart;
do {
- i = v->index;
- f = boundvert_rep_face(v, NULL);
- f2 = boundvert_rep_face(v->next, NULL);
+ i = bndv->index;
+ f = boundvert_rep_face(bndv, NULL);
+ f2 = boundvert_rep_face(bndv->next, NULL);
if (bp->vertex_only) {
- e = v->efirst;
+ e = bndv->efirst;
}
else {
- e = v->ebev;
+ e = bndv->ebev;
}
bme = e ? e->e : NULL;
/* For odd ns, make polys with lower left corner at (i,j,k) for
@@ -4576,7 +4985,7 @@ static void bevel_build_rings(BevelParams *bp, BMesh *bm, BevVert *bv)
f2,
NULL,
NULL,
- v->next->efirst->e,
+ bndv->next->efirst->e,
bme,
mat_nr);
}
@@ -4617,8 +5026,8 @@ static void bevel_build_rings(BevelParams *bp, BMesh *bm, BevVert *bv)
else {
bme1 = k == ns2 - 1 ? bme : NULL;
bme3 = NULL;
- if (j == ns2 - 1 && v->prev->ebev) {
- bme3 = v->prev->ebev->e;
+ if (j == ns2 - 1 && bndv->prev->ebev) {
+ bme3 = bndv->prev->ebev->e;
}
bme2 = bme1 != NULL ? bme1 : bme3;
r_f = bev_create_quad_ex(
@@ -4628,14 +5037,14 @@ static void bevel_build_rings(BevelParams *bp, BMesh *bm, BevVert *bv)
record_face_kind(bp, r_f, F_VERT);
}
}
- } while ((v = v->next) != vm->boundstart);
+ } while ((bndv = bndv->next) != vm->boundstart);
/* Fix UVs along center lines if even number of segments */
if (!odd) {
- v = vm->boundstart;
+ bndv = vm->boundstart;
do {
- i = v->index;
- if (!v->any_seam) {
+ i = bndv->index;
+ if (!bndv->any_seam) {
for (ring = 1; ring < ns2; ring++) {
BMVert *v_uv = mesh_vert(vm, i, ring, ns2)->v;
if (v_uv) {
@@ -4643,7 +5052,7 @@ static void bevel_build_rings(BevelParams *bp, BMesh *bm, BevVert *bv)
}
}
}
- } while ((v = v->next) != vm->boundstart);
+ } while ((bndv = bndv->next) != vm->boundstart);
bmv1 = mesh_vert(vm, 0, ns2, ns2)->v;
if (bp->vertex_only || count_bound_vert_seams(bv) <= 1) {
bev_merge_uvs(bm, bmv1);
@@ -4656,6 +5065,179 @@ static void bevel_build_rings(BevelParams *bp, BMesh *bm, BevVert *bv)
}
}
+/** Builds the vertex mesh when the vertex mesh type is set to "cut off" with a face closing
+ * off each incoming edge's profile */
+/* HANS-TODO: Make cutoff VMesh work with outer miter != sharp. This should be possible but there
+ * are two problems currently:
+ * - Miter profiles don't have plane_no filled, so down direction is incorrect.
+ * - Indexing profile points of miters with (i, 0, k) seems to return zero except for the first
+ * and last profile point. */
+/* HANS-TODO: Use repface / edge arrays for UV interpolation properly. */
+static void bevel_build_cutoff(BevelParams *bp, BMesh *bm, BevVert *bv)
+{
+#ifdef DEBUG_CUSTOM_PROFILE_CUTOFF
+ printf("BEVEL BUILD CUTOFF\n");
+ int j;
+#endif
+ int i;
+ int n_bndv = bv->vmesh->count;
+ BoundVert *bndv;
+ float length;
+ float down_direction[3], new_vert[3];
+ bool build_center_face;
+ /* BMFace *repface; */
+ BMVert **face_bmverts = NULL;
+ BMEdge **bmedges = NULL;
+ BMFace **bmfaces = NULL;
+
+ /* Find the locations for the corner vertices at the bottom of the cutoff faces. */
+ bndv = bv->vmesh->boundstart;
+ do {
+ i = bndv->index;
+
+ /* Find the "down" direction for this side of the cutoff face. */
+ /* Find the direction along the intersection of the two adjecent profile normals. */
+ cross_v3_v3v3(down_direction, bndv->profile.plane_no, bndv->prev->profile.plane_no);
+ if (dot_v3v3(down_direction, bv->v->no) > 0.0f) {
+ negate_v3(down_direction);
+ }
+
+ /* Move down from the boundvert by average profile height from the two adjacent profiles. */
+ length = (bndv->profile.height / sqrtf(2.0f) + bndv->prev->profile.height / sqrtf(2.0f)) / 2;
+ madd_v3_v3v3fl(new_vert, bndv->nv.co, down_direction, length);
+
+ /* Use this location for this profile's first corner vert and the last profile's second. */
+ copy_v3_v3(mesh_vert(bv->vmesh, i, 1, 0)->co, new_vert);
+ copy_v3_v3(mesh_vert(bv->vmesh, bndv->prev->index, 1, 1)->co, new_vert);
+
+ } while ((bndv = bndv->next) != bv->vmesh->boundstart);
+
+#ifdef DEBUG_CUSTOM_PROFILE_CUTOFF
+ printf("Corner vertices:\n");
+ for (j = 0; j < n_bndv; j++) {
+ printf(" (%.3f, %.3f, %.3f)\n",
+ (double)mesh_vert(bv->vmesh, j, 1, 0)->co[0],
+ (double)mesh_vert(bv->vmesh, j, 1, 0)->co[1],
+ (double)mesh_vert(bv->vmesh, j, 1, 0)->co[2]);
+ }
+#endif
+
+ /* Disable the center face if the corner vertices share the same location. */
+ build_center_face = true;
+ if (n_bndv == 3) { /* Vertices only collapse with a 3-way VMesh. */
+ build_center_face &= len_squared_v3v3(mesh_vert(bv->vmesh, 0, 1, 0)->co,
+ mesh_vert(bv->vmesh, 1, 1, 0)->co) > BEVEL_EPSILON;
+ build_center_face &= len_squared_v3v3(mesh_vert(bv->vmesh, 0, 1, 0)->co,
+ mesh_vert(bv->vmesh, 2, 1, 0)->co) > BEVEL_EPSILON;
+ build_center_face &= len_squared_v3v3(mesh_vert(bv->vmesh, 1, 1, 0)->co,
+ mesh_vert(bv->vmesh, 2, 1, 0)->co) > BEVEL_EPSILON;
+ }
+#ifdef DEBUG_CUSTOM_PROFILE_CUTOFF
+ printf("build_center_face: %d\n", build_center_face);
+#endif
+
+ /* Create the corner vertex BMVerts. */
+ if (build_center_face) {
+ do {
+ i = bndv->index;
+ create_mesh_bmvert(bm, bv->vmesh, i, 1, 0, bv->v);
+ /* The second corner vertex for the previous profile shares this BMVert. */
+ mesh_vert(bv->vmesh, bndv->prev->index, 1, 1)->v = mesh_vert(bv->vmesh, i, 1, 0)->v;
+
+ } while ((bndv = bndv->next) != bv->vmesh->boundstart);
+ }
+ else {
+ /* Use the same BMVert for all of the corner vertices. */
+ create_mesh_bmvert(bm, bv->vmesh, 0, 1, 0, bv->v);
+ for (i = 1; i < n_bndv; i++) {
+ mesh_vert(bv->vmesh, i, 1, 0)->v = mesh_vert(bv->vmesh, 0, 1, 0)->v;
+ }
+ }
+
+ /* Build the profile cutoff faces. */
+ /* Extra one or two for corner vertices and one for last point along profile, or the size of the
+ * center face array if it's bigger. */
+#ifdef DEBUG_CUSTOM_PROFILE_CUTOFF
+ printf("Building profile cutoff faces.\n");
+#endif
+ face_bmverts = BLI_memarena_alloc(
+ bp->mem_arena, ((size_t)max_ii(bp->seg + 2 + build_center_face, n_bndv) * sizeof(BMVert *)));
+ bndv = bv->vmesh->boundstart;
+ do {
+ i = bndv->index;
+ BLI_array_staticdeclare(bmedges, BM_DEFAULT_NGON_STACK_SIZE);
+ BLI_array_staticdeclare(bmfaces, BM_DEFAULT_NGON_STACK_SIZE);
+
+ /* Add the first corner vertex under this boundvert */
+ face_bmverts[0] = mesh_vert(bv->vmesh, i, 1, 0)->v;
+
+#ifdef DEBUG_CUSTOM_PROFILE_CUTOFF
+ printf("Profile Number %d:\n", i);
+ if (bndv->is_patch_start || bndv->is_arc_start) {
+ printf(" Miter profile\n");
+ }
+ printf(" Corner 1: (%0.3f, %0.3f, %0.3f)\n",
+ (double)mesh_vert(bv->vmesh, i, 1, 0)->co[0],
+ (double)mesh_vert(bv->vmesh, i, 1, 0)->co[1],
+ (double)mesh_vert(bv->vmesh, i, 1, 0)->co[2]);
+#endif
+
+ /* Add profile point vertices to the face, including the last one. */
+ for (int k = 0; k < bp->seg + 1; k++) {
+ face_bmverts[k + 1] = mesh_vert(bv->vmesh, i, 0, k)->v; /* Leave room for first vert. */
+#ifdef DEBUG_CUSTOM_PROFILE_CUTOFF
+ printf(" Profile %d: (%0.3f, %0.3f, %0.3f)\n",
+ k,
+ (double)mesh_vert(bv->vmesh, i, 0, k)->co[0],
+ (double)mesh_vert(bv->vmesh, i, 0, k)->co[1],
+ (double)mesh_vert(bv->vmesh, i, 0, k)->co[2]);
+#endif
+ }
+
+ /* Add the second corner vert to complete the bottom of the face. */
+ if (build_center_face) {
+ face_bmverts[bp->seg + 2] = mesh_vert(bv->vmesh, i, 1, 1)->v;
+#ifdef DEBUG_CUSTOM_PROFILE_CUTOFF
+ printf(" Corner 2: (%0.3f, %0.3f, %0.3f)\n",
+ (double)mesh_vert(bv->vmesh, i, 1, 1)->co[0],
+ (double)mesh_vert(bv->vmesh, i, 1, 1)->co[1],
+ (double)mesh_vert(bv->vmesh, i, 1, 1)->co[2]);
+#endif
+ }
+
+ /* Create the profile cutoff face for this boundvert. */
+ /* repface = boundvert_rep_face(bndv, NULL); */
+ bev_create_ngon(bm,
+ face_bmverts,
+ bp->seg + 2 + build_center_face,
+ bmfaces,
+ NULL,
+ bmedges,
+ bp->mat_nr,
+ true);
+
+ BLI_array_free(bmedges);
+ BLI_array_free(bmfaces);
+ } while ((bndv = bndv->next) != bv->vmesh->boundstart);
+
+ /* Create the bottom face if it should be built, reusing previous face_bmverts allocation. */
+ if (build_center_face) {
+ BLI_array_staticdeclare(bmedges, BM_DEFAULT_NGON_STACK_SIZE);
+ BLI_array_staticdeclare(bmfaces, BM_DEFAULT_NGON_STACK_SIZE);
+
+ /* Add all of the corner vertices to this face. */
+ for (i = 0; i < n_bndv; i++) {
+ /* Add verts from each cutoff face. */
+ face_bmverts[i] = mesh_vert(bv->vmesh, i, 1, 0)->v;
+ }
+ /* BLI_array_append(bmfaces, repface); */
+ bev_create_ngon(bm, face_bmverts, n_bndv, bmfaces, NULL, bmedges, bp->mat_nr, true);
+
+ BLI_array_free(bmedges);
+ BLI_array_free(bmfaces);
+ }
+}
+
/* 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 function is called when the only inside-of-frep vertex is vm->boundstart.
@@ -4690,71 +5272,72 @@ static bool is_bad_uv_poly(BevVert *bv, BMFace *frep)
static BMFace *bevel_build_poly(BevelParams *bp, BMesh *bm, BevVert *bv)
{
- BMFace *f, *frep, *frep2;
+ BMFace *f, *repface, *frep2;
int n, k;
VMesh *vm = bv->vmesh;
- BoundVert *v;
- 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);
+ BoundVert *bndv;
+ BMEdge *repface_e1, *repface_e2, *frep_e;
+ BMVert **bmverts = NULL;
+ BMEdge **bmedges = NULL;
+ BMFace **bmfaces = NULL;
+ BLI_array_staticdeclare(bmverts, BM_DEFAULT_NGON_STACK_SIZE);
+ BLI_array_staticdeclare(bmedges, BM_DEFAULT_NGON_STACK_SIZE);
+ BLI_array_staticdeclare(bmfaces, BM_DEFAULT_NGON_STACK_SIZE);
if (bv->any_seam) {
- frep = boundvert_rep_face(vm->boundstart, &frep2);
- if (frep2 && frep && is_bad_uv_poly(bv, frep)) {
- frep = frep2;
+ repface = boundvert_rep_face(vm->boundstart, &frep2);
+ if (frep2 && repface && is_bad_uv_poly(bv, repface)) {
+ repface = frep2;
}
- get_incident_edges(frep, bv->v, &frep_e1, &frep_e2);
+ get_incident_edges(repface, bv->v, &repface_e1, &repface_e2);
}
else {
- frep = NULL;
- frep_e1 = frep_e2 = NULL;
+ repface = NULL;
+ repface_e1 = repface_e2 = NULL;
}
- v = vm->boundstart;
+ bndv = 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);
- 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);
+ BLI_array_append(bmverts, bndv->nv.v);
+ if (repface) {
+ BLI_array_append(bmfaces, repface);
+ frep_e = find_closer_edge(bndv->nv.v->co, repface_e1, repface_e2);
+ BLI_array_append(bmedges, n > 0 ? frep_e : NULL);
}
else {
- BLI_array_append(vf, boundvert_rep_face(v, NULL));
- BLI_array_append(ve, NULL);
+ BLI_array_append(bmfaces, boundvert_rep_face(bndv, NULL));
+ BLI_array_append(bmedges, 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);
- 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);
+ if (bndv->ebev && bndv->ebev->seg > 1) {
+ for (k = 1; k < bndv->ebev->seg; k++) {
+ BLI_array_append(bmverts, mesh_vert(vm, bndv->index, 0, k)->v);
+ if (repface) {
+ BLI_array_append(bmfaces, repface);
+ frep_e = find_closer_edge(
+ mesh_vert(vm, bndv->index, 0, k)->v->co, repface_e1, repface_e2);
+ BLI_array_append(bmedges, k < bndv->ebev->seg / 2 ? NULL : frep_e);
}
else {
- BLI_array_append(vf, boundvert_rep_face(v, NULL));
- BLI_array_append(ve, NULL);
+ BLI_array_append(bmfaces, boundvert_rep_face(bndv, NULL));
+ BLI_array_append(bmedges, NULL);
}
n++;
}
}
- } while ((v = v->next) != vm->boundstart);
+ } while ((bndv = bndv->next) != vm->boundstart);
if (n > 2) {
- f = bev_create_ngon(bm, vv, n, vf, frep, ve, bp->mat_nr, true);
+ f = bev_create_ngon(bm, bmverts, n, bmfaces, repface, bmedges, bp->mat_nr, true);
record_face_kind(bp, f, F_VERT);
}
else {
f = NULL;
}
- BLI_array_free(vv);
- BLI_array_free(vf);
- BLI_array_free(ve);
+ BLI_array_free(bmverts);
+ BLI_array_free(bmedges);
+ BLI_array_free(bmfaces);
return f;
}
@@ -4817,6 +5400,7 @@ static void bevel_build_trifan(BevelParams *bp, BMesh *bm, BevVert *bv)
* we have to make it here. */
static void bevel_vert_two_edges(BevelParams *bp, BMesh *bm, BevVert *bv)
{
+
VMesh *vm = bv->vmesh;
BMVert *v1, *v2;
BMEdge *e_eg, *bme;
@@ -4832,18 +5416,20 @@ static void bevel_vert_two_edges(BevelParams *bp, BMesh *bm, BevVert *bv)
ns = vm->seg;
if (ns > 1) {
- /* Set up profile parameters */
+ /* Set up profile parameters. */
bndv = vm->boundstart;
pro = &bndv->profile;
pro->super_r = bp->pro_super_r;
- copy_v3_v3(pro->coa, v1->co);
- copy_v3_v3(pro->cob, v2->co);
- copy_v3_v3(pro->midco, bv->v->co);
- /* don't use projection */
+ copy_v3_v3(pro->start, v1->co);
+ copy_v3_v3(pro->end, v2->co);
+ copy_v3_v3(pro->middle, bv->v->co);
+ /* Don't use projection. */
zero_v3(pro->plane_co);
zero_v3(pro->plane_no);
zero_v3(pro->proj_dir);
- calculate_profile(bp, bndv);
+ /* there's no orientation chain to continue so the orientation of the bevel doesn't matter. */
+ calculate_profile(bp, bndv, false, false);
+
for (k = 1; k < ns; k++) {
get_profile_point(bp, pro, k, ns, co);
copy_v3_v3(mesh_vert(vm, 0, 0, k)->co, co);
@@ -4874,84 +5460,129 @@ static void bevel_vert_two_edges(BevelParams *bp, BMesh *bm, BevVert *bv)
* for the boundary and the interior of the vertex mesh. */
static void build_vmesh(BevelParams *bp, BMesh *bm, BevVert *bv)
{
- MemArena *mem_arena = bp->mem_arena;
VMesh *vm = bv->vmesh;
- BoundVert *v, *weld1, *weld2;
+ BoundVert *bndv, *weld1, *weld2, *vpipe;
int n, ns, ns2, i, k, weld;
- float *va, *vb, co[3];
+ float *v_weld1, *v_weld2, co[3];
n = vm->count;
ns = vm->seg;
ns2 = ns / 2;
- vm->mesh = (NewVert *)BLI_memarena_alloc(mem_arena, n * (ns2 + 1) * (ns + 1) * sizeof(NewVert));
+ vm->mesh = (NewVert *)BLI_memarena_alloc(bp->mem_arena,
+ (size_t)(n * (ns2 + 1) * (ns + 1)) * sizeof(NewVert));
- /* special case: two beveled ends welded together */
+ /* Special case: just two beveled edges welded together. */
weld = (bv->selcount == 2) && (vm->count == 2);
- weld1 = weld2 = NULL; /* will hold two BoundVerts involved in weld */
+ weld1 = weld2 = NULL; /* Will hold two BoundVerts involved in weld. */
- /* make (i, 0, 0) mesh verts for all i */
- v = vm->boundstart;
+ /* Make (i, 0, 0) mesh verts for all i boundverts. */
+ bndv = vm->boundstart;
do {
- i = v->index;
- copy_v3_v3(mesh_vert(vm, i, 0, 0)->co, v->nv.co);
- create_mesh_bmvert(bm, vm, i, 0, 0, bv->v);
- v->nv.v = mesh_vert(vm, i, 0, 0)->v;
- if (weld && v->ebev) {
+ i = bndv->index;
+ copy_v3_v3(mesh_vert(vm, i, 0, 0)->co, bndv->nv.co); /* Mesh NewVert to boundary NewVert. */
+ create_mesh_bmvert(bm, vm, i, 0, 0, bv->v); /* Create BMVert for that NewVert. */
+ bndv->nv.v = mesh_vert(vm, i, 0, 0)->v; /* Use the BMVert for the BoundVert's NewVert. */
+
+ /* Find boundverts and move profile planes if this is a weld case. */
+ if (weld && bndv->ebev) {
if (!weld1) {
- weld1 = v;
+ weld1 = bndv;
}
- else {
- weld2 = v;
- move_weld_profile_planes(bv, weld1, weld2);
- calculate_profile(bp, weld1);
- calculate_profile(bp, weld2);
+ else { /* Get the last of the two BoundVerts. */
+ weld2 = bndv;
+ move_weld_profile_planes(bv, weld1, weld2); /* Profile recalculated in next loop. */
}
}
- } while ((v = v->next) != vm->boundstart);
+ } while ((bndv = bndv->next) != vm->boundstart);
- /* copy other ends to (i, 0, ns) for all i, and fill in profiles for edges */
- v = vm->boundstart;
+ /* Create new vertices and place them based on the profiles. */
+ /* Copy other ends to (i, 0, ns) for all i, and fill in profiles for edges. */
+ bndv = vm->boundstart;
do {
- i = v->index;
- copy_mesh_vert(vm, i, 0, ns, v->next->index, 0, 0);
- for (k = 1; k < ns; k++) {
- if (v->ebev && vm->mesh_kind != M_ADJ) {
- get_profile_point(bp, &v->profile, k, ns, co);
- copy_v3_v3(mesh_vert(vm, i, 0, k)->co, co);
- if (!weld) {
- create_mesh_bmvert(bm, vm, i, 0, k, bv->v);
+ i = bndv->index;
+ /* bndv's last vert along the boundary arc is the the first of the next BoundVert's arc. */
+ copy_mesh_vert(vm, i, 0, ns, bndv->next->index, 0, 0);
+
+ /* Fix the profile orientations if it's not a miter profile. */
+ if (bp->use_custom_profile && !bndv->is_arc_start && !bndv->is_patch_start) {
+ calculate_profile(bp, bndv, !bndv->is_profile_start, false);
+ }
+ if (vm->mesh_kind != M_ADJ) {
+ for (k = 1; k < ns; k++) {
+ if (bndv->ebev) {
+ get_profile_point(bp, &bndv->profile, k, ns, co);
+ copy_v3_v3(mesh_vert(vm, i, 0, k)->co, co); /* Get NewVert location from profile coord */
+ if (!weld) {
+ /* This is done later with (possibly) better positions for the weld case. */
+ create_mesh_bmvert(bm, vm, i, 0, k, bv->v);
+ }
+ }
+ else if (n == 2 && !bndv->ebev) {
+ /* case of one edge beveled and this is the v without ebev */
+ /* want to copy the verts from other v, in reverse order */
+ copy_mesh_vert(bv->vmesh, i, 0, k, 1 - i, 0, ns - k);
}
- }
- else if (n == 2 && !v->ebev && vm->mesh_kind != M_ADJ) {
- /* case of one edge beveled and this is the v without ebev */
- /* want to copy the verts from other v, in reverse order */
- copy_mesh_vert(vm, i, 0, k, 1 - i, 0, ns - k);
}
}
- } while ((v = v->next) != vm->boundstart);
+ } while ((bndv = bndv->next) != vm->boundstart);
+ /* Build the profile for the weld case (just a connection between the two boundverts). */
if (weld) {
- vm->mesh_kind = M_NONE;
+ bv->vmesh->mesh_kind = M_NONE;
for (k = 1; k < ns; k++) {
- va = mesh_vert(vm, weld1->index, 0, k)->co;
- vb = mesh_vert(vm, weld2->index, 0, ns - k)->co;
- /* if one of the profiles is on a flat plane,
- * just use the boundary point of the other */
- if (weld1->profile.super_r == PRO_LINE_R && weld2->profile.super_r != PRO_LINE_R) {
- copy_v3_v3(co, vb);
- }
- else if (weld2->profile.super_r == PRO_LINE_R && weld1->profile.super_r != PRO_LINE_R) {
- copy_v3_v3(co, va);
+ v_weld1 = mesh_vert(bv->vmesh, weld1->index, 0, k)->co;
+ v_weld2 = mesh_vert(bv->vmesh, weld2->index, 0, ns - k)->co;
+ if (bp->use_custom_profile) {
+ /* Don't bother with special case profile check from below. */
+ mid_v3_v3v3(co, v_weld1, v_weld2);
}
else {
- mid_v3_v3v3(co, va, vb);
+ /* Use the point from the other profile if one is in a special case. */
+ if (weld1->profile.super_r == PRO_LINE_R && weld2->profile.super_r != PRO_LINE_R) {
+ copy_v3_v3(co, v_weld2);
+ }
+ else if (weld2->profile.super_r == PRO_LINE_R && weld1->profile.super_r != PRO_LINE_R) {
+ copy_v3_v3(co, v_weld1);
+ }
+ else {
+ /* In case the profiles aren't snapped to the same plane, use their midpoint. */
+ mid_v3_v3v3(co, v_weld1, v_weld2);
+ }
}
- copy_v3_v3(mesh_vert(vm, weld1->index, 0, k)->co, co);
- create_mesh_bmvert(bm, vm, weld1->index, 0, k, bv->v);
+ copy_v3_v3(mesh_vert(bv->vmesh, weld1->index, 0, k)->co, co);
+ create_mesh_bmvert(bm, bv->vmesh, weld1->index, 0, k, bv->v);
}
for (k = 1; k < ns; k++) {
- copy_mesh_vert(vm, weld2->index, 0, ns - k, weld1->index, 0, k);
+ copy_mesh_vert(bv->vmesh, weld2->index, 0, ns - k, weld1->index, 0, k);
+ }
+ }
+#ifdef DEBUG_CUSTOM_PROFILE_WELD
+ if (weld && ns > 1) {
+ printf("Weld1 profile coordinates:\n");
+ for (k = 0; k < ns; k++) {
+ printf("%0.4f, %0.4f, %0.4f\n",
+ (double)weld1->profile.prof_co[3 * k],
+ (double)weld1->profile.prof_co[3 * k + 1],
+ (double)weld1->profile.prof_co[3 * k + 2]);
+ }
+ printf("Weld2 profile coordinates\n");
+ for (k = 0; k < ns; k++) {
+ printf("%0.4f, %0.4f, %0.4f\n",
+ (double)weld2->profile.prof_co[3 * k],
+ (double)weld2->profile.prof_co[3 * k + 1],
+ (double)weld2->profile.prof_co[3 * k + 2]);
+ }
+ }
+#endif
+
+ /* Make sure the pipe case ADJ mesh is used for both the "Grid Fill" (ADJ) and cutoff options. */
+ vpipe = NULL;
+ if ((vm->count == 3 || vm->count == 4) && bp->seg > 1) {
+ /* Result is passed to bevel_build_rings to avoid overhead. */
+ vpipe = pipe_test(bv);
+ if (vpipe) {
+ vm->mesh_kind = M_ADJ;
}
}
@@ -4965,11 +5596,13 @@ static void build_vmesh(BevelParams *bp, BMesh *bm, BevVert *bv)
bevel_build_poly(bp, bm, bv);
break;
case M_ADJ:
- bevel_build_rings(bp, bm, bv);
+ bevel_build_rings(bp, bm, bv, vpipe);
break;
case M_TRI_FAN:
bevel_build_trifan(bp, bm, bv);
break;
+ case M_CUTOFF:
+ bevel_build_cutoff(bp, bm, bv);
}
}
@@ -5229,9 +5862,7 @@ static void find_bevel_edge_order(BMesh *bm, BevVert *bv, BMEdge *first_bme)
}
}
-/*
- * Construction around the vertex
- */
+/* Construction around the vertex */
static BevVert *bevel_vert_construct(BMesh *bm, BevelParams *bp, BMVert *v)
{
BMEdge *bme;
@@ -5504,7 +6135,7 @@ static BevVert *bevel_vert_construct(BMesh *bm, BevelParams *bp, BMVert *v)
return bv;
}
-/* Face f has at least one beveled vertex. Rebuild f */
+/* Face f has at least one beveled vertex. Rebuild f */
static bool bev_rebuild_polygon(BMesh *bm, BevelParams *bp, BMFace *f)
{
BMIter liter, eiter, fiter;
@@ -5849,7 +6480,7 @@ static void weld_cross_attrs_copy(BMesh *bm, BevVert *bv, VMesh *vm, int vmindex
}
BLI_assert(bme_prev && bme_next);
- /* want seams and sharp edges to cross only if that way on both sides */
+ /* Want seams and sharp edges to cross only if that way on both sides. */
disable_seam = BM_elem_flag_test(bme_prev, BM_ELEM_SEAM) !=
BM_elem_flag_test(bme_next, BM_ELEM_SEAM);
enable_smooth = BM_elem_flag_test(bme_prev, BM_ELEM_SMOOTH) !=
@@ -5869,8 +6500,8 @@ static void weld_cross_attrs_copy(BMesh *bm, BevVert *bv, VMesh *vm, int vmindex
}
}
-/*
- * Build the polygons along the selected Edge
+/**
+ * Build the bevel polygons along the selected Edge.
*/
static void bevel_build_edge_polygons(BMesh *bm, BevelParams *bp, BMEdge *bme)
{
@@ -6269,50 +6900,131 @@ static void find_even_superellipse_chords(int n, float r, double *xvals, double
find_even_superellipse_chords_general(n, r, xvals, yvals);
}
-/* The superellipse used for multisegment profiles does not
- * have a closed-form way to generate evenly spaced points
- * along an arc. We use an expensive search procedure to find
- * the parameter values that lead to bp->seg even chords.
- * We also want spacing for a number of segments that is
- * a power of 2 >= bp->seg (but at least 4).
- * Use doubles because otherwise we cannot come close to float
- * precision for final results. */
-static void set_profile_spacing(BevelParams *bp)
+/** Find the profile's "fullness," which is the fraction of the space it takes up way from the
+ * boundvert's centroid to to the original vertex for a non-custom profile, or in the case of a
+ * custom profile, the average "height" of the profile points along its centerline. */
+static float find_profile_fullness(BevelParams *bp)
+{
+ float fullness;
+ int nseg = bp->seg;
+
+ /* Precalculated fullness for circle profile radius and more common low seg values. */
+#define CIRCLE_FULLNESS_SEGS 11
+ static const float circle_fullness[CIRCLE_FULLNESS_SEGS] = {
+ 0.0f, /* nsegs == 1 */
+ 0.559f, /* 2 */
+ 0.642f, /* 3 */
+ 0.551f, /* 4 */
+ 0.646f, /* 5 */
+ 0.624f, /* 6 */
+ 0.646f, /* 7 */
+ 0.619f, /* 8 */
+ 0.647f, /* 9 */
+ 0.639f, /* 10 */
+ 0.647f, /* 11 */
+ };
+
+ if (bp->use_custom_profile) {
+ /* Set fullness to the average "height" of the profile's sampled points. */
+ fullness = 0.0f;
+ for (int i = 0; i < nseg; i++) { /* Don't use the end points. */
+ fullness += (float)(bp->pro_spacing.xvals[i] + bp->pro_spacing.yvals[i]) / (2.0f * nseg);
+ }
+ }
+ else {
+ /* An offline optimization process found fullness that led to closest fit to sphere as
+ * a function of r and ns (for case of cube corner). */
+ if (bp->pro_super_r == PRO_LINE_R) {
+ fullness = 0.0f;
+ }
+ else if (bp->pro_super_r == PRO_CIRCLE_R && nseg > 0 && nseg <= CIRCLE_FULLNESS_SEGS) {
+ fullness = circle_fullness[nseg - 1];
+ }
+ else {
+ /* Linear regression fit found best linear function, separately for even/odd segs. */
+ if (nseg % 2 == 0) {
+ fullness = 2.4506f * bp->profile - 0.00000300f * nseg - 0.6266f;
+ }
+ else {
+ fullness = 2.3635f * bp->profile + 0.000152f * nseg - 0.6060f;
+ }
+ }
+ }
+ return fullness;
+}
+
+/** Fills the ProfileSpacing struct with the 2D coordinates for the profile's vertices.
+ * The superellipse used for multisegment profiles does not have a closed-form way
+ * to generate evenly spaced points along an arc. We use an expensive search procedure
+ * to find the parameter values that lead to bp->seg even chords.
+ * We also want spacing for a number of segments that is a power of 2 >= bp->seg (but at least 4).
+ * Use doubles because otherwise we cannot come close to float precision for final results.
+ * \param pro_spacing: The struct to fill. Changes depending on whether there needs
+ to be a separate miter profile. */
+static void set_profile_spacing(BevelParams *bp, ProfileSpacing *pro_spacing, bool custom)
{
int seg, seg_2;
seg = bp->seg;
+ seg_2 = power_of_2_max_i(bp->seg);
if (seg > 1) {
- bp->pro_spacing.xvals = (double *)BLI_memarena_alloc(bp->mem_arena,
- (seg + 1) * sizeof(double));
- bp->pro_spacing.yvals = (double *)BLI_memarena_alloc(bp->mem_arena,
- (seg + 1) * sizeof(double));
- find_even_superellipse_chords(
- seg, bp->pro_super_r, bp->pro_spacing.xvals, bp->pro_spacing.yvals);
- seg_2 = power_of_2_max_i(bp->seg);
+ /* Sample the input number of segments. */
+ pro_spacing->xvals = (double *)BLI_memarena_alloc(bp->mem_arena,
+ (size_t)(seg + 1) * sizeof(double));
+ pro_spacing->yvals = (double *)BLI_memarena_alloc(bp->mem_arena,
+ (size_t)(seg + 1) * sizeof(double));
+ if (custom) {
+ /* Make sure the curve profile's sample table is full. */
+ if (bp->custom_profile->segments_len != seg || !bp->custom_profile->segments) {
+ BKE_curveprofile_initialize((CurveProfile *)bp->custom_profile, (short)seg);
+ }
+
+ /* Copy segment locations into the profile spacing struct. */
+ for (int i = 0; i < seg + 1; i++) {
+ pro_spacing->xvals[i] = (double)bp->custom_profile->segments[i].y;
+ pro_spacing->yvals[i] = (double)bp->custom_profile->segments[i].x;
+ }
+ }
+ else {
+ find_even_superellipse_chords(seg, bp->pro_super_r, pro_spacing->xvals, pro_spacing->yvals);
+ }
+
+ /* Sample the seg_2 segments used for subdividing the vertex meshes. */
if (seg_2 == 2) {
seg_2 = 4;
}
bp->pro_spacing.seg_2 = seg_2;
if (seg_2 == seg) {
- bp->pro_spacing.xvals_2 = bp->pro_spacing.xvals;
- bp->pro_spacing.yvals_2 = bp->pro_spacing.yvals;
+ pro_spacing->xvals_2 = pro_spacing->xvals;
+ pro_spacing->yvals_2 = pro_spacing->yvals;
}
else {
- bp->pro_spacing.xvals_2 = (double *)BLI_memarena_alloc(bp->mem_arena,
- (seg_2 + 1) * sizeof(double));
- bp->pro_spacing.yvals_2 = (double *)BLI_memarena_alloc(bp->mem_arena,
- (seg_2 + 1) * sizeof(double));
- find_even_superellipse_chords(
- seg_2, bp->pro_super_r, bp->pro_spacing.xvals_2, bp->pro_spacing.yvals_2);
+ pro_spacing->xvals_2 = (double *)BLI_memarena_alloc(bp->mem_arena,
+ (size_t)(seg_2 + 1) * sizeof(double));
+ pro_spacing->yvals_2 = (double *)BLI_memarena_alloc(bp->mem_arena,
+ (size_t)(seg_2 + 1) * sizeof(double));
+ if (custom) {
+ /* Make sure the curve profile widget's sample table is full of the seg_2 samples. */
+ BKE_curveprofile_initialize((CurveProfile *)bp->custom_profile, (short)seg_2);
+
+ /* Copy segment locations into the profile spacing struct. */
+ for (int i = 0; i < seg_2 + 1; i++) {
+ pro_spacing->xvals_2[i] = (double)bp->custom_profile->segments[i].y;
+ pro_spacing->yvals_2[i] = (double)bp->custom_profile->segments[i].x;
+ }
+ }
+ else {
+ find_even_superellipse_chords(
+ seg_2, bp->pro_super_r, pro_spacing->xvals_2, pro_spacing->yvals_2);
+ }
}
}
- else {
- bp->pro_spacing.xvals = NULL;
- bp->pro_spacing.yvals = NULL;
- bp->pro_spacing.xvals_2 = NULL;
- bp->pro_spacing.yvals_2 = NULL;
- bp->pro_spacing.seg_2 = 0;
+ else { /* Only 1 segment, we don't need any profile information */
+ pro_spacing->xvals = NULL;
+ pro_spacing->yvals = NULL;
+ pro_spacing->xvals_2 = NULL;
+ pro_spacing->yvals_2 = NULL;
+ pro_spacing->seg_2 = 0;
}
}
@@ -6320,7 +7032,7 @@ static void set_profile_spacing(BevelParams *bp)
* Assume we have a situation like:
*
* a d
- * \ /
+ * \ /
* A \ / C
* \ th1 th2/
* b---------c
@@ -6566,7 +7278,10 @@ void BM_mesh_bevel(BMesh *bm,
const int miter_outer,
const int miter_inner,
const float spread,
- const float smoothresh)
+ const float smoothresh,
+ const bool use_custom_profile,
+ const struct CurveProfile *custom_profile,
+ const int vmesh_method)
{
BMIter iter, liter;
BMVert *v, *v_next;
@@ -6578,9 +7293,9 @@ void BM_mesh_bevel(BMesh *bm,
bp.offset = offset;
bp.offset_type = offset_type;
- bp.seg = segments;
+ bp.seg = (int)segments;
bp.profile = profile;
- bp.pro_super_r = -log(2.0) / log(sqrt(profile)); /* convert to superellipse exponent */
+ bp.pro_super_r = -logf(2.0) / logf(sqrtf(profile)); /* convert to superellipse exponent */
bp.vertex_only = vertex_only;
bp.use_weights = use_weights;
bp.loop_slide = loop_slide;
@@ -6598,6 +7313,15 @@ void BM_mesh_bevel(BMesh *bm,
bp.spread = spread;
bp.smoothresh = smoothresh;
bp.face_hash = NULL;
+ bp.use_custom_profile = use_custom_profile;
+ bp.custom_profile = custom_profile;
+ bp.vmesh_method = vmesh_method;
+
+ /* Disable the miters with the cutoff vertex mesh method, this combination isn't useful anyway */
+ if (bp.vmesh_method == BEVEL_VMESH_CUTOFF) {
+ bp.miter_outer = BEVEL_MITER_SHARP;
+ bp.miter_inner = BEVEL_MITER_SHARP;
+ }
if (profile >= 0.950f) { /* r ~ 692, so PRO_SQUARE_R is 1e4 */
bp.pro_super_r = PRO_SQUARE_R;
@@ -6608,7 +7332,18 @@ void BM_mesh_bevel(BMesh *bm,
bp.vert_hash = BLI_ghash_ptr_new(__func__);
bp.mem_arena = BLI_memarena_new(MEM_SIZE_OPTIMAL(1 << 16), __func__);
BLI_memarena_use_calloc(bp.mem_arena);
- set_profile_spacing(&bp);
+
+ /* Get the 2D profile point locations from either the superellipse or the custom profile */
+ set_profile_spacing(&bp, &bp.pro_spacing, bp.use_custom_profile);
+ if (bp.seg > 1) {
+ bp.pro_spacing.fullness = find_profile_fullness(&bp);
+ }
+
+ /* Get separate non-custom profile samples for the miter profiles if they are needed */
+ if (bp.use_custom_profile &&
+ (bp.miter_inner != BEVEL_MITER_SHARP || bp.miter_outer != BEVEL_MITER_SHARP)) {
+ set_profile_spacing(&bp, &bp.pro_spacing_miter, false);
+ }
bp.face_hash = BLI_ghash_ptr_new(__func__);
BLI_ghash_flag_set(bp.face_hash, GHASH_FLAG_ALLOW_DUPES);
@@ -6643,6 +7378,20 @@ void BM_mesh_bevel(BMesh *bm,
adjust_offsets(&bp, bm);
}
+ /* Maintain consistent orientations for the unsymmetrical custom profiles. */
+ if (bp.use_custom_profile) {
+ BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) {
+ if (BM_elem_flag_test(e, BM_ELEM_TAG)) {
+ regularize_profile_orientation(&bp, e);
+ }
+ }
+ }
+#ifdef DEBUG_PROFILE_ORIENTATION_DRAW
+ if (bp.use_custom_profile) {
+ debug_draw_profile_orientation(&bp, bm);
+ }
+#endif
+
/* Build the meshes around vertices, now that positions are final */
BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) {
if (BM_elem_flag_test(v, BM_ELEM_TAG)) {
@@ -6688,14 +7437,14 @@ void BM_mesh_bevel(BMesh *bm,
}
if (bp.harden_normals) {
- bevel_harden_normals(bm, &bp);
+ bevel_harden_normals(&bp, bm);
}
if (bp.face_strength_mode != BEVEL_FACE_STRENGTH_NONE) {
bevel_set_weighted_normal_face_strength(bm, &bp);
}
/* When called from operator (as opposed to modifier), bm->use_toolflags
- * will be set, and we to transfer the oflags to BM_ELEM_TAGs */
+ * will be set, and we need to transfer the oflags to BM_ELEM_TAGs */
if (bm->use_toolflags) {
BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) {
if (BMO_vert_flag_test(bm, v, VERT_OUT)) {
@@ -6717,6 +7466,22 @@ void BM_mesh_bevel(BMesh *bm,
BM_ITER_ELEM (l, &liter, f, BM_LOOPS_OF_FACE) {
BM_elem_flag_disable(l, BM_ELEM_LONG_TAG);
}
+
+#ifdef DEBUG_CUSTOM_PROFILE_SAMPLE
+ printf("Profile spacing:\n");
+ printf("Seg values:\n");
+ if (bp.pro_spacing.xvals != NULL) {
+ for (int i = 0; i < bp.seg; i++) {
+ printf("(%.3f, %.3f)\n", bp.pro_spacing.xvals[i], bp.pro_spacing.yvals[i]);
+ }
+ }
+ if (bp.pro_spacing.seg_2 != bp.seg && bp.pro_spacing.seg_2 != 0) {
+ printf("Seg_2 values:\n");
+ for (int i = 0; i < bp.pro_spacing.seg_2; i++) {
+ printf("(%0.2f, %0.2f)\n", bp.pro_spacing.xvals_2[i], bp.pro_spacing.yvals_2[i]);
+ }
+ }
+#endif
}
/* primary free */
diff --git a/source/blender/bmesh/tools/bmesh_bevel.h b/source/blender/bmesh/tools/bmesh_bevel.h
index 496be9219b7..45bebbb33ce 100644
--- a/source/blender/bmesh/tools/bmesh_bevel.h
+++ b/source/blender/bmesh/tools/bmesh_bevel.h
@@ -21,6 +21,7 @@
* \ingroup bmesh
*/
+struct CurveProfile;
struct MDeformVert;
void BM_mesh_bevel(BMesh *bm,
@@ -42,6 +43,8 @@ void BM_mesh_bevel(BMesh *bm,
const int miter_outer,
const int miter_inner,
const float spread,
- const float smoothresh);
-
+ const float smoothresh,
+ const bool use_custom_profile,
+ const struct CurveProfile *custom_profile,
+ const int vmesh_method);
#endif /* __BMESH_BEVEL_H__ */