diff options
author | Rohan Rathi <rohanrathi08@gmail.com> | 2018-08-09 17:22:07 +0300 |
---|---|---|
committer | Rohan Rathi <rohanrathi08@gmail.com> | 2018-08-09 17:22:07 +0300 |
commit | 2b41b208c7e5e74a0a5fe043f4bfab2a122a9ed1 (patch) | |
tree | cc5c8948a12a73d3997f8323044d2c117a7af8df /source/blender/bmesh | |
parent | 85cac2221c543e67a9070c94d89d61bf27c7190f (diff) | |
parent | fea5f26ea56acd73d043e97a96d06067b24ef811 (diff) |
Merge branch 'soc-2018-bevel' into blender2.8
Diffstat (limited to 'source/blender/bmesh')
-rw-r--r-- | source/blender/bmesh/bmesh_class.h | 29 | ||||
-rw-r--r-- | source/blender/bmesh/intern/bmesh_mesh.c | 445 | ||||
-rw-r--r-- | source/blender/bmesh/intern/bmesh_mesh.h | 21 | ||||
-rw-r--r-- | source/blender/bmesh/intern/bmesh_opdefines.c | 5 | ||||
-rw-r--r-- | source/blender/bmesh/intern/bmesh_operator_api.h | 1 | ||||
-rw-r--r-- | source/blender/bmesh/intern/bmesh_operators.h | 7 | ||||
-rw-r--r-- | source/blender/bmesh/operators/bmo_bevel.c | 6 | ||||
-rw-r--r-- | source/blender/bmesh/tools/bmesh_bevel.c | 375 | ||||
-rw-r--r-- | source/blender/bmesh/tools/bmesh_bevel.h | 3 |
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__ */ |