diff options
Diffstat (limited to 'source/blender/editors/mesh/editmesh_bevel.c')
-rw-r--r-- | source/blender/editors/mesh/editmesh_bevel.c | 268 |
1 files changed, 221 insertions, 47 deletions
diff --git a/source/blender/editors/mesh/editmesh_bevel.c b/source/blender/editors/mesh/editmesh_bevel.c index e44a43c4c3a..39bebc9a980 100644 --- a/source/blender/editors/mesh/editmesh_bevel.c +++ b/source/blender/editors/mesh/editmesh_bevel.c @@ -30,6 +30,7 @@ #include "BLI_string.h" #include "BLI_math.h" +#include "BLI_linklist_stack.h" #include "BLT_translation.h" @@ -37,6 +38,8 @@ #include "BKE_global.h" #include "BKE_editmesh.h" #include "BKE_unit.h" +#include "BKE_layer.h" +#include "BKE_mesh.h" #include "RNA_define.h" #include "RNA_access.h" @@ -77,17 +80,24 @@ static const float value_scale_per_inch[NUM_VALUE_KINDS] = { 0.0f, 100.0f, 1.0f, typedef struct { BMEditMesh *em; + BMBackup mesh_backup; +} BevelObjectStore; + + +typedef struct { float initial_length[NUM_VALUE_KINDS]; float scale[NUM_VALUE_KINDS]; NumInput num_input[NUM_VALUE_KINDS]; float shift_value[NUM_VALUE_KINDS]; /* The current value when shift is pressed. Negative when shift not active. */ bool is_modal; + BevelObjectStore *ob_store; + uint ob_store_len; + /* modal only */ float mcenter[2]; - BMBackup mesh_backup; void *draw_handle_pixel; - short twtype; + short gizmo_flag; short value_mode; /* Which value does mouse movement and numeric input affect? */ float segments; /* Segments as float so smooth mouse pan works in small increments */ } BevelData; @@ -122,30 +132,133 @@ static void edbm_bevel_update_header(bContext *C, wmOperator *op) WM_bool_as_string(opdata->value_mode == PROFILE_VALUE), offset_str, RNA_int_get(op->ptr, "segments"), RNA_float_get(op->ptr, "profile")); - ED_area_headerprint(sa, msg); + ED_area_status_text(sa, msg); + } +} + +static void bevel_harden_normals(BMEditMesh *em, BMOperator *bmop, float face_strength) +{ + BKE_editmesh_lnorspace_update(em); + BM_normals_loops_edges_tag(em->bm, true); + const int cd_clnors_offset = CustomData_get_offset(&em->bm->ldata, CD_CUSTOMLOOPNORMAL); + + BMesh *bm = em->bm; + BMFace *f; + BMLoop *l, *l_cur, *l_first; + BMIter fiter; + + BMOpSlot *nslot = BMO_slot_get(bmop->slots_out, "normals.out"); /* Per vertex normals depending on hn_mode */ + + /* Similar functionality to bm_mesh_loops_calc_normals... Edges that can be smoothed are tagged */ + BM_ITER_MESH(f, &fiter, bm, BM_FACES_OF_MESH) { + l_cur = l_first = BM_FACE_FIRST_LOOP(f); + do { + if ((BM_elem_flag_test(l_cur->v, BM_ELEM_SELECT)) && + ((!BM_elem_flag_test(l_cur->e, BM_ELEM_TAG)) || + (!BM_elem_flag_test(l_cur, BM_ELEM_TAG) && BM_loop_check_cyclic_smooth_fan(l_cur)))) + { + /* Both adjacent loops are sharp, set clnor to face normal */ + if (!BM_elem_flag_test(l_cur->e, BM_ELEM_TAG) && !BM_elem_flag_test(l_cur->prev->e, BM_ELEM_TAG)) { + const int loop_index = BM_elem_index_get(l_cur); + short *clnors = BM_ELEM_CD_GET_VOID_P(l_cur, cd_clnors_offset); + BKE_lnor_space_custom_normal_to_data(bm->lnor_spacearr->lspacearr[loop_index], f->no, clnors); + } + else { + /* Find next corresponding sharp edge in this smooth fan */ + BMVert *v_pivot = l_cur->v; + float *calc_n = BLI_ghash_lookup(nslot->data.ghash, v_pivot); + + BMEdge *e_next; + const BMEdge *e_org = l_cur->e; + BMLoop *lfan_pivot, *lfan_pivot_next; + + lfan_pivot = l_cur; + e_next = lfan_pivot->e; + BLI_SMALLSTACK_DECLARE(loops, BMLoop *); + float cn_wght[3] = { 0.0f, 0.0f, 0.0f }, cn_unwght[3] = { 0.0f, 0.0f, 0.0f }; + + /* Fan through current vert and accumulate normals and loops */ + while (true) { + lfan_pivot_next = BM_vert_step_fan_loop(lfan_pivot, &e_next); + if (lfan_pivot_next) { + BLI_assert(lfan_pivot_next->v == v_pivot); + } + else { + e_next = (lfan_pivot->e == e_next) ? lfan_pivot->prev->e : lfan_pivot->e; + } + + BLI_SMALLSTACK_PUSH(loops, lfan_pivot); + float cur[3]; + mul_v3_v3fl(cur, lfan_pivot->f->no, BM_face_calc_area(lfan_pivot->f)); + add_v3_v3(cn_wght, cur); + + if (BM_elem_flag_test(lfan_pivot->f, BM_ELEM_SELECT)) + add_v3_v3(cn_unwght, cur); + + if (!BM_elem_flag_test(e_next, BM_ELEM_TAG) || (e_next == e_org)) { + break; + } + lfan_pivot = lfan_pivot_next; + } + + normalize_v3(cn_wght); + normalize_v3(cn_unwght); + if (calc_n) { + mul_v3_fl(cn_wght, face_strength); + mul_v3_fl(calc_n, 1.0f - face_strength); + add_v3_v3(calc_n, cn_wght); + normalize_v3(calc_n); + } + while ((l = BLI_SMALLSTACK_POP(loops))) { + const int l_index = BM_elem_index_get(l); + short *clnors = BM_ELEM_CD_GET_VOID_P(l, cd_clnors_offset); + if (calc_n) { + BKE_lnor_space_custom_normal_to_data( + bm->lnor_spacearr->lspacearr[l_index], calc_n, clnors); + } + else { + BKE_lnor_space_custom_normal_to_data( + bm->lnor_spacearr->lspacearr[l_index], cn_unwght, clnors); + } + } + BLI_ghash_remove(nslot->data.ghash, v_pivot, NULL, MEM_freeN); + } + } + } while ((l_cur = l_cur->next) != l_first); } } static bool edbm_bevel_init(bContext *C, wmOperator *op, const bool is_modal) { - Object *obedit = CTX_data_edit_object(C); Scene *scene = CTX_data_scene(C); - BMEditMesh *em = BKE_editmesh_from_object(obedit); BevelData *opdata; + ViewLayer *view_layer = CTX_data_view_layer(C); float pixels_per_inch; int i; - if (em->bm->totvertsel == 0) { - return false; - } - if (is_modal) { RNA_float_set(op->ptr, "offset", 0.0f); } op->customdata = opdata = MEM_mallocN(sizeof(BevelData), "beveldata_mesh_operator"); + uint objects_used_len = 0; + + { + uint ob_store_len = 0; + Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(view_layer, &ob_store_len); + opdata->ob_store = MEM_malloc_arrayN(ob_store_len, sizeof(*opdata->ob_store), __func__); + for (uint ob_index = 0; ob_index < ob_store_len; ob_index++) { + Object *obedit = objects[ob_index]; + BMEditMesh *em = BKE_editmesh_from_object(obedit); + if (em->bm->totvertsel > 0) { + opdata->ob_store[objects_used_len].em = em; + objects_used_len++; + } + } + MEM_freeN(objects); + opdata->ob_store_len = objects_used_len; + } - opdata->em = em; opdata->is_modal = is_modal; opdata->value_mode = OFFSET_VALUE; opdata->segments = (float) RNA_int_get(op->ptr, "segments"); @@ -174,14 +287,16 @@ static bool edbm_bevel_init(bContext *C, wmOperator *op, const bool is_modal) View3D *v3d = CTX_wm_view3d(C); ARegion *ar = CTX_wm_region(C); - opdata->mesh_backup = EDBM_redo_state_store(em); + for (uint ob_index = 0; ob_index < opdata->ob_store_len; ob_index++) { + opdata->ob_store[ob_index].mesh_backup = EDBM_redo_state_store(opdata->ob_store[ob_index].em); + } opdata->draw_handle_pixel = ED_region_draw_cb_activate(ar->type, ED_region_draw_mouse_line_cb, opdata->mcenter, REGION_DRAW_POST_PIXEL); G.moving = G_TRANSFORM_EDIT; if (v3d) { - opdata->twtype = v3d->twtype; - v3d->twtype = 0; + opdata->gizmo_flag = v3d->gizmo_flag; + v3d->gizmo_flag = V3D_GIZMO_HIDE; } } @@ -191,8 +306,10 @@ static bool edbm_bevel_init(bContext *C, wmOperator *op, const bool is_modal) static bool edbm_bevel_calc(wmOperator *op) { BevelData *opdata = op->customdata; - BMEditMesh *em = opdata->em; + BMEditMesh *em; BMOperator bmop; + bool changed = false; + const float offset = RNA_float_get(op->ptr, "offset"); const int offset_type = RNA_enum_get(op->ptr, "offset_type"); const int segments = RNA_int_get(op->ptr, "segments"); @@ -201,41 +318,55 @@ static bool edbm_bevel_calc(wmOperator *op) const bool clamp_overlap = RNA_boolean_get(op->ptr, "clamp_overlap"); int material = RNA_int_get(op->ptr, "material"); const bool loop_slide = RNA_boolean_get(op->ptr, "loop_slide"); + const bool mark_seam = RNA_boolean_get(op->ptr, "mark_seam"); + const bool mark_sharp = RNA_boolean_get(op->ptr, "mark_sharp"); + const float hn_strength = RNA_float_get(op->ptr, "strength"); + const int hnmode = RNA_enum_get(op->ptr, "hnmode"); - /* revert to original mesh */ - if (opdata->is_modal) { - EDBM_redo_state_restore(opdata->mesh_backup, em, false); - } - if (em->ob) { - material = CLAMPIS(material, -1, em->ob->totcol - 1); - } + for (uint ob_index = 0; ob_index < opdata->ob_store_len; ob_index++) { + em = opdata->ob_store[ob_index].em; + + /* revert to original mesh */ + if (opdata->is_modal) { + EDBM_redo_state_restore(opdata->ob_store[ob_index].mesh_backup, em, false); + } - EDBM_op_init(em, &bmop, op, - "bevel geom=%hev offset=%f segments=%i vertex_only=%b offset_type=%i profile=%f clamp_overlap=%b " - "material=%i loop_slide=%b", - BM_ELEM_SELECT, offset, segments, vertex_only, offset_type, profile, - clamp_overlap, material, loop_slide); + if (em->ob) { + material = CLAMPIS(material, -1, em->ob->totcol - 1); + } - BMO_op_exec(em->bm, &bmop); + EDBM_op_init( + em, &bmop, op, + "bevel geom=%hev offset=%f segments=%i vertex_only=%b offset_type=%i profile=%f clamp_overlap=%b " + "material=%i loop_slide=%b mark_seam=%b mark_sharp=%b strength=%f hnmode=%i", + BM_ELEM_SELECT, offset, segments, vertex_only, offset_type, profile, + clamp_overlap, material, loop_slide, mark_seam, mark_sharp, hn_strength, hnmode); - if (offset != 0.0f) { - /* not essential, but we may have some loose geometry that - * won't get bevel'd and better not leave it selected */ - EDBM_flag_disable_all(em, BM_ELEM_SELECT); - BMO_slot_buffer_hflag_enable(em->bm, bmop.slots_out, "faces.out", BM_FACE, BM_ELEM_SELECT, true); - } + BMO_op_exec(em->bm, &bmop); - /* no need to de-select existing geometry */ - if (!EDBM_op_finish(em, &bmop, op, true)) { - return false; - } + if (offset != 0.0f) { + /* not essential, but we may have some loose geometry that + * won't get bevel'd and better not leave it selected */ + EDBM_flag_disable_all(em, BM_ELEM_SELECT); + BMO_slot_buffer_hflag_enable(em->bm, bmop.slots_out, "faces.out", BM_FACE, BM_ELEM_SELECT, true); + } - EDBM_mesh_normals_update(opdata->em); + if (hnmode != BEVEL_HN_NONE) { + bevel_harden_normals(em, &bmop, hn_strength); + } - EDBM_update_generic(opdata->em, true, true); + /* no need to de-select existing geometry */ + if (!EDBM_op_finish(em, &bmop, op, true)) { + continue; + } - return true; + EDBM_mesh_normals_update(em); + + EDBM_update_generic(em, true, true); + changed = true; + } + return changed; } static void edbm_bevel_exit(bContext *C, wmOperator *op) @@ -245,20 +376,23 @@ static void edbm_bevel_exit(bContext *C, wmOperator *op) ScrArea *sa = CTX_wm_area(C); if (sa) { - ED_area_headerprint(sa, NULL); + ED_area_status_text(sa, NULL); } if (opdata->is_modal) { View3D *v3d = CTX_wm_view3d(C); ARegion *ar = CTX_wm_region(C); - EDBM_redo_state_free(&opdata->mesh_backup, NULL, false); + for (uint ob_index = 0; ob_index < opdata->ob_store_len; ob_index++) { + EDBM_redo_state_free(&opdata->ob_store[ob_index].mesh_backup, NULL, false); + } ED_region_draw_cb_exit(ar->type, opdata->draw_handle_pixel); if (v3d) { - v3d->twtype = opdata->twtype; + v3d->gizmo_flag = opdata->gizmo_flag; } G.moving = 0; } - MEM_freeN(opdata); + MEM_SAFE_FREE(opdata->ob_store); + MEM_SAFE_FREE(op->customdata); op->customdata = NULL; } @@ -266,8 +400,10 @@ static void edbm_bevel_cancel(bContext *C, wmOperator *op) { BevelData *opdata = op->customdata; if (opdata->is_modal) { - EDBM_redo_state_free(&opdata->mesh_backup, opdata->em, true); - EDBM_update_generic(opdata->em, false, true); + for (uint ob_index = 0; ob_index < opdata->ob_store_len; ob_index++) { + EDBM_redo_state_free(&opdata->ob_store[ob_index].mesh_backup, opdata->ob_store[ob_index].em, true); + EDBM_update_generic(opdata->ob_store[ob_index].em, false, true); + } } edbm_bevel_exit(C, op); @@ -446,7 +582,9 @@ static int edbm_bevel_modal(bContext *C, wmOperator *op, const wmEvent *event) case LEFTMOUSE: case PADENTER: case RETKEY: - if (event->val == KM_PRESS) { + if ((event->val == KM_PRESS) || + ((event->val == KM_RELEASE) && RNA_boolean_get(op->ptr, "release_confirm"))) + { edbm_bevel_calc(op); edbm_bevel_exit(C, op); return OPERATOR_FINISHED; @@ -570,6 +708,26 @@ static int edbm_bevel_modal(bContext *C, wmOperator *op, const wmEvent *event) edbm_bevel_update_header(C, op); handled = true; break; + case UKEY: + if (event->val == KM_RELEASE) + break; + else { + bool mark_seam = RNA_boolean_get(op->ptr, "mark_seam"); + RNA_boolean_set(op->ptr, "mark_seam", !mark_seam); + edbm_bevel_calc(op); + handled = true; + break; + } + case KKEY: + if (event->val == KM_RELEASE) + break; + else { + bool mark_sharp = RNA_boolean_get(op->ptr, "mark_sharp"); + RNA_boolean_set(op->ptr, "mark_sharp", !mark_sharp); + edbm_bevel_calc(op); + handled = true; + break; + } } @@ -608,6 +766,13 @@ void MESH_OT_bevel(wmOperatorType *ot) {0, NULL, 0, NULL, NULL}, }; + static EnumPropertyItem harden_normals_items[] = { + { BEVEL_HN_NONE, "HN_NONE", 0, "Off", "Do not use Harden Normals" }, + { BEVEL_HN_FACE, "HN_FACE", 0, "Face Area", "Use faces as weight" }, + { BEVEL_HN_ADJ, "HN_ADJ", 0, "Vertex average", "Use adjacent vertices as weight" }, + { 0, NULL, 0, NULL, NULL }, + }; + /* identifiers */ ot->name = "Bevel"; ot->description = "Edge Bevel"; @@ -633,6 +798,15 @@ void MESH_OT_bevel(wmOperatorType *ot) RNA_def_boolean(ot->srna, "clamp_overlap", false, "Clamp Overlap", "Do not allow beveled edges/vertices to overlap each other"); RNA_def_boolean(ot->srna, "loop_slide", true, "Loop Slide", "Prefer slide along edge to even widths"); + RNA_def_boolean(ot->srna, "mark_seam", false, "Mark Seams", "Mark Seams along beveled edges"); + RNA_def_boolean(ot->srna, "mark_sharp", false, "Mark Sharp", "Mark beveled edges as sharp"); RNA_def_int(ot->srna, "material", -1, -1, INT_MAX, "Material", "Material for bevel faces (-1 means use adjacent faces)", -1, 100); + RNA_def_float(ot->srna, "strength", 0.5f, 0.0f, 1.0f, "Normal Strength", + "Strength of calculated normal", 0.0f, 1.0f); + RNA_def_enum(ot->srna, "hnmode", harden_normals_items, BEVEL_HN_NONE, "Normal Mode", + "Weighting mode for Harden Normals"); + prop = RNA_def_boolean(ot->srna, "release_confirm", 0, "Confirm on Release", ""); + RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); + } |