diff options
-rw-r--r-- | source/blender/bmesh/intern/bmesh_mesh.c | 328 | ||||
-rw-r--r-- | source/blender/bmesh/intern/bmesh_mesh.h | 3 | ||||
-rw-r--r-- | source/blender/editors/mesh/editmesh_rip.c | 4 | ||||
-rw-r--r-- | source/blender/editors/mesh/editmesh_tools.c | 42 |
4 files changed, 377 insertions, 0 deletions
diff --git a/source/blender/bmesh/intern/bmesh_mesh.c b/source/blender/bmesh/intern/bmesh_mesh.c index 854421d32d4..fe7c9dba08e 100644 --- a/source/blender/bmesh/intern/bmesh_mesh.c +++ b/source/blender/bmesh/intern/bmesh_mesh.c @@ -34,6 +34,7 @@ #include "BLI_utildefines.h" #include "BKE_editmesh.h" +#include "BKE_global.h" #include "BKE_mesh.h" #include "BKE_multires.h" @@ -1005,6 +1006,268 @@ static void bm_mesh_loops_calc_normals(BMesh *bm, } } +/* This threshold is a bit touchy (usual float precision issue), this value seems OK. */ +#define LNOR_SPACE_TRIGO_THRESHOLD (1.0f - 1e-4f) + +/** + * Check each current smooth fan (one lnor space per smooth fan!), and if all its + * matching custom lnors are not (enough) equal, add sharp edges as needed. + */ +static bool bm_mesh_loops_split_lnor_fans(BMesh *bm, + MLoopNorSpaceArray *lnors_spacearr, + const float (*new_lnors)[3]) +{ + BLI_bitmap *done_loops = BLI_BITMAP_NEW((size_t)bm->totloop, __func__); + bool changed = false; + + BLI_assert(lnors_spacearr->data_type == MLNOR_SPACEARR_BMLOOP_PTR); + + for (int i = 0; i < bm->totloop; i++) { + if (!lnors_spacearr->lspacearr[i]) { + /* This should not happen in theory, but in some rare case (probably ugly geometry) + * we can get some NULL loopspacearr at this point. :/ + * Maybe we should set those loops' edges as sharp? + */ + BLI_BITMAP_ENABLE(done_loops, i); + if (G.debug & G_DEBUG) { + printf("WARNING! Getting invalid NULL loop space for loop %d!\n", i); + } + continue; + } + + if (!BLI_BITMAP_TEST(done_loops, i)) { + /* Notes: + * * In case of mono-loop smooth fan, we have nothing to do. + * * Loops in this linklist are ordered (in reversed order compared to how they were + * discovered by BKE_mesh_normals_loop_split(), but this is not a problem). + * Which means if we find a mismatching clnor, + * we know all remaining loops will have to be in a new, different smooth fan/lnor space. + * * In smooth fan case, we compare each clnor against a ref one, + * to avoid small differences adding up into a real big one in the end! + */ + if (lnors_spacearr->lspacearr[i]->flags & MLNOR_SPACE_IS_SINGLE) { + BLI_BITMAP_ENABLE(done_loops, i); + continue; + } + + LinkNode *loops = lnors_spacearr->lspacearr[i]->loops; + BMLoop *prev_ml = NULL; + const float *org_nor = NULL; + + while (loops) { + BMLoop *ml = loops->link; + const int lidx = BM_elem_index_get(ml); + const float *nor = new_lnors[lidx]; + + if (!org_nor) { + org_nor = nor; + } + else if (dot_v3v3(org_nor, nor) < LNOR_SPACE_TRIGO_THRESHOLD) { + /* Current normal differs too much from org one, we have to tag the edge between + * previous loop's face and current's one as sharp. + * We know those two loops do not point to the same edge, + * since we do not allow reversed winding in a same smooth fan. + */ + BMEdge *e = (prev_ml->e == ml->prev->e) ? prev_ml->e : ml->e; + + BM_elem_flag_disable(e, BM_ELEM_TAG | BM_ELEM_SMOOTH); + changed = true; + + org_nor = nor; + } + + prev_ml = ml; + loops = loops->next; + BLI_BITMAP_ENABLE(done_loops, lidx); + } + + /* We also have to check between last and first loops, + * otherwise we may miss some sharp edges here! + * This is just a simplified version of above while loop. + * See T45984. */ + loops = lnors_spacearr->lspacearr[i]->loops; + if (loops && org_nor) { + BMLoop *ml = loops->link; + const int lidx = BM_elem_index_get(ml); + const float *nor = new_lnors[lidx]; + + if (dot_v3v3(org_nor, nor) < LNOR_SPACE_TRIGO_THRESHOLD) { + BMEdge *e = (prev_ml->e == ml->prev->e) ? prev_ml->e : ml->e; + + BM_elem_flag_disable(e, BM_ELEM_TAG | BM_ELEM_SMOOTH); + changed = true; + } + } + } + } + + MEM_freeN(done_loops); + return changed; +} + +/** + * Assign custom normal data from given normal vectors, averaging normals + * from one smooth fan as necessary. + */ +static void bm_mesh_loops_assign_normal_data(BMesh *bm, + MLoopNorSpaceArray *lnors_spacearr, + short (*r_clnors_data)[2], + const int cd_loop_clnors_offset, + const float (*new_lnors)[3]) +{ + BLI_bitmap *done_loops = BLI_BITMAP_NEW((size_t)bm->totloop, __func__); + + BLI_SMALLSTACK_DECLARE(clnors_data, short *); + + BLI_assert(lnors_spacearr->data_type == MLNOR_SPACEARR_BMLOOP_PTR); + + for (int i = 0; i < bm->totloop; i++) { + if (!lnors_spacearr->lspacearr[i]) { + BLI_BITMAP_ENABLE(done_loops, i); + if (G.debug & G_DEBUG) { + printf("WARNING! Still getting invalid NULL loop space in second loop for loop %d!\n", i); + } + continue; + } + + if (!BLI_BITMAP_TEST(done_loops, i)) { + /* Note we accumulate and average all custom normals in current smooth fan, + * to avoid getting different clnors data (tiny differences in plain custom normals can + * give rather huge differences in computed 2D factors). + */ + LinkNode *loops = lnors_spacearr->lspacearr[i]->loops; + + if (lnors_spacearr->lspacearr[i]->flags & MLNOR_SPACE_IS_SINGLE) { + BMLoop *ml = (BMLoop *)loops; + const int lidx = BM_elem_index_get(ml); + + BLI_assert(lidx == i); + + const float *nor = new_lnors[lidx]; + short *clnor = r_clnors_data ? &r_clnors_data[lidx] : + BM_ELEM_CD_GET_VOID_P(ml, cd_loop_clnors_offset); + + BKE_lnor_space_custom_normal_to_data(lnors_spacearr->lspacearr[i], nor, clnor); + BLI_BITMAP_ENABLE(done_loops, i); + } + else { + int nbr_nors = 0; + float avg_nor[3]; + short clnor_data_tmp[2], *clnor_data; + + zero_v3(avg_nor); + + while (loops) { + BMLoop *ml = loops->link; + const int lidx = BM_elem_index_get(ml); + const float *nor = new_lnors[lidx]; + short *clnor = r_clnors_data ? &r_clnors_data[lidx] : + BM_ELEM_CD_GET_VOID_P(ml, cd_loop_clnors_offset); + + nbr_nors++; + add_v3_v3(avg_nor, nor); + BLI_SMALLSTACK_PUSH(clnors_data, clnor); + + loops = loops->next; + BLI_BITMAP_ENABLE(done_loops, lidx); + } + + mul_v3_fl(avg_nor, 1.0f / (float)nbr_nors); + BKE_lnor_space_custom_normal_to_data( + lnors_spacearr->lspacearr[i], avg_nor, clnor_data_tmp); + + while ((clnor_data = BLI_SMALLSTACK_POP(clnors_data))) { + clnor_data[0] = clnor_data_tmp[0]; + clnor_data[1] = clnor_data_tmp[1]; + } + } + } + } + + MEM_freeN(done_loops); +} + +/** + * Compute internal representation of given custom normals (as an array of float[2] or data layer). + * + * It also makes sure the mesh matches those custom normals, by marking new sharp edges to split + * the smooth fans when loop normals for the same vertex are different, or averaging the normals + * instead, depending on the do_split_fans parameter. + */ +static void bm_mesh_loops_custom_normals_set(BMesh *bm, + const float (*vcos)[3], + const float (*vnos)[3], + const float (*fnos)[3], + MLoopNorSpaceArray *r_lnors_spacearr, + short (*r_clnors_data)[2], + const int cd_loop_clnors_offset, + float (*new_lnors)[3], + const int cd_new_lnors_offset, + bool do_split_fans) +{ + BMFace *f; + BMLoop *l; + BMIter liter, fiter; + float(*cur_lnors)[3] = MEM_mallocN(sizeof(*cur_lnors) * bm->totloop, __func__); + + BKE_lnor_spacearr_clear(r_lnors_spacearr); + + /* Tag smooth edges and set lnos from vnos when they might be completely smooth... + * When using custom loop normals, disable the angle feature! */ + bm_mesh_edges_sharp_tag(bm, vnos, fnos, cur_lnors, (float)M_PI, 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, cur_lnors, r_lnors_spacearr, r_clnors_data, cd_loop_clnors_offset, false); + + /* Extract new normals from the data layer if necessary. */ + float(*custom_lnors)[3] = new_lnors; + + if (new_lnors == NULL) { + custom_lnors = MEM_mallocN(sizeof(*new_lnors) * bm->totloop, __func__); + + BM_ITER_MESH (f, &fiter, bm, BM_FACES_OF_MESH) { + BM_ITER_ELEM (l, &liter, f, BM_LOOPS_OF_FACE) { + const float *normal = BM_ELEM_CD_GET_VOID_P(l, cd_new_lnors_offset); + copy_v3_v3(custom_lnors[BM_elem_index_get(l)], normal); + } + } + } + + /* Validate the new normals. */ + for (int i = 0; i < bm->totloop; i++) { + if (is_zero_v3(custom_lnors[i])) { + copy_v3_v3(custom_lnors[i], cur_lnors[i]); + } + else { + normalize_v3(custom_lnors[i]); + } + } + + /* Now, check each current smooth fan (one lnor space per smooth fan!), + * and if all its matching custom lnors are not equal, add sharp edges as needed. */ + if (do_split_fans && bm_mesh_loops_split_lnor_fans(bm, r_lnors_spacearr, custom_lnors)) { + /* If any sharp edges were added, run bm_mesh_loops_calc_normals() again to get lnor + * spacearr/smooth fans matching the given custom lnors. */ + BKE_lnor_spacearr_clear(r_lnors_spacearr); + + bm_mesh_loops_calc_normals( + bm, vcos, fnos, cur_lnors, r_lnors_spacearr, r_clnors_data, cd_loop_clnors_offset, false); + } + + /* And we just have to convert plain object-space custom normals to our + * lnor space-encoded ones. */ + bm_mesh_loops_assign_normal_data( + bm, r_lnors_spacearr, r_clnors_data, cd_loop_clnors_offset, custom_lnors); + + MEM_freeN(cur_lnors); + + if (custom_lnors != new_lnors) { + MEM_freeN(custom_lnors); + } +} + static void bm_mesh_loops_calc_normals_no_autosmooth(BMesh *bm, const float (*vnos)[3], const float (*fnos)[3], @@ -1628,6 +1891,71 @@ void BM_loop_normal_editdata_array_free(BMLoopNorEditDataArray *lnors_ed_arr) MEM_freeN(lnors_ed_arr); } +bool BM_custom_loop_normals_to_vector_layer(BMesh *bm) +{ + BMFace *f; + BMLoop *l; + BMIter liter, fiter; + + if (!CustomData_has_layer(&bm->ldata, CD_CUSTOMLOOPNORMAL)) { + return false; + } + + BM_lnorspace_update(bm); + BM_mesh_elem_index_ensure(bm, BM_LOOP); + + /* Create a loop normal layer. */ + if (!CustomData_has_layer(&bm->ldata, CD_NORMAL)) { + BM_data_layer_add(bm, &bm->ldata, CD_NORMAL); + + CustomData_set_layer_flag(&bm->ldata, CD_NORMAL, CD_FLAG_TEMPORARY); + } + + const int cd_custom_normal_offset = CustomData_get_offset(&bm->ldata, CD_CUSTOMLOOPNORMAL); + const int cd_normal_offset = CustomData_get_offset(&bm->ldata, CD_NORMAL); + + BM_ITER_MESH (f, &fiter, bm, BM_FACES_OF_MESH) { + BM_ITER_ELEM (l, &liter, f, BM_LOOPS_OF_FACE) { + const int l_index = BM_elem_index_get(l); + const short *clnors_data = BM_ELEM_CD_GET_VOID_P(l, cd_custom_normal_offset); + float *normal = BM_ELEM_CD_GET_VOID_P(l, cd_normal_offset); + + BKE_lnor_space_custom_data_to_normal( + bm->lnor_spacearr->lspacearr[l_index], clnors_data, normal); + } + } + + return true; +} + +void BM_custom_loop_normals_from_vector_layer(BMesh *bm, bool add_sharp_edges) +{ + if (!CustomData_has_layer(&bm->ldata, CD_CUSTOMLOOPNORMAL) || + !CustomData_has_layer(&bm->ldata, CD_NORMAL)) { + return; + } + + const int cd_custom_normal_offset = CustomData_get_offset(&bm->ldata, CD_CUSTOMLOOPNORMAL); + const int cd_normal_offset = CustomData_get_offset(&bm->ldata, CD_NORMAL); + + if (bm->lnor_spacearr == NULL) { + bm->lnor_spacearr = MEM_callocN(sizeof(*bm->lnor_spacearr), __func__); + } + + bm_mesh_loops_custom_normals_set(bm, + NULL, + NULL, + NULL, + bm->lnor_spacearr, + NULL, + cd_custom_normal_offset, + NULL, + cd_normal_offset, + add_sharp_edges); + + bm->spacearr_dirty &= ~(BM_SPACEARR_DIRTY | BM_SPACEARR_DIRTY_ALL); +} + /** * \brief BMesh Begin Edit * diff --git a/source/blender/bmesh/intern/bmesh_mesh.h b/source/blender/bmesh/intern/bmesh_mesh.h index fa542ba5f12..4ba0d948499 100644 --- a/source/blender/bmesh/intern/bmesh_mesh.h +++ b/source/blender/bmesh/intern/bmesh_mesh.h @@ -73,6 +73,9 @@ struct BMLoopNorEditDataArray *BM_loop_normal_editdata_array_init(BMesh *bm, const bool do_all_loops_of_vert); void BM_loop_normal_editdata_array_free(struct BMLoopNorEditDataArray *lnors_ed_arr); +bool BM_custom_loop_normals_to_vector_layer(struct BMesh *bm); +void BM_custom_loop_normals_from_vector_layer(struct BMesh *bm, bool add_sharp_edges); + void BM_edges_sharp_from_angle_set(BMesh *bm, const float split_angle); void bmesh_edit_begin(BMesh *bm, const BMOpTypeFlag type_flag); diff --git a/source/blender/editors/mesh/editmesh_rip.c b/source/blender/editors/mesh/editmesh_rip.c index 5d9923c6a7d..e805507351c 100644 --- a/source/blender/editors/mesh/editmesh_rip.c +++ b/source/blender/editors/mesh/editmesh_rip.c @@ -1053,6 +1053,8 @@ static int edbm_rip_invoke(bContext *C, wmOperator *op, const wmEvent *event) * useful selection for grabbing. */ + BM_custom_loop_normals_to_vector_layer(bm); + /* BM_ELEM_SELECT --> BM_ELEM_TAG */ BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) { BM_elem_flag_set(e, BM_ELEM_TAG, BM_elem_flag_test(e, BM_ELEM_SELECT)); @@ -1070,6 +1072,8 @@ static int edbm_rip_invoke(bContext *C, wmOperator *op, const wmEvent *event) continue; } + BM_custom_loop_normals_from_vector_layer(bm, false); + BLI_assert(singlesel ? (bm->totvertsel > 0) : (bm->totedgesel > 0)); if (bm->totvertsel == 0) { diff --git a/source/blender/editors/mesh/editmesh_tools.c b/source/blender/editors/mesh/editmesh_tools.c index 142bc119958..69497d019b8 100644 --- a/source/blender/editors/mesh/editmesh_tools.c +++ b/source/blender/editors/mesh/editmesh_tools.c @@ -451,6 +451,8 @@ static int edbm_delete_exec(bContext *C, wmOperator *op) BMEditMesh *em = BKE_editmesh_from_object(obedit); const int type = RNA_enum_get(op->ptr, "type"); + BM_custom_loop_normals_to_vector_layer(em->bm); + switch (type) { case MESH_DELETE_VERT: /* Erase Vertices */ if (!(em->bm->totvertsel && @@ -495,6 +497,8 @@ static int edbm_delete_exec(bContext *C, wmOperator *op) EDBM_flag_disable_all(em, BM_ELEM_SELECT); + BM_custom_loop_normals_from_vector_layer(em->bm, false); + EDBM_update_generic(obedit->data, true, true); DEG_id_tag_update(obedit->data, ID_RECALC_SELECT); @@ -1216,6 +1220,8 @@ static bool edbm_connect_vert_pair(BMEditMesh *em, struct Mesh *me, wmOperator * } } if (checks_succeded) { + BM_custom_loop_normals_to_vector_layer(bm); + BMO_op_exec(bm, &bmop); len = BMO_slot_get(bmop.slots_out, "edges.out")->len; @@ -1232,6 +1238,8 @@ static bool edbm_connect_vert_pair(BMEditMesh *em, struct Mesh *me, wmOperator * /* so newly created edges get the selection state from the vertex */ EDBM_selectmode_flush(em); + BM_custom_loop_normals_from_vector_layer(bm, false); + EDBM_update_generic(me, true, true); } } @@ -1524,8 +1532,13 @@ static int edbm_vert_connect_path_exec(bContext *C, wmOperator *op) } } + BM_custom_loop_normals_to_vector_layer(bm); + if (bm_vert_connect_select_history(bm)) { EDBM_selectmode_flush(em); + + BM_custom_loop_normals_from_vector_layer(bm, false); + EDBM_update_generic(obedit->data, true, true); } else { @@ -3183,6 +3196,8 @@ static int edbm_merge_exec(bContext *C, wmOperator *op) continue; } + BM_custom_loop_normals_to_vector_layer(em->bm); + bool ok = false; switch (type) { case MESH_MERGE_CENTER: @@ -3209,6 +3224,8 @@ static int edbm_merge_exec(bContext *C, wmOperator *op) continue; } + BM_custom_loop_normals_from_vector_layer(em->bm, false); + EDBM_update_generic(obedit->data, true, true); /* once collapsed, we can't have edge/face selection */ @@ -3353,6 +3370,8 @@ static int edbm_remove_doubles_exec(bContext *C, wmOperator *op) htype_select = BM_FACE; } + BM_custom_loop_normals_to_vector_layer(em->bm); + /* store selection as tags */ BM_mesh_elem_hflag_enable_test(em->bm, htype_select, BM_ELEM_TAG, true, true, BM_ELEM_SELECT); @@ -3380,6 +3399,8 @@ static int edbm_remove_doubles_exec(bContext *C, wmOperator *op) BM_mesh_elem_hflag_enable_test(em->bm, htype_select, BM_ELEM_SELECT, true, true, BM_ELEM_TAG); EDBM_selectmode_flush(em); + BM_custom_loop_normals_from_vector_layer(em->bm, true); + if (count) { count_multi += count; EDBM_update_generic(obedit->data, true, true); @@ -4047,6 +4068,8 @@ static int edbm_knife_cut_exec(bContext *C, wmOperator *op) MEM_freeN(screen_vert_coords); MEM_freeN(mouse_path); + BM_custom_loop_normals_to_vector_layer(bm); + BMO_slot_buffer_from_enabled_flag(bm, &bmop, bmop.slots_in, "edges", BM_EDGE, ELE_EDGE_CUT); if (mode == KNIFE_MIDPOINT) { @@ -4065,6 +4088,8 @@ static int edbm_knife_cut_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } + BM_custom_loop_normals_from_vector_layer(bm, false); + EDBM_update_generic(obedit->data, true, true); return OPERATOR_FINISHED; @@ -5344,6 +5369,8 @@ static int edbm_tris_convert_to_quads_exec(bContext *C, wmOperator *op) do_vcols = RNA_boolean_get(op->ptr, "vcols"); do_materials = RNA_boolean_get(op->ptr, "materials"); + BM_custom_loop_normals_to_vector_layer(em->bm); + if (!EDBM_op_call_and_selectf( em, op, @@ -5362,6 +5389,8 @@ static int edbm_tris_convert_to_quads_exec(bContext *C, wmOperator *op) continue; } + BM_custom_loop_normals_from_vector_layer(em->bm, false); + EDBM_update_generic(obedit->data, true, true); } MEM_freeN(objects); @@ -5674,6 +5703,8 @@ static int edbm_dissolve_verts_exec(bContext *C, wmOperator *op) continue; } + BM_custom_loop_normals_to_vector_layer(em->bm); + if (!EDBM_op_callf(em, op, "dissolve_verts verts=%hv use_face_split=%b use_boundary_tear=%b", @@ -5682,6 +5713,9 @@ static int edbm_dissolve_verts_exec(bContext *C, wmOperator *op) use_boundary_tear)) { continue; } + + BM_custom_loop_normals_from_vector_layer(em->bm, false); + EDBM_update_generic(obedit->data, true, true); } @@ -5730,6 +5764,8 @@ static int edbm_dissolve_edges_exec(bContext *C, wmOperator *op) continue; } + BM_custom_loop_normals_to_vector_layer(em->bm); + if (!EDBM_op_callf(em, op, "dissolve_edges edges=%he use_verts=%b use_face_split=%b", @@ -5739,6 +5775,8 @@ static int edbm_dissolve_edges_exec(bContext *C, wmOperator *op) continue; } + BM_custom_loop_normals_from_vector_layer(em->bm, false); + EDBM_update_generic(obedit->data, true, true); } @@ -5786,6 +5824,8 @@ static int edbm_dissolve_faces_exec(bContext *C, wmOperator *op) continue; } + BM_custom_loop_normals_to_vector_layer(em->bm); + if (!EDBM_op_call_and_selectf(em, op, "region.out", @@ -5796,6 +5836,8 @@ static int edbm_dissolve_faces_exec(bContext *C, wmOperator *op) continue; } + BM_custom_loop_normals_from_vector_layer(em->bm, false); + EDBM_update_generic(obedit->data, true, true); } MEM_freeN(objects); |