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:
authorRohan Rathi <rohanrathi08@gmail.com>2018-08-09 17:22:07 +0300
committerRohan Rathi <rohanrathi08@gmail.com>2018-08-09 17:22:07 +0300
commit2b41b208c7e5e74a0a5fe043f4bfab2a122a9ed1 (patch)
treecc5c8948a12a73d3997f8323044d2c117a7af8df /source/blender/bmesh
parent85cac2221c543e67a9070c94d89d61bf27c7190f (diff)
parentfea5f26ea56acd73d043e97a96d06067b24ef811 (diff)
Merge branch 'soc-2018-bevel' into blender2.8
Diffstat (limited to 'source/blender/bmesh')
-rw-r--r--source/blender/bmesh/bmesh_class.h29
-rw-r--r--source/blender/bmesh/intern/bmesh_mesh.c445
-rw-r--r--source/blender/bmesh/intern/bmesh_mesh.h21
-rw-r--r--source/blender/bmesh/intern/bmesh_opdefines.c5
-rw-r--r--source/blender/bmesh/intern/bmesh_operator_api.h1
-rw-r--r--source/blender/bmesh/intern/bmesh_operators.h7
-rw-r--r--source/blender/bmesh/operators/bmo_bevel.c6
-rw-r--r--source/blender/bmesh/tools/bmesh_bevel.c375
-rw-r--r--source/blender/bmesh/tools/bmesh_bevel.h3
9 files changed, 849 insertions, 43 deletions
diff --git a/source/blender/bmesh/bmesh_class.h b/source/blender/bmesh/bmesh_class.h
index 10e2892c5a5..70884454ce5 100644
--- a/source/blender/bmesh/bmesh_class.h
+++ b/source/blender/bmesh/bmesh_class.h
@@ -38,6 +38,8 @@ struct BMEdge;
struct BMLoop;
struct BMFace;
+struct MLoopNorSpaceArray;
+
struct BLI_mempool;
/* note: it is very important for BMHeader to start with two
@@ -236,6 +238,9 @@ typedef struct BMesh {
struct BLI_mempool *looplistpool;
#endif
+ struct MLoopNorSpaceArray *lnor_spacearr;
+ char spacearr_dirty;
+
/* should be copy of scene select mode */
/* stored in BMEditMesh too, this is a bit confusing,
* make sure they're in sync!
@@ -263,9 +268,33 @@ enum {
BM_FACE = 8
};
+typedef struct BMLoopNorEditData {
+ int loop_index;
+ BMLoop *loop;
+ float niloc[3];
+ float nloc[3];
+ float *loc;
+ short *clnors_data;
+} BMLoopNorEditData;
+
+typedef struct BMLoopNorEditDataArray {
+ BMLoopNorEditData *lnor_editdata;
+ /* This one has full amount of loops, used to map loop index to actual BMLoopNorEditData struct. */
+ BMLoopNorEditData **lidx_to_lnor_editdata;
+
+ int cd_custom_normal_offset;
+ int totloop;
+} BMLoopNorEditDataArray;
+
#define BM_ALL (BM_VERT | BM_EDGE | BM_LOOP | BM_FACE)
#define BM_ALL_NOLOOP (BM_VERT | BM_EDGE | BM_FACE)
+enum {
+ BM_SPACEARR_DIRTY = 1 << 0,
+ BM_SPACEARR_DIRTY_ALL = 1 << 1,
+ BM_SPACEARR_BMO_SET = 1 << 2,
+};
+
/* args for _Generic */
#define _BM_GENERIC_TYPE_ELEM_NONCONST \
void *, BMVert *, BMEdge *, BMLoop *, BMFace *, \
diff --git a/source/blender/bmesh/intern/bmesh_mesh.c b/source/blender/bmesh/intern/bmesh_mesh.c
index 442cd9275ec..292453fac4b 100644
--- a/source/blender/bmesh/intern/bmesh_mesh.c
+++ b/source/blender/bmesh/intern/bmesh_mesh.c
@@ -30,6 +30,7 @@
#include "DNA_listBase.h"
#include "DNA_object_types.h"
+#include "DNA_scene_types.h"
#include "BLI_linklist_stack.h"
#include "BLI_listbase.h"
@@ -260,6 +261,11 @@ void BM_mesh_data_free(BMesh *bm)
BLI_freelistN(&bm->selected);
+ if (bm->lnor_spacearr) {
+ BKE_lnor_spacearr_free(bm->lnor_spacearr);
+ MEM_freeN(bm->lnor_spacearr);
+ }
+
BMO_error_clear(bm);
}
@@ -313,6 +319,9 @@ void BM_mesh_free(BMesh *bm)
* Helpers for #BM_mesh_normals_update and #BM_verts_calc_normal_vcos
*/
+/* We use that existing internal API flag, assuming no other tool using it would run concurrently to clnors editing. */
+#define BM_LNORSPACE_UPDATE _FLAG_MF
+
typedef struct BMEdgesCalcVectorsData {
/* Read-only data. */
const float (*vcos)[3];
@@ -638,7 +647,8 @@ bool BM_loop_check_cyclic_smooth_fan(BMLoop *l_curr)
* Will use first clnors_data array, and fallback to cd_loop_clnors_offset (use NULL and -1 to not use clnors). */
static void bm_mesh_loops_calc_normals(
BMesh *bm, const float (*vcos)[3], const float (*fnos)[3], float (*r_lnos)[3],
- MLoopNorSpaceArray *r_lnors_spacearr, short (*clnors_data)[2], const int cd_loop_clnors_offset)
+ MLoopNorSpaceArray *r_lnors_spacearr, short (*clnors_data)[2],
+ const int cd_loop_clnors_offset, const bool do_rebuild)
{
BMIter fiter;
BMFace *f_curr;
@@ -694,6 +704,11 @@ static void bm_mesh_loops_calc_normals(
l_curr = l_first = BM_FACE_FIRST_LOOP(f_curr);
do {
+ if (do_rebuild && !BM_ELEM_API_FLAG_TEST(l_curr, BM_LNORSPACE_UPDATE) &&
+ !(bm->spacearr_dirty & BM_SPACEARR_DIRTY_ALL))
+ {
+ continue;
+ }
/* A smooth edge, we have to check for cyclic smooth fan case.
* If we find a new, never-processed cyclic smooth fan, we can do it now using that loop/edge as
* 'entry point', otherwise we can skip it. */
@@ -894,7 +909,10 @@ static void bm_mesh_loops_calc_normals(
clnors_avg[0] /= clnors_nbr;
clnors_avg[1] /= clnors_nbr;
/* Fix/update all clnors of this fan with computed average value. */
- printf("Invalid clnors in this fan!\n");
+
+ /* Prints continuously when merge custom normals, so commenting. */
+ /* printf("Invalid clnors in this fan!\n"); */
+
while ((clnor = BLI_SMALLSTACK_POP(clnors))) {
//print_v2("org clnor", clnor);
clnor[0] = (short)clnors_avg[0];
@@ -1009,7 +1027,8 @@ void BM_mesh_loop_normals_update(
void BM_loops_calc_normal_vcos(
BMesh *bm, const float (*vcos)[3], const float (*vnos)[3], const float (*fnos)[3],
const bool use_split_normals, const float split_angle, float (*r_lnos)[3],
- MLoopNorSpaceArray *r_lnors_spacearr, short (*clnors_data)[2], const int cd_loop_clnors_offset)
+ MLoopNorSpaceArray *r_lnors_spacearr, short (*clnors_data)[2],
+ const int cd_loop_clnors_offset, const bool do_rebuild)
{
const bool has_clnors = clnors_data || (cd_loop_clnors_offset != -1);
@@ -1019,7 +1038,8 @@ void BM_loops_calc_normal_vcos(
bm_mesh_edges_sharp_tag(bm, vnos, fnos, r_lnos, has_clnors ? (float)M_PI : split_angle, false);
/* Finish computing lnos by accumulating face normals in each fan of faces defined by sharp edges. */
- bm_mesh_loops_calc_normals(bm, vcos, fnos, r_lnos, r_lnors_spacearr, clnors_data, cd_loop_clnors_offset);
+ bm_mesh_loops_calc_normals(
+ bm, vcos, fnos, r_lnos, r_lnors_spacearr, clnors_data, cd_loop_clnors_offset, do_rebuild);
}
else {
BLI_assert(!r_lnors_spacearr);
@@ -1041,6 +1061,419 @@ void BM_edges_sharp_from_angle_set(BMesh *bm, const float split_angle)
bm_mesh_edges_sharp_tag(bm, NULL, NULL, NULL, split_angle, true);
}
+void BM_lnorspacearr_store(BMesh *bm, float(*r_lnors)[3])
+{
+ BLI_assert(bm->lnor_spacearr != NULL);
+
+ if (!CustomData_has_layer(&bm->ldata, CD_CUSTOMLOOPNORMAL)) {
+ BM_data_layer_add(bm, &bm->ldata, CD_CUSTOMLOOPNORMAL);
+ }
+
+ int cd_loop_clnors_offset = CustomData_get_offset(&bm->ldata, CD_CUSTOMLOOPNORMAL);
+
+ BM_loops_calc_normal_vcos(
+ bm, NULL, NULL, NULL, true, M_PI, r_lnors, bm->lnor_spacearr, NULL, cd_loop_clnors_offset, false);
+ bm->spacearr_dirty &= ~(BM_SPACEARR_DIRTY | BM_SPACEARR_DIRTY_ALL);
+}
+
+#define CLEAR_SPACEARRAY_THRESHOLD(x) ((x) / 2)
+
+void BM_lnorspace_invalidate(BMesh *bm, const bool do_invalidate_all)
+{
+ if (bm->spacearr_dirty & BM_SPACEARR_DIRTY_ALL) {
+ return;
+ }
+ if (do_invalidate_all || bm->totvertsel > CLEAR_SPACEARRAY_THRESHOLD(bm->totvert)) {
+ bm->spacearr_dirty |= BM_SPACEARR_DIRTY_ALL;
+ return;
+ }
+ if (bm->lnor_spacearr == NULL) {
+ bm->spacearr_dirty |= BM_SPACEARR_DIRTY_ALL;
+ return;
+ }
+
+ BMVert *v;
+ BMLoop *l;
+ BMIter viter, liter;
+ /* Note: we could use temp tag of BMItem for that, but probably better not use it in such a low-level func?
+ * --mont29 */
+ BLI_bitmap *done_verts = BLI_BITMAP_NEW(bm->totvert, __func__);
+
+ BM_mesh_elem_index_ensure(bm, BM_VERT);
+
+ /* When we affect a given vertex, we may affect following smooth fans:
+ * - all smooth fans of said vertex;
+ * - all smooth fans of all immediate loop-neighbors vertices;
+ * This can be simplified as 'all loops of selected vertices and their immediate neighbors'
+ * need to be tagged for update.
+ */
+ BM_ITER_MESH(v, &viter, bm, BM_VERTS_OF_MESH) {
+ if (BM_elem_flag_test(v, BM_ELEM_SELECT)) {
+ BM_ITER_ELEM(l, &liter, v, BM_LOOPS_OF_VERT) {
+ BM_ELEM_API_FLAG_ENABLE(l, BM_LNORSPACE_UPDATE);
+
+ /* Note that we only handle unselected neighbor vertices here, main loop will take care of
+ * selected ones. */
+ if ((!BM_elem_flag_test(l->prev->v, BM_ELEM_SELECT)) &&
+ !BLI_BITMAP_TEST(done_verts, BM_elem_index_get(l->prev->v)))
+ {
+
+ BMLoop *l_prev;
+ BMIter liter_prev;
+ BM_ITER_ELEM(l_prev, &liter_prev, l->prev->v, BM_LOOPS_OF_VERT) {
+ BM_ELEM_API_FLAG_ENABLE(l_prev, BM_LNORSPACE_UPDATE);
+ }
+ BLI_BITMAP_ENABLE(done_verts, BM_elem_index_get(l_prev->v));
+ }
+
+ if ((!BM_elem_flag_test(l->next->v, BM_ELEM_SELECT)) &&
+ !BLI_BITMAP_TEST(done_verts, BM_elem_index_get(l->next->v)))
+ {
+
+ BMLoop *l_next;
+ BMIter liter_next;
+ BM_ITER_ELEM(l_next, &liter_next, l->next->v, BM_LOOPS_OF_VERT) {
+ BM_ELEM_API_FLAG_ENABLE(l_next, BM_LNORSPACE_UPDATE);
+ }
+ BLI_BITMAP_ENABLE(done_verts, BM_elem_index_get(l_next->v));
+ }
+ }
+
+ BLI_BITMAP_ENABLE(done_verts, BM_elem_index_get(v));
+ }
+ }
+
+ MEM_freeN(done_verts);
+ bm->spacearr_dirty |= BM_SPACEARR_DIRTY;
+}
+
+void BM_lnorspace_rebuild(BMesh *bm, bool preserve_clnor)
+{
+ BLI_assert(bm->lnor_spacearr != NULL);
+
+ if (!(bm->spacearr_dirty & (BM_SPACEARR_DIRTY | BM_SPACEARR_DIRTY_ALL))) {
+ return;
+ }
+ BMFace *f;
+ BMLoop *l;
+ BMIter fiter, liter;
+
+ float(*r_lnors)[3] = MEM_callocN(sizeof(*r_lnors) * bm->totloop, __func__);
+ float(*oldnors)[3] = preserve_clnor ? MEM_mallocN(sizeof(*oldnors) * bm->totloop, __func__) : NULL;
+
+ int cd_loop_clnors_offset = CustomData_get_offset(&bm->ldata, CD_CUSTOMLOOPNORMAL);
+
+ BM_mesh_elem_index_ensure(bm, BM_LOOP);
+
+ if (preserve_clnor) {
+ BLI_assert(bm->lnor_spacearr->lspacearr != NULL);
+
+ BM_ITER_MESH(f, &fiter, bm, BM_FACES_OF_MESH) {
+ BM_ITER_ELEM(l, &liter, f, BM_LOOPS_OF_FACE) {
+ if (BM_ELEM_API_FLAG_TEST(l, BM_LNORSPACE_UPDATE) || bm->spacearr_dirty & BM_SPACEARR_DIRTY_ALL) {
+ short(*clnor)[2] = BM_ELEM_CD_GET_VOID_P(l, cd_loop_clnors_offset);
+ int l_index = BM_elem_index_get(l);
+
+ BKE_lnor_space_custom_data_to_normal(bm->lnor_spacearr->lspacearr[l_index], *clnor,
+ oldnors[l_index]);
+ }
+ }
+ }
+ }
+
+ if (bm->spacearr_dirty & BM_SPACEARR_DIRTY_ALL) {
+ BKE_lnor_spacearr_clear(bm->lnor_spacearr);
+ }
+ BM_loops_calc_normal_vcos(
+ bm, NULL, NULL, NULL, true, M_PI, r_lnors, bm->lnor_spacearr, NULL, cd_loop_clnors_offset, true);
+ MEM_freeN(r_lnors);
+
+ BM_ITER_MESH(f, &fiter, bm, BM_FACES_OF_MESH) {
+ BM_ITER_ELEM(l, &liter, f, BM_LOOPS_OF_FACE) {
+ if (BM_ELEM_API_FLAG_TEST(l, BM_LNORSPACE_UPDATE) || bm->spacearr_dirty & BM_SPACEARR_DIRTY_ALL) {
+ if (preserve_clnor) {
+ short(*clnor)[2] = BM_ELEM_CD_GET_VOID_P(l, cd_loop_clnors_offset);
+ int l_index = BM_elem_index_get(l);
+ BKE_lnor_space_custom_normal_to_data(bm->lnor_spacearr->lspacearr[l_index], oldnors[l_index],
+ *clnor);
+ }
+ BM_ELEM_API_FLAG_DISABLE(l, BM_LNORSPACE_UPDATE);
+ }
+ }
+ }
+
+ MEM_SAFE_FREE(oldnors);
+ bm->spacearr_dirty &= ~(BM_SPACEARR_DIRTY | BM_SPACEARR_DIRTY_ALL);
+
+#ifndef NDEBUG
+ BM_lnorspace_err(bm);
+#endif
+}
+
+void BM_lnorspace_update(BMesh *bm)
+{
+ if (bm->lnor_spacearr == NULL) {
+ bm->lnor_spacearr = MEM_callocN(sizeof(*bm->lnor_spacearr), __func__);
+ }
+ if (bm->lnor_spacearr->lspacearr == NULL) {
+ float(*lnors)[3] = MEM_callocN(sizeof(*lnors) * bm->totloop, __func__);
+
+ BM_lnorspacearr_store(bm, lnors);
+
+ MEM_freeN(lnors);
+ }
+ else if (bm->spacearr_dirty & (BM_SPACEARR_DIRTY | BM_SPACEARR_DIRTY_ALL)) {
+ BM_lnorspace_rebuild(bm, false);
+ }
+}
+
+void BM_normals_loops_edges_tag(BMesh *bm, const bool do_edges)
+{
+ BMFace *f;
+ BMEdge *e;
+ BMIter fiter, eiter;
+ BMLoop *l_curr, *l_first;
+
+ if (do_edges) {
+ int index_edge;
+ BM_ITER_MESH_INDEX(e, &eiter, bm, BM_EDGES_OF_MESH, index_edge) {
+ BMLoop *l_a, *l_b;
+
+ BM_elem_index_set(e, index_edge); /* set_inline */
+ BM_elem_flag_disable(e, BM_ELEM_TAG);
+ if (BM_edge_loop_pair(e, &l_a, &l_b)) {
+ if (BM_elem_flag_test(e, BM_ELEM_SMOOTH) && l_a->v != l_b->v) {
+ BM_elem_flag_enable(e, BM_ELEM_TAG);
+ }
+ }
+ }
+ bm->elem_index_dirty &= ~BM_EDGE;
+ }
+
+ int index_face, index_loop = 0;
+ BM_ITER_MESH_INDEX(f, &fiter, bm, BM_FACES_OF_MESH, index_face) {
+ BM_elem_index_set(f, index_face); /* set_inline */
+ l_curr = l_first = BM_FACE_FIRST_LOOP(f);
+ do {
+ BM_elem_index_set(l_curr, index_loop++); /* set_inline */
+ BM_elem_flag_disable(l_curr, BM_ELEM_TAG);
+ } while ((l_curr = l_curr->next) != l_first);
+ }
+ bm->elem_index_dirty &= ~(BM_FACE | BM_LOOP);
+}
+
+/**
+* Auxillary function only used by rebuild to detect if any spaces were not marked as invalid.
+* Reports error if any of the lnor spaces change after rebuilding, meaning that all the possible
+* lnor spaces to be rebuilt were not correctly marked.
+*/
+#ifndef NDEBUG
+void BM_lnorspace_err(BMesh *bm)
+{
+ bm->spacearr_dirty |= BM_SPACEARR_DIRTY_ALL;
+ bool clear = true;
+
+ MLoopNorSpaceArray *temp = MEM_callocN(sizeof(*temp), __func__);
+ temp->lspacearr = NULL;
+
+ BKE_lnor_spacearr_init(temp, bm->totloop, MLNOR_SPACEARR_BMLOOP_PTR);
+
+ int cd_loop_clnors_offset = CustomData_get_offset(&bm->ldata, CD_CUSTOMLOOPNORMAL);
+ float(*lnors)[3] = MEM_callocN(sizeof(*lnors) * bm->totloop, __func__);
+ BM_loops_calc_normal_vcos(bm, NULL, NULL, NULL, true, M_PI, lnors, temp, NULL, cd_loop_clnors_offset, true);
+
+ for (int i = 0; i < bm->totloop; i++) {
+ int j = 0;
+ j += compare_ff(temp->lspacearr[i]->ref_alpha, bm->lnor_spacearr->lspacearr[i]->ref_alpha, 1e-4f);
+ j += compare_ff(temp->lspacearr[i]->ref_beta, bm->lnor_spacearr->lspacearr[i]->ref_beta, 1e-4f);
+ j += compare_v3v3(temp->lspacearr[i]->vec_lnor, bm->lnor_spacearr->lspacearr[i]->vec_lnor, 1e-4f);
+ j += compare_v3v3(temp->lspacearr[i]->vec_ortho, bm->lnor_spacearr->lspacearr[i]->vec_ortho, 1e-4f);
+ j += compare_v3v3(temp->lspacearr[i]->vec_ref, bm->lnor_spacearr->lspacearr[i]->vec_ref, 1e-4f);
+
+ if (j != 5) {
+ clear = false;
+ break;
+ }
+ }
+ BKE_lnor_spacearr_free(temp);
+ MEM_freeN(temp);
+ MEM_freeN(lnors);
+ BLI_assert(clear);
+
+ bm->spacearr_dirty &= ~BM_SPACEARR_DIRTY_ALL;
+}
+#endif
+
+static void bm_loop_normal_mark_indiv_do_loop(
+ BMLoop *l, BLI_bitmap *loops, MLoopNorSpaceArray *lnor_spacearr, int *totloopsel)
+{
+ if (l != NULL) {
+ const int l_idx = BM_elem_index_get(l);
+
+ if (!BLI_BITMAP_TEST(loops, BM_elem_index_get(l))) {
+ /* If vert and face selected share a loop, mark it for editing. */
+ BLI_BITMAP_ENABLE(loops, l_idx);
+ (*totloopsel)++;
+
+ /* Mark all loops in same loop normal space (aka smooth fan). */
+ if ((lnor_spacearr->lspacearr[l_idx]->flags & MLNOR_SPACE_IS_SINGLE) == 0) {
+ for (LinkNode *node = lnor_spacearr->lspacearr[l_idx]->loops; node; node = node->next) {
+ const int lfan_idx = BM_elem_index_get((BMLoop *)node->link);
+ if (!BLI_BITMAP_TEST(loops, lfan_idx)) {
+ BLI_BITMAP_ENABLE(loops, lfan_idx);
+ (*totloopsel)++;
+ }
+ }
+ }
+ }
+ }
+}
+
+/* Mark the individual clnors to be edited, if multiple selection methods are used. */
+static int bm_loop_normal_mark_indiv(BMesh *bm, BLI_bitmap *loops)
+{
+ BMEditSelection *ese, *ese_prev;
+ int totloopsel = 0;
+
+ BM_mesh_elem_index_ensure(bm, BM_LOOP);
+
+ BLI_assert(bm->lnor_spacearr != NULL);
+ BLI_assert(bm->lnor_spacearr->data_type == MLNOR_SPACEARR_BMLOOP_PTR);
+
+ /* Goes from last selected to the first selected element. */
+ for (ese = bm->selected.last; ese; ese = ese->prev) {
+ if (ese->htype == BM_FACE) {
+ ese_prev = ese;
+ /* If current face is selected, then any verts to be edited must have been selected before it. */
+ while ((ese_prev = ese_prev->prev)) {
+ if (ese_prev->htype == BM_VERT) {
+ bm_loop_normal_mark_indiv_do_loop(
+ BM_face_vert_share_loop((BMFace *)ese->ele, (BMVert *)ese_prev->ele),
+ loops, bm->lnor_spacearr, &totloopsel);
+ }
+ else if (ese_prev->htype == BM_EDGE) {
+ bm_loop_normal_mark_indiv_do_loop(
+ BM_face_vert_share_loop((BMFace *)ese->ele, ((BMEdge *)ese_prev->ele)->v1),
+ loops, bm->lnor_spacearr, &totloopsel);
+
+ bm_loop_normal_mark_indiv_do_loop(
+ BM_face_vert_share_loop((BMFace *)ese->ele, ((BMEdge *)ese_prev->ele)->v2),
+ loops, bm->lnor_spacearr, &totloopsel);
+ }
+ }
+ }
+ }
+
+ return totloopsel;
+}
+
+static void loop_normal_editdata_init(BMesh *bm, BMLoopNorEditData *lnor_ed, BMVert *v, BMLoop *l, const int offset)
+{
+ BLI_assert(bm->lnor_spacearr != NULL);
+ BLI_assert(bm->lnor_spacearr->lspacearr != NULL);
+
+ const int l_index = BM_elem_index_get(l);
+ short *clnors_data = BM_ELEM_CD_GET_VOID_P(l, offset);
+
+ lnor_ed->loop_index = l_index;
+ lnor_ed->loop = l;
+
+ float custom_normal[3];
+ BKE_lnor_space_custom_data_to_normal(bm->lnor_spacearr->lspacearr[l_index], clnors_data, custom_normal);
+
+ lnor_ed->clnors_data = clnors_data;
+ copy_v3_v3(lnor_ed->nloc, custom_normal);
+ copy_v3_v3(lnor_ed->niloc, custom_normal);
+
+ lnor_ed->loc = v->co;
+}
+
+BMLoopNorEditDataArray *BM_loop_normal_editdata_array_init(BMesh *bm)
+{
+ BMLoop *l;
+ BMVert *v;
+ BMIter liter, viter;
+
+ bool verts = (bm->selectmode & SCE_SELECT_VERTEX) != 0;
+ bool edges = (bm->selectmode & SCE_SELECT_EDGE) != 0;
+ bool faces = (bm->selectmode & SCE_SELECT_FACE) != 0;
+ int totloopsel = 0;
+
+ BLI_assert(bm->spacearr_dirty == 0);
+
+ BMLoopNorEditDataArray *lnors_ed_arr = MEM_mallocN(sizeof(*lnors_ed_arr), __func__);
+ lnors_ed_arr->lidx_to_lnor_editdata = MEM_callocN(sizeof(*lnors_ed_arr->lidx_to_lnor_editdata) * bm->totloop,
+ __func__);
+
+ if (!CustomData_has_layer(&bm->ldata, CD_CUSTOMLOOPNORMAL)) {
+ BM_data_layer_add(bm, &bm->ldata, CD_CUSTOMLOOPNORMAL);
+ }
+ const int cd_custom_normal_offset = CustomData_get_offset(&bm->ldata, CD_CUSTOMLOOPNORMAL);
+
+ BM_mesh_elem_index_ensure(bm, BM_LOOP);
+
+ BLI_bitmap *loops = BLI_BITMAP_NEW(bm->totloop, __func__);
+ if (faces && (verts || edges)) {
+ /* More than one selection mode, check for individual normals to edit. */
+ totloopsel = bm_loop_normal_mark_indiv(bm, loops);
+ }
+
+ if (totloopsel) {
+ BMLoopNorEditData *lnor_ed = lnors_ed_arr->lnor_editdata = MEM_mallocN(sizeof(*lnor_ed) * totloopsel, __func__);
+
+ BM_ITER_MESH(v, &viter, bm, BM_VERTS_OF_MESH) {
+ BM_ITER_ELEM(l, &liter, v, BM_LOOPS_OF_VERT) {
+ if (BLI_BITMAP_TEST(loops, BM_elem_index_get(l))) {
+ loop_normal_editdata_init(bm, lnor_ed, v, l, cd_custom_normal_offset);
+ lnors_ed_arr->lidx_to_lnor_editdata[BM_elem_index_get(l)] = lnor_ed;
+ lnor_ed++;
+ }
+ }
+ }
+ lnors_ed_arr->totloop = totloopsel;
+ }
+ else { /* If multiple selection modes are inactive OR no such loop is found, fall back to editing all loops. */
+ totloopsel = BM_total_loop_select(bm);
+ BMLoopNorEditData *lnor_ed = lnors_ed_arr->lnor_editdata = MEM_mallocN(sizeof(*lnor_ed) * totloopsel, __func__);
+
+ BM_ITER_MESH(v, &viter, bm, BM_VERTS_OF_MESH) {
+ if (BM_elem_flag_test(v, BM_ELEM_SELECT)) {
+ BM_ITER_ELEM(l, &liter, v, BM_LOOPS_OF_VERT) {
+ loop_normal_editdata_init(bm, lnor_ed, v, l, cd_custom_normal_offset);
+ lnors_ed_arr->lidx_to_lnor_editdata[BM_elem_index_get(l)] = lnor_ed;
+ lnor_ed++;
+ }
+ }
+ }
+ lnors_ed_arr->totloop = totloopsel;
+ }
+
+ MEM_freeN(loops);
+ lnors_ed_arr->cd_custom_normal_offset = cd_custom_normal_offset;
+ return lnors_ed_arr;
+}
+
+void BM_loop_normal_editdata_array_free(BMLoopNorEditDataArray *lnors_ed_arr)
+{
+ MEM_SAFE_FREE(lnors_ed_arr->lnor_editdata);
+ MEM_SAFE_FREE(lnors_ed_arr->lidx_to_lnor_editdata);
+ MEM_freeN(lnors_ed_arr);
+}
+
+int BM_total_loop_select(BMesh *bm)
+{
+ int r_sel = 0;
+ BMVert *v;
+ BMIter viter;
+
+ BM_ITER_MESH(v, &viter, bm, BM_VERTS_OF_MESH) {
+ if (BM_elem_flag_test(v, BM_ELEM_SELECT)) {
+ r_sel += BM_vert_face_count(v);
+ }
+ }
+ return r_sel;
+}
+
/**
* \brief BMesh Begin Edit
*
@@ -1089,6 +1522,7 @@ void bmesh_edit_end(BMesh *bm, BMOpTypeFlag type_flag)
/* compute normals, clear temp flags and flush selections */
if (type_flag & BMO_OPTYPE_FLAG_NORMALS_CALC) {
+ bm->spacearr_dirty |= BM_SPACEARR_DIRTY_ALL;
BM_mesh_normals_update(bm);
}
@@ -1105,6 +1539,9 @@ void bmesh_edit_end(BMesh *bm, BMOpTypeFlag type_flag)
if ((type_flag & BMO_OPTYPE_FLAG_SELECT_VALIDATE) == 0) {
bm->selected = select_history;
}
+ if (type_flag & BMO_OPTYPE_FLAG_INVALIDATE_CLNOR_ALL) {
+ bm->spacearr_dirty |= BM_SPACEARR_DIRTY_ALL;
+ }
}
void BM_mesh_elem_index_ensure_ex(BMesh *bm, const char htype, int elem_offset[4])
diff --git a/source/blender/bmesh/intern/bmesh_mesh.h b/source/blender/bmesh/intern/bmesh_mesh.h
index af7745e4c9f..89077d84447 100644
--- a/source/blender/bmesh/intern/bmesh_mesh.h
+++ b/source/blender/bmesh/intern/bmesh_mesh.h
@@ -29,6 +29,7 @@
struct BMAllocTemplate;
struct MLoopNorSpaceArray;
+struct BMLoopNorEditDataArray;
void BM_mesh_elem_toolflags_ensure(BMesh *bm);
void BM_mesh_elem_toolflags_clear(BMesh *bm);
@@ -48,11 +49,25 @@ void BM_mesh_clear(BMesh *bm);
void BM_mesh_normals_update(BMesh *bm);
void BM_verts_calc_normal_vcos(BMesh *bm, const float (*fnos)[3], const float (*vcos)[3], float (*vnos)[3]);
void BM_loops_calc_normal_vcos(
- BMesh *bm, const float (*vcos)[3], const float (*vnos)[3], const float (*pnos)[3],
- const bool use_split_normals, const float split_angle, float (*r_lnos)[3],
- struct MLoopNorSpaceArray *r_lnors_spacearr, short (*clnors_data)[2], const int cd_loop_clnors_offset);
+ BMesh *bm, const float(*vcos)[3], const float(*vnos)[3], const float(*pnos)[3],
+ const bool use_split_normals, const float split_angle, float(*r_lnos)[3],
+ struct MLoopNorSpaceArray *r_lnors_spacearr, short(*clnors_data)[2],
+ const int cd_loop_clnors_offset, const bool do_rebuild);
bool BM_loop_check_cyclic_smooth_fan(BMLoop *l_curr);
+void BM_lnorspacearr_store(BMesh *bm, float(*r_lnors)[3]);
+void BM_lnorspace_invalidate(BMesh *bm, const bool do_invalidate_all);
+void BM_lnorspace_rebuild(BMesh *bm, bool preserve_clnor);
+void BM_lnorspace_update(BMesh *bm);
+void BM_normals_loops_edges_tag(BMesh *bm, const bool do_edges);
+#ifndef NDEBUG
+void BM_lnorspace_err(BMesh *bm);
+#endif
+
+/* Loop Generics */
+struct BMLoopNorEditDataArray *BM_loop_normal_editdata_array_init(BMesh *bm);
+void BM_loop_normal_editdata_array_free(struct BMLoopNorEditDataArray *lnors_ed_arr);
+int BM_total_loop_select(BMesh *bm);
void BM_edges_sharp_from_angle_set(BMesh *bm, const float split_angle);
diff --git a/source/blender/bmesh/intern/bmesh_opdefines.c b/source/blender/bmesh/intern/bmesh_opdefines.c
index b5e6fe168e5..d9093e774e6 100644
--- a/source/blender/bmesh/intern/bmesh_opdefines.c
+++ b/source/blender/bmesh/intern/bmesh_opdefines.c
@@ -1736,12 +1736,17 @@ static BMOpDefine bmo_bevel_def = {
{"clamp_overlap", BMO_OP_SLOT_BOOL}, /* do not allow beveled edges/vertices to overlap each other */
{"material", BMO_OP_SLOT_INT}, /* material for bevel faces, -1 means get from adjacent faces */
{"loop_slide", BMO_OP_SLOT_BOOL}, /* prefer to slide along edges to having even widths */
+ {"mark_seam", BMO_OP_SLOT_BOOL}, /* extend edge data to allow seams to run across bevels */
+ {"mark_sharp", BMO_OP_SLOT_BOOL}, /* extend edge data to allow sharp edges to run across bevels */
+ {"strength", BMO_OP_SLOT_FLT}, /* strength of calculated normal in range (0, 1) for custom clnors */
+ {"hnmode", BMO_OP_SLOT_INT}, /* harden normals mode used in bevel if enabled */
{{'\0'}},
},
/* slots_out */
{{"faces.out", BMO_OP_SLOT_ELEMENT_BUF, {BM_FACE}}, /* output faces */
{"edges.out", BMO_OP_SLOT_ELEMENT_BUF, {BM_EDGE}}, /* output edges */
{"verts.out", BMO_OP_SLOT_ELEMENT_BUF, {BM_VERT}}, /* output verts */
+ {"normals.out", BMO_OP_SLOT_MAPPING}, /* output normals per vertex for beveled edges */
{{'\0'}},
},
diff --git a/source/blender/bmesh/intern/bmesh_operator_api.h b/source/blender/bmesh/intern/bmesh_operator_api.h
index 30cd1df9c4e..de87da71e8d 100644
--- a/source/blender/bmesh/intern/bmesh_operator_api.h
+++ b/source/blender/bmesh/intern/bmesh_operator_api.h
@@ -244,6 +244,7 @@ typedef enum {
BMO_OPTYPE_FLAG_NORMALS_CALC = (1 << 1),
BMO_OPTYPE_FLAG_SELECT_FLUSH = (1 << 2),
BMO_OPTYPE_FLAG_SELECT_VALIDATE = (1 << 3),
+ BMO_OPTYPE_FLAG_INVALIDATE_CLNOR_ALL = (1 << 4),
} BMOpTypeFlag;
typedef struct BMOperator {
diff --git a/source/blender/bmesh/intern/bmesh_operators.h b/source/blender/bmesh/intern/bmesh_operators.h
index 80b57eb3565..6d518545967 100644
--- a/source/blender/bmesh/intern/bmesh_operators.h
+++ b/source/blender/bmesh/intern/bmesh_operators.h
@@ -127,6 +127,13 @@ enum {
BEVEL_AMT_PERCENT
};
+enum {
+ BEVEL_HN_NONE, /* Disable harden normals */
+ BEVEL_HN_FACE, /* harden normals according to face area */
+ BEVEL_HN_ADJ, /* harden normals according to adjacent 'beveled' faces */
+ BEVEL_HN_FIX_SHA, /* Special mode to fix normal shading continuity */
+};
+
extern const BMOpDefine *bmo_opdefines[];
extern const int bmo_opdefines_total;
diff --git a/source/blender/bmesh/operators/bmo_bevel.c b/source/blender/bmesh/operators/bmo_bevel.c
index 2ae87b64286..eb299dbba60 100644
--- a/source/blender/bmesh/operators/bmo_bevel.c
+++ b/source/blender/bmesh/operators/bmo_bevel.c
@@ -43,6 +43,9 @@ void bmo_bevel_exec(BMesh *bm, BMOperator *op)
const bool clamp_overlap = BMO_slot_bool_get(op->slots_in, "clamp_overlap");
const int material = BMO_slot_int_get(op->slots_in, "material");
const bool loop_slide = BMO_slot_bool_get(op->slots_in, "loop_slide");
+ const bool mark_seam = BMO_slot_bool_get(op->slots_in, "mark_seam");
+ const bool mark_sharp = BMO_slot_bool_get(op->slots_in, "mark_sharp");
+ const int hnmode = BMO_slot_int_get(op->slots_in, "hnmode");
if (offset > 0) {
BMOIter siter;
@@ -63,7 +66,8 @@ void bmo_bevel_exec(BMesh *bm, BMOperator *op)
}
}
- BM_mesh_bevel(bm, offset, offset_type, seg, profile, vonly, false, clamp_overlap, NULL, -1, material, loop_slide);
+ BM_mesh_bevel(bm, offset, offset_type, seg, profile, vonly, false, clamp_overlap, NULL, -1, material,
+ loop_slide, mark_seam, mark_sharp, hnmode, op);
BMO_slot_buffer_from_enabled_hflag(bm, op, op->slots_out, "faces.out", BM_FACE, BM_ELEM_TAG);
BMO_slot_buffer_from_enabled_hflag(bm, op, op->slots_out, "edges.out", BM_EDGE, BM_ELEM_TAG);
diff --git a/source/blender/bmesh/tools/bmesh_bevel.c b/source/blender/bmesh/tools/bmesh_bevel.c
index adfcb8dc68f..cc9651066a0 100644
--- a/source/blender/bmesh/tools/bmesh_bevel.c
+++ b/source/blender/bmesh/tools/bmesh_bevel.c
@@ -34,6 +34,7 @@
#include "DNA_object_types.h"
#include "DNA_meshdata_types.h"
+#include "DNA_modifier_types.h"
#include "BLI_array.h"
#include "BLI_alloca.h"
@@ -43,6 +44,7 @@
#include "BKE_customdata.h"
#include "BKE_deform.h"
+#include "BKE_mesh.h"
#include "eigen_capi.h"
@@ -150,6 +152,8 @@ typedef struct BoundVert {
Profile profile; /* edge profile between this and next BoundVert */
bool any_seam; /* are any of the edges attached here seams? */
bool visited; /* used during delta adjust pass */
+ int seam_len; /* length of seam starting from current boundvert to next boundvert with ccw ordering */
+ int sharp_len; /* Same as seam_len but defines length of sharp edges */
// int _pad;
} BoundVert;
@@ -188,6 +192,8 @@ typedef struct BevelParams {
/* hash of BevVert for each vertex involved in bevel
* GHash: (key=(BMVert *), value=(BevVert *)) */
GHash *vert_hash;
+ /* Hash set used to store resultant beveled faces for VMesh when poly is ring */
+ GHash *faceHash;
MemArena *mem_arena; /* use for all allocs while bevel runs, if we need to free we can switch to mempool */
ProfileSpacing pro_spacing; /* parameter values for evenly spaced profiles */
@@ -200,9 +206,12 @@ typedef struct BevelParams {
bool loop_slide; /* should bevel prefer to slide along edges rather than keep widths spec? */
bool limit_offset; /* should offsets be limited by collisions? */
bool offset_adjust; /* should offsets be adjusted to try to get even widths? */
+ bool mark_seam;
+ bool mark_sharp;
const struct MDeformVert *dvert; /* vertex group array, maybe set if vertex_only */
int vertex_group; /* vertex group index, maybe set if vertex_only */
int mat_nr; /* if >= 0, material number for bevel; else material comes from adjacent faces */
+ int hnmode;
} BevelParams;
// #pragma GCC diagnostic ignored "-Wpadded"
@@ -1522,8 +1531,216 @@ static void snap_to_superellipsoid(float co[3], const float super_r, bool midlin
co[2] = z;
}
+#define BEV_EXTEND_EDGE_DATA_CHECK(eh, flag) (BM_elem_flag_test(eh->e, flag))
+
+static void check_edge_data_seam_sharp_edges(BevVert *bv, int flag, bool neg)
+{
+ EdgeHalf *e = &bv->edges[0], *efirst = &bv->edges[0];
+
+ /* First first edge with seam or sharp edge data */
+ while ((!neg && !BEV_EXTEND_EDGE_DATA_CHECK(e, flag) || (neg && BEV_EXTEND_EDGE_DATA_CHECK(e, flag)))) {
+ e = e->next;
+ if (e == efirst)
+ break;
+ }
+
+ /* If no such edge found, return */
+ if ((!neg && !BEV_EXTEND_EDGE_DATA_CHECK(e, flag) || (neg && BEV_EXTEND_EDGE_DATA_CHECK(e, flag))))
+ return;
+
+ efirst = e; /* Set efirst to this first encountered edge*/
+
+ do {
+ int flag_count = 0;
+ EdgeHalf *ne = e->next;
+
+ while ((!neg && !BEV_EXTEND_EDGE_DATA_CHECK(ne, flag) || (neg && BEV_EXTEND_EDGE_DATA_CHECK(ne, flag))) &&
+ ne != efirst)
+ {
+ if (ne->is_bev)
+ flag_count++;
+ ne = ne->next;
+ }
+ if (ne == e || (ne == efirst && (!neg && !BEV_EXTEND_EDGE_DATA_CHECK(efirst, flag) ||
+ (neg && BEV_EXTEND_EDGE_DATA_CHECK(efirst, flag)))))
+ {
+ break;
+ }
+ if (flag == BM_ELEM_SEAM) /* Set seam_len / sharp_len of starting edge */
+ e->rightv->seam_len = flag_count;
+ else if (flag == BM_ELEM_SMOOTH)
+ e->rightv->sharp_len = flag_count;
+ e = ne;
+ } while (e != efirst);
+}
+
+static void bevel_extend_edge_data(BevVert *bv)
+{
+ VMesh *vm = bv->vmesh;
+
+ BoundVert *bcur = bv->vmesh->boundstart, *start = bcur;
+
+ do {
+ /* If current boundvert has a seam length > 0 then it has a seam running along its edges */
+ if (bcur->seam_len) {
+ if (!bv->vmesh->boundstart->seam_len && start == bv->vmesh->boundstart)
+ start = bcur; /* set start to first boundvert with seam_len > 0 */
+
+ /* Now for all the mesh_verts starting at current index and ending at idxlen
+ * We go through outermost ring and through all its segments and add seams
+ * for those edges */
+ int idxlen = bcur->index + bcur->seam_len;
+ for (int i = bcur->index; i < idxlen; i++) {
+ BMVert *v1 = mesh_vert(vm, i % vm->count, 0, 0)->v, *v2;
+ BMEdge *e;
+ for (int k = 1; k < vm->seg; k++) {
+ v2 = mesh_vert(vm, i % vm->count, 0, k)->v;
+
+ /* Here v1 & v2 are current and next BMverts, we find common edge and set its edge data */
+ e = v1->e;
+ while (e->v1 != v2 && e->v2 != v2) {
+ if (e->v1 == v1)
+ e = e->v1_disk_link.next;
+ else
+ e = e->v2_disk_link.next;
+ }
+ BM_elem_flag_set(e, BM_ELEM_SEAM, true);
+ v1 = v2;
+ }
+ BMVert *v3 = mesh_vert(vm, (i + 1) % vm->count, 0, 0)->v;
+ e = v1->e; //Do same as above for first and last vert
+ while (e->v1 != v3 && e->v2 != v3) {
+ if (e->v1 == v1)
+ e = e->v1_disk_link.next;
+ else
+ e = e->v2_disk_link.next;
+ }
+ BM_elem_flag_set(e, BM_ELEM_SEAM, true);
+ bcur = bcur->next;
+ }
+ }
+ else
+ bcur = bcur->next;
+ } while (bcur != start);
+
+
+ bcur = bv->vmesh->boundstart;
+ start = bcur;
+ do {
+ if (bcur->sharp_len) {
+ if (!bv->vmesh->boundstart->sharp_len && start == bv->vmesh->boundstart)
+ start = bcur;
+
+ int idxlen = bcur->index + bcur->sharp_len;
+ for (int i = bcur->index; i < idxlen; i++) {
+ BMVert *v1 = mesh_vert(vm, i % vm->count, 0, 0)->v, *v2;
+ BMEdge *e;
+ for (int k = 1; k < vm->seg; k++) {
+ v2 = mesh_vert(vm, i % vm->count, 0, k)->v;
+
+ e = v1->e;
+ while (e->v1 != v2 && e->v2 != v2) {
+ if (e->v1 == v1)
+ e = e->v1_disk_link.next;
+ else
+ e = e->v2_disk_link.next;
+ }
+ BM_elem_flag_set(e, BM_ELEM_SMOOTH, false);
+ v1 = v2;
+ }
+ BMVert *v3 = mesh_vert(vm, (i + 1) % vm->count, 0, 0)->v;
+ e = v1->e;
+ while (e->v1 != v3 && e->v2 != v3) {
+ if (e->v1 == v1)
+ e = e->v1_disk_link.next;
+ else
+ e = e->v2_disk_link.next;
+ }
+ BM_elem_flag_set(e, BM_ELEM_SMOOTH, false);
+ bcur = bcur->next;
+ }
+ }
+ else
+ bcur = bcur->next;
+ } while (bcur != start);
+}
+
+static void bevel_harden_normals_mode(BMesh *bm, BevelParams *bp, BevVert *bv, BMOperator *op)
+{
+ if (bp->hnmode == BEVEL_HN_NONE)
+ return;
+
+ VMesh *vm = bv->vmesh;
+ BoundVert *bcur = vm->boundstart, *bstart = bcur;
+ int ns = vm->seg, ns2 = ns / 2;
+
+ BMEdge *e;
+ BMIter eiter;
+
+ BMOpSlot *nslot = BMO_slot_get(op->slots_out, "normals.out");
+ float n_final[3] = { 0.0f, 0.0f, 0.0f };
+
+ if (bp->hnmode == BEVEL_HN_FACE) {
+ GHash *tempfaceHash = BLI_ghash_int_new(__func__);
+
+ /* Iterate through all faces of current BMVert and add their normal*face_area to n_final */
+ BM_ITER_ELEM(e, &eiter, bv->v, BM_EDGES_OF_VERT) {
+ if (BM_elem_flag_test(e, BM_ELEM_TAG)) {
+
+ BMFace *f_a, *f_b;
+ BM_edge_face_pair(e, &f_a, &f_b);
+
+ if (f_a && !BLI_ghash_haskey(tempfaceHash, SET_UINT_IN_POINTER(BM_elem_index_get(f_a)))) {
+ int f_area = BM_face_calc_area(f_a);
+ float f_no[3];
+ copy_v3_v3(f_no, f_a->no);
+ mul_v3_fl(f_no, f_area);
+ add_v3_v3(n_final, f_no);
+ BLI_ghash_insert(tempfaceHash, SET_UINT_IN_POINTER(BM_elem_index_get(f_a)), NULL);
+ }
+ if (f_b && !BLI_ghash_haskey(tempfaceHash, SET_UINT_IN_POINTER(BM_elem_index_get(f_b)))) {
+ int f_area = BM_face_calc_area(f_b);
+ float f_no[3];
+ copy_v3_v3(f_no, f_b->no);
+ mul_v3_fl(f_no, f_area);
+ add_v3_v3(n_final, f_no);
+ BLI_ghash_insert(tempfaceHash, SET_UINT_IN_POINTER(BM_elem_index_get(f_b)), NULL);
+ }
+ }
+ }
+ BLI_ghash_free(tempfaceHash, NULL, NULL);
+ normalize_v3(n_final);
+ }
+ else if (bp->hnmode == BEVEL_HN_ADJ) {
+ BM_ITER_ELEM(e, &eiter, bv->v, BM_EDGES_OF_VERT) {
+ if (BM_elem_flag_test(e, BM_ELEM_TAG)) {
+ if (e->v1 == bv->v) {
+ add_v3_v3(n_final, e->v2->no);
+ }
+ else {
+ add_v3_v3(n_final, e->v1->no);
+ }
+ }
+ }
+ normalize_v3(n_final);
+ }
+
+ do {
+ /* Set normals.out for vertices as computed earlier */
+ if (BMO_slot_map_contains(nslot, bcur->nv.v) != true) {
+
+ float(*vert_normal) = MEM_callocN(sizeof(*vert_normal) * 3, __func__);
+ add_v3_v3(vert_normal, n_final);
+ normalize_v3(vert_normal);
+
+ BMO_slot_map_insert(op, nslot, bcur->nv.v, vert_normal);
+ }
+ bcur = bcur->next;
+ } while (bcur != bstart);
+}
+
/* Set the any_seam property for a BevVert and all its BoundVerts */
-static void set_bound_vert_seams(BevVert *bv)
+static void set_bound_vert_seams(BevVert *bv, bool mark_seam, bool mark_sharp)
{
BoundVert *v;
EdgeHalf *e;
@@ -1539,6 +1756,13 @@ static void set_bound_vert_seams(BevVert *bv)
}
bv->any_seam |= v->any_seam;
} while ((v = v->next) != bv->vmesh->boundstart);
+
+ if (mark_seam) {
+ check_edge_data_seam_sharp_edges(bv, BM_ELEM_SEAM, false);
+ }
+ if (mark_sharp) {
+ check_edge_data_seam_sharp_edges(bv, BM_ELEM_SMOOTH, true);
+ }
}
static int count_bound_vert_seams(BevVert *bv)
@@ -1609,7 +1833,7 @@ static void build_boundary_vertex_only(BevelParams *bp, BevVert *bv, bool constr
calculate_vm_profiles(bp, bv, vm);
if (construct) {
- set_bound_vert_seams(bv);
+ set_bound_vert_seams(bv, bp->mark_seam, bp->mark_sharp);
if (vm->count == 2)
vm->mesh_kind = M_NONE;
else if (bp->seg == 1)
@@ -1664,7 +1888,7 @@ static void build_boundary_terminal_edge(BevelParams *bp, BevVert *bv, EdgeHalf
e->next->leftv = e->next->rightv = v;
/* could use M_POLY too, but tri-fan looks nicer)*/
vm->mesh_kind = M_TRI_FAN;
- set_bound_vert_seams(bv);
+ set_bound_vert_seams(bv, bp->mark_seam, bp->mark_sharp);
}
else {
adjust_bound_vert(e->next->leftv, co);
@@ -1723,7 +1947,7 @@ static void build_boundary_terminal_edge(BevelParams *bp, BevVert *bv, EdgeHalf
}
if (construct) {
- set_bound_vert_seams(bv);
+ set_bound_vert_seams(bv, bp->mark_seam, bp->mark_sharp);
if (vm->count == 2 && bv->edgecount == 3) {
vm->mesh_kind = M_NONE;
@@ -1865,7 +2089,7 @@ static void build_boundary(BevelParams *bp, BevVert *bv, bool construct)
calculate_vm_profiles(bp, bv, vm);
if (construct) {
- set_bound_vert_seams(bv);
+ set_bound_vert_seams(bv, bp->mark_seam, bp->mark_sharp);
if (vm->count == 2) {
vm->mesh_kind = M_NONE;
@@ -2919,29 +3143,34 @@ static VMesh *make_cube_corner_adj_vmesh(BevelParams *bp)
}
/* Is this a good candidate for using tri_corner_adj_vmesh? */
-static bool tri_corner_test(BevelParams *bp, BevVert *bv)
+static int tri_corner_test(BevelParams *bp, BevVert *bv)
{
float ang, totang, angdiff;
EdgeHalf *e;
int i;
+ int in_plane_e = 0;
- if (bv->edgecount != 3 || bv->selcount != 3)
- return false;
totang = 0.0f;
- for (i = 0; i < 3; i++) {
+ for (i = 0; i < bv->edgecount; i++) {
e = &bv->edges[i];
ang = BM_edge_calc_face_angle_signed_ex(e->e, 0.0f);
- if (ang <= (float) M_PI_4 || ang >= 3.0f * (float) M_PI_4)
- return false;
+ if (ang <= M_PI_4)
+ in_plane_e++;
+ else if (ang >= 3.0f * (float) M_PI_4)
+ return -1;
totang += ang;
}
+ if (in_plane_e != bv->edgecount - 3)
+ return -1;
angdiff = fabsf(totang - 3.0f * (float)M_PI_2);
if ((bp->pro_super_r == PRO_SQUARE_R && angdiff > (float)M_PI / 16.0f) ||
(angdiff > (float)M_PI_4))
{
- return false;
+ return -1;
}
- return true;
+ if (bv->edgecount != 3 || bv->selcount != 3)
+ return 0;
+ return 1;
}
static VMesh *tri_corner_adj_vmesh(BevelParams *bp, BevVert *bv)
@@ -2952,7 +3181,7 @@ static VMesh *tri_corner_adj_vmesh(BevelParams *bp, BevVert *bv)
VMesh *vm;
BoundVert *bndv;
- BLI_assert(bv->edgecount == 3 && bv->selcount == 3);
+ /*BLI_assert(bv->edgecount == 3 && bv->selcount == 3); Add support for in plane edges */
bndv = bv->vmesh->boundstart;
copy_v3_v3(co0, bndv->nv.co);
bndv = bndv->next;
@@ -2985,9 +3214,14 @@ static VMesh *adj_vmesh(BevelParams *bp, BevVert *bv)
BoundVert *bndv;
MemArena *mem_arena = bp->mem_arena;
float r, fac, fullness;
+ n = 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) {
+ return tri_corner_adj_vmesh(bp, bv);
+ }
/* First construct an initial control mesh, with nseg==2 */
- n = bv->vmesh->count;
ns = bv->vmesh->seg;
vm0 = new_adj_vmesh(mem_arena, n, 2, bv->vmesh->boundstart);
@@ -3430,7 +3664,7 @@ static void bevel_build_rings(BevelParams *bp, BMesh *bm, BevVert *bv)
VMesh *vm1, *vm;
BoundVert *v;
BMVert *bmv1, *bmv2, *bmv3, *bmv4;
- BMFace *f, *f2;
+ BMFace *f, *f2, *r_f;
BMEdge *bme, *bme1, *bme2, *bme3;
EdgeHalf *e;
BoundVert *vpipe;
@@ -3442,6 +3676,17 @@ static void bevel_build_rings(BevelParams *bp, BMesh *bm, BevVert *bv)
odd = ns % 2;
BLI_assert(n >= 3 && ns > 1);
+ /* Add support for profiles in vertex only in-plane bevels */
+ if (bp->vertex_only) {
+ v = bv->vmesh->boundstart;
+ do {
+ Profile *pro = &v->profile;
+ 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);
+ }
vpipe = pipe_test(bv);
@@ -3451,7 +3696,7 @@ static void bevel_build_rings(BevelParams *bp, BMesh *bm, BevVert *bv)
else if (vpipe) {
vm1 = pipe_adj_vmesh(bp, bv, vpipe);
}
- else if (tri_corner_test(bp, bv)) {
+ else if (tri_corner_test(bp, bv) == 1) {
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 */
@@ -3464,6 +3709,8 @@ static void bevel_build_rings(BevelParams *bp, BMesh *bm, BevVert *bv)
vm1 = adj_vmesh(bp, bv);
}
+ bool do_fix_shading_bv = bp->faceHash != NULL;
+
/* copy final vmesh into bv->vmesh, make BMVerts and BMFaces */
vm = bv->vmesh;
for (i = 0; i < n; i++) {
@@ -3505,24 +3752,24 @@ static void bevel_build_rings(BevelParams *bp, BMesh *bm, BevVert *bv)
if (bp->vertex_only) {
if (j < k) {
if (k == ns2 && j == ns2 - 1) {
- bev_create_quad_ex(bm, bmv1, bmv2, bmv3, bmv4, f2, f2, f2, f2,
+ r_f = bev_create_quad_ex(bm, bmv1, bmv2, bmv3, bmv4, f2, f2, f2, f2,
NULL, NULL, v->next->efirst->e, bme, mat_nr);
}
else {
- bev_create_quad(bm, bmv1, bmv2, bmv3, bmv4, f2, f2, f2, f2, mat_nr);
+ r_f = bev_create_quad(bm, bmv1, bmv2, bmv3, bmv4, f2, f2, f2, f2, mat_nr);
}
}
else if (j > k) {
- bev_create_quad(bm, bmv1, bmv2, bmv3, bmv4, f2, f2, f2, f2, mat_nr);
+ r_f = bev_create_quad(bm, bmv1, bmv2, bmv3, bmv4, f2, f2, f2, f2, mat_nr);
}
else { /* j == k */
/* only one edge attached to v, since vertex_only */
if (e->is_seam) {
- bev_create_quad_ex(bm, bmv1, bmv2, bmv3, bmv4, f2, f2, f2, f2,
+ r_f = bev_create_quad_ex(bm, bmv1, bmv2, bmv3, bmv4, f2, f2, f2, f2,
bme, NULL, bme, NULL, mat_nr);
}
else {
- bev_create_quad_ex(bm, bmv1, bmv2, bmv3, bmv4, f2, f2, f2, f,
+ r_f = bev_create_quad_ex(bm, bmv1, bmv2, bmv3, bmv4, f2, f2, f2, f,
bme, NULL, bme, NULL, mat_nr);
}
}
@@ -3531,25 +3778,27 @@ static void bevel_build_rings(BevelParams *bp, BMesh *bm, BevVert *bv)
if (odd) {
if (k == ns2) {
if (e->is_seam) {
- bev_create_quad_ex(bm, bmv1, bmv2, bmv3, bmv4, f, f, f, f,
+ r_f = bev_create_quad_ex(bm, bmv1, bmv2, bmv3, bmv4, f, f, f, f,
NULL, bme, bme, NULL, mat_nr);
}
else {
- bev_create_quad(bm, bmv1, bmv2, bmv3, bmv4, f, f2, f2, f, mat_nr);
+ r_f = bev_create_quad(bm, bmv1, bmv2, bmv3, bmv4, f, f2, f2, f, mat_nr);
}
}
else {
- bev_create_quad(bm, bmv1, bmv2, bmv3, bmv4, f, f, f, f, mat_nr);
+ r_f = bev_create_quad(bm, bmv1, bmv2, bmv3, bmv4, f, f, f, f, mat_nr);
}
}
else {
bme1 = k == ns2 - 1 ? bme : NULL;
bme3 = j == ns2 - 1 ? v->prev->ebev->e : NULL;
bme2 = bme1 != NULL ? bme1 : bme3;
- bev_create_quad_ex(bm, bmv1, bmv2, bmv3, bmv4, f, f, f, f,
+ r_f = bev_create_quad_ex(bm, bmv1, bmv2, bmv3, bmv4, f, f, f, f,
NULL, bme1, bme2, bme3, mat_nr);
}
}
+ if (do_fix_shading_bv)
+ BLI_ghash_insert(bp->faceHash, r_f, NULL);
}
}
} while ((v = v->next) != vm->boundstart);
@@ -3622,6 +3871,8 @@ static BMFace *bevel_build_poly(BevelParams *bp, BMesh *bm, BevVert *bv)
BLI_array_staticdeclare(vf, BM_DEFAULT_NGON_STACK_SIZE);
BLI_array_staticdeclare(ve, BM_DEFAULT_NGON_STACK_SIZE);
+ bool do_fix_shading_bv = bp->faceHash != NULL;
+
if (bv->any_seam) {
frep = boundvert_rep_face(vm->boundstart, &frep2);
if (frep2 && frep && is_bad_uv_poly(bv, frep)) {
@@ -3667,6 +3918,8 @@ static BMFace *bevel_build_poly(BevelParams *bp, BMesh *bm, BevVert *bv)
} while ((v = v->next) != vm->boundstart);
if (n > 2) {
f = bev_create_ngon(bm, vv, n, vf, frep, ve, bp->mat_nr, true);
+ if (do_fix_shading_bv)
+ BLI_ghash_insert(bp->faceHash, f, NULL);
}
else {
f = NULL;
@@ -3681,6 +3934,7 @@ static void bevel_build_trifan(BevelParams *bp, BMesh *bm, BevVert *bv)
{
BMFace *f;
BLI_assert(next_bev(bv, NULL)->seg == 1 || bv->selcount == 1);
+ bool do_fix_shading_bv = bp->faceHash != NULL;
f = bevel_build_poly(bp, bm, bv);
@@ -3689,6 +3943,11 @@ static void bevel_build_trifan(BevelParams *bp, BMesh *bm, BevVert *bv)
BMLoop *l_fan = BM_FACE_FIRST_LOOP(f)->prev;
BMVert *v_fan = l_fan->v;
+ if (f->len == 3) {
+ if (do_fix_shading_bv)
+ BLI_ghash_insert(bp->faceHash, f, NULL);
+ }
+
while (f->len > 3) {
BMLoop *l_new;
BMFace *f_new;
@@ -3709,6 +3968,8 @@ static void bevel_build_trifan(BevelParams *bp, BMesh *bm, BevVert *bv)
else if (l_fan->prev->v == v_fan) { l_fan = l_fan->prev; }
else { BLI_assert(0); }
}
+ if (do_fix_shading_bv)
+ BLI_ghash_insert(bp->faceHash, f_new, NULL);
}
}
}
@@ -3717,6 +3978,7 @@ static void bevel_build_quadstrip(BevelParams *bp, BMesh *bm, BevVert *bv)
{
BMFace *f;
BLI_assert(bv->selcount == 2);
+ bool do_fix_shading_bv = bp->faceHash != NULL;
f = bevel_build_poly(bp, bm, bv);
@@ -3728,6 +3990,11 @@ static void bevel_build_quadstrip(BevelParams *bp, BMesh *bm, BevVert *bv)
BMLoop *l_b = BM_face_vert_share_loop(f, eh_b->leftv->nv.v);
int split_count = bv->vmesh->seg + 1; /* ensure we don't walk past the segments */
+ if (f->len == 4) {
+ if (do_fix_shading_bv)
+ BLI_ghash_insert(bp->faceHash, f, NULL);
+ }
+
while (f->len > 4 && split_count > 0) {
BMLoop *l_new;
BLI_assert(l_a->f == f);
@@ -3746,6 +4013,9 @@ static void bevel_build_quadstrip(BevelParams *bp, BMesh *bm, BevVert *bv)
/* walk around the new face to get the next verts to split */
l_a = l_new->prev;
l_b = l_new->next->next;
+
+ if (do_fix_shading_bv)
+ BLI_ghash_insert(bp->faceHash, f, NULL);
}
split_count--;
}
@@ -3808,6 +4078,13 @@ static void bevel_vert_two_edges(BevelParams *bp, BMesh *bm, BevVert *bv)
flag_out_edge(bm, bme);
}
}
+ else if (bp->faceHash) {
+ BMFace *f;
+ BMIter fiter;
+ BM_ITER_ELEM(f, &fiter, bv->v, BM_FACES_OF_VERT) {
+ BLI_ghash_insert(bp->faceHash, f, NULL);
+ }
+ }
}
/* Given that the boundary is built, now make the actual BMVerts
@@ -4722,13 +4999,15 @@ static void bevel_build_edge_polygons(BMesh *bm, BevelParams *bp, BMEdge *bme)
VMesh *vm1, *vm2;
EdgeHalf *e1, *e2;
BMEdge *bme1, *bme2, *center_bme;
- BMFace *f1, *f2, *f;
+ BMFace *f1, *f2, *f, *r_f;
BMVert *verts[4];
BMFace *faces[4];
BMEdge *edges[4];
int k, nseg, i1, i2, odd, mid;
int mat_nr = bp->mat_nr;
+ bool do_fix_shading_bv = bp->faceHash != NULL;
+
if (!BM_edge_is_manifold(bme))
return;
@@ -4784,18 +5063,18 @@ static void bevel_build_edge_polygons(BMesh *bm, BevelParams *bp, BMEdge *bme)
/* straddles a seam: choose to interpolate in f1 and snap right edge to bme */
edges[0] = edges[1] = NULL;
edges[2] = edges[3] = bme;
- bev_create_ngon(bm, verts, 4, NULL, f1, edges, mat_nr, true);
+ r_f = bev_create_ngon(bm, verts, 4, NULL, f1, edges, mat_nr, true);
}
else {
/* straddles but not a seam: interpolate left half in f1, right half in f2 */
- bev_create_ngon(bm, verts, 4, faces, NULL, NULL, mat_nr, true);
+ r_f = bev_create_ngon(bm, verts, 4, faces, NULL, NULL, mat_nr, true);
}
}
else if (!odd && k == mid) {
/* left poly that touches an even center line on right */
edges[0] = edges[1] = NULL;
edges[2] = edges[3] = bme;
- bev_create_ngon(bm, verts, 4, NULL, f1, edges, mat_nr, true);
+ r_f = bev_create_ngon(bm, verts, 4, NULL, f1, edges, mat_nr, true);
center_bme = BM_edge_exists(verts[2], verts[3]);
BLI_assert(center_bme != NULL);
}
@@ -4803,13 +5082,15 @@ static void bevel_build_edge_polygons(BMesh *bm, BevelParams *bp, BMEdge *bme)
/* right poly that touches an even center line on left */
edges[0] = edges[1] = bme;
edges[2] = edges[3] = NULL;
- bev_create_ngon(bm, verts, 4, NULL, f2, edges, mat_nr, true);
+ r_f = bev_create_ngon(bm, verts, 4, NULL, f2, edges, mat_nr, true);
}
else {
/* doesn't cross or touch the center line, so interpolate in appropriate f1 or f2 */
f = (k <= mid) ? f1 : f2;
- bev_create_ngon(bm, verts, 4, NULL, f, NULL, mat_nr, true);
+ r_f = bev_create_ngon(bm, verts, 4, NULL, f, NULL, mat_nr, true);
}
+ if (do_fix_shading_bv)
+ BLI_ghash_insert(bp->faceHash, r_f, NULL);
verts[0] = verts[3];
verts[1] = verts[2];
}
@@ -5349,7 +5630,8 @@ void BM_mesh_bevel(
const float segments, const float profile,
const bool vertex_only, const bool use_weights, const bool limit_offset,
const struct MDeformVert *dvert, const int vertex_group, const int mat,
- const bool loop_slide)
+ const bool loop_slide, const bool mark_seam, const bool mark_sharp,
+ const int hnmode, void *mod_bmop_customdata)
{
BMIter iter;
BMVert *v, *v_next;
@@ -5358,6 +5640,9 @@ void BM_mesh_bevel(
BevelParams bp = {NULL};
GHashIterator giter;
+ BMOperator *op;
+ BevelModNorEditData *clnordata;
+
bp.offset = offset;
bp.offset_type = offset_type;
bp.seg = segments;
@@ -5370,8 +5655,12 @@ void BM_mesh_bevel(
bp.dvert = dvert;
bp.vertex_group = vertex_group;
bp.mat_nr = mat;
+ bp.mark_seam = mark_seam;
+ bp.mark_sharp = mark_sharp;
+ bp.hnmode = hnmode;
+ bp.faceHash = NULL;
- if (profile >= 0.999f) { /* r ~ 692, so PRO_SQUARE_R is 1e4 */
+ if (profile >= 0.950f) { /* r ~ 692, so PRO_SQUARE_R is 1e4 */
bp.pro_super_r = PRO_SQUARE_R;
}
@@ -5382,6 +5671,15 @@ void BM_mesh_bevel(
BLI_memarena_use_calloc(bp.mem_arena);
set_profile_spacing(&bp);
+ /* Stores BMOp if executed through tool else stores BevelModNorEditData */
+ if (bm->use_toolflags)
+ op = mod_bmop_customdata;
+ else {
+ clnordata = mod_bmop_customdata;
+ clnordata->faceHash = BLI_ghash_ptr_new(__func__);
+ bp.faceHash = clnordata->faceHash;
+ }
+
/* Analyze input vertices, sorting edges and assigning initial new vertex positions */
BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) {
if (BM_elem_flag_test(v, BM_ELEM_TAG)) {
@@ -5426,6 +5724,15 @@ void BM_mesh_bevel(
}
}
+ /* Extend edge data like sharp edges and precompute normals for harden */
+ GHASH_ITER(giter, bp.vert_hash) {
+ bv = BLI_ghashIterator_getValue(&giter);
+ bevel_extend_edge_data(bv);
+ if (bm->use_toolflags) {
+ bevel_harden_normals_mode(bm, &bp, bv, op);
+ }
+ }
+
/* Rebuild face polygons around affected vertices */
BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) {
if (BM_elem_flag_test(v, BM_ELEM_TAG)) {
diff --git a/source/blender/bmesh/tools/bmesh_bevel.h b/source/blender/bmesh/tools/bmesh_bevel.h
index 386dc8a1fce..5cf8b1e78bb 100644
--- a/source/blender/bmesh/tools/bmesh_bevel.h
+++ b/source/blender/bmesh/tools/bmesh_bevel.h
@@ -33,6 +33,7 @@ void BM_mesh_bevel(
BMesh *bm, const float offset, const int offset_type, const float segments,
const float profile, const bool vertex_only, const bool use_weights,
const bool limit_offset, const struct MDeformVert *dvert, const int vertex_group,
- const int mat, const bool loop_slide);
+ const int mat, const bool loop_slide, const bool mark_seam, const bool mark_sharp,
+ const int hnmode, void *mod_bmop_customdata);
#endif /* __BMESH_BEVEL_H__ */