diff options
Diffstat (limited to 'source/blender/editors')
-rw-r--r-- | source/blender/editors/include/ED_screen.h | 1 | ||||
-rw-r--r-- | source/blender/editors/include/ED_transform.h | 2 | ||||
-rw-r--r-- | source/blender/editors/mesh/editmesh_tools.c | 1201 | ||||
-rw-r--r-- | source/blender/editors/mesh/editmesh_undo.c | 2 | ||||
-rw-r--r-- | source/blender/editors/mesh/editmesh_utils.c | 5 | ||||
-rw-r--r-- | source/blender/editors/mesh/mesh_intern.h | 8 | ||||
-rw-r--r-- | source/blender/editors/mesh/mesh_ops.c | 11 | ||||
-rw-r--r-- | source/blender/editors/screen/screen_ops.c | 10 | ||||
-rw-r--r-- | source/blender/editors/space_outliner/outliner_draw.c | 1 | ||||
-rw-r--r-- | source/blender/editors/transform/transform.c | 230 | ||||
-rw-r--r-- | source/blender/editors/transform/transform.h | 4 | ||||
-rw-r--r-- | source/blender/editors/transform/transform_generics.c | 3 | ||||
-rw-r--r-- | source/blender/editors/transform/transform_ops.c | 112 |
13 files changed, 1554 insertions, 36 deletions
diff --git a/source/blender/editors/include/ED_screen.h b/source/blender/editors/include/ED_screen.h index af4abc60a23..ff51f6144d2 100644 --- a/source/blender/editors/include/ED_screen.h +++ b/source/blender/editors/include/ED_screen.h @@ -171,6 +171,7 @@ int ED_operator_object_active_editable_font(struct bContext *C); int ED_operator_editmesh(struct bContext *C); int ED_operator_editmesh_view3d(struct bContext *C); int ED_operator_editmesh_region_view3d(struct bContext *C); +int ED_operator_editmesh_auto_smooth(struct bContext *C); int ED_operator_editarmature(struct bContext *C); int ED_operator_editcurve(struct bContext *C); int ED_operator_editcurve_3d(struct bContext *C); diff --git a/source/blender/editors/include/ED_transform.h b/source/blender/editors/include/ED_transform.h index 9a0a7f8f1bb..d70c9a3a91f 100644 --- a/source/blender/editors/include/ED_transform.h +++ b/source/blender/editors/include/ED_transform.h @@ -88,6 +88,7 @@ enum TfmMode { TFM_VERT_SLIDE, TFM_SEQ_SLIDE, TFM_BONE_ENVELOPE_DIST, + TFM_NORMAL_ROTATION, }; /* TRANSFORM CONTEXTS */ @@ -150,6 +151,7 @@ int BIF_countTransformOrientation(const struct bContext *C); #define P_NO_TEXSPACE (1 << 11) #define P_CENTER (1 << 12) #define P_GPENCIL_EDIT (1 << 13) +#define P_CLNOR_INVALIDATE (1 << 14) void Transform_Properties(struct wmOperatorType *ot, int flags); diff --git a/source/blender/editors/mesh/editmesh_tools.c b/source/blender/editors/mesh/editmesh_tools.c index c979a73e964..79adba36302 100644 --- a/source/blender/editors/mesh/editmesh_tools.c +++ b/source/blender/editors/mesh/editmesh_tools.c @@ -41,11 +41,16 @@ #include "DNA_object_types.h" #include "DNA_scene_types.h" +#include "BLI_bitmap.h" +#include "BLI_heap.h" #include "BLI_listbase.h" +#include "BLI_linklist.h" +#include "BLI_linklist_stack.h" #include "BLI_noise.h" #include "BLI_math.h" #include "BLI_rand.h" #include "BLI_sort_utils.h" +#include "BLI_string.h" #include "BKE_material.h" #include "BKE_context.h" @@ -54,6 +59,7 @@ #include "BKE_report.h" #include "BKE_texture.h" #include "BKE_main.h" +#include "BKE_mesh.h" #include "BKE_editmesh.h" #include "BLT_translation.h" @@ -5987,3 +5993,1198 @@ void MESH_OT_mark_freestyle_face(wmOperatorType *ot) } #endif + +/* Initialize loop normal data */ +static int point_normals_init(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) +{ + Object *obedit = CTX_data_edit_object(C); + BMEditMesh *em = BKE_editmesh_from_object(obedit); + BMesh *bm = em->bm; + + BKE_editmesh_lnorspace_update(em); + BMLoopNorEditDataArray *lnors_ed_arr = BM_loop_normal_editdata_array_init(bm); + + lnors_ed_arr->funcdata = NULL; + op->customdata = lnors_ed_arr; + + return lnors_ed_arr->totloop; +} + +static void point_normals_apply(bContext *C, wmOperator *op, const wmEvent *UNUSED(event), float target[3]) +{ + Object *obedit = CTX_data_edit_object(C); + BMesh *bm = BKE_editmesh_from_object(obedit)->bm; + BMLoopNorEditDataArray *lnors_ed_arr = op->customdata; + + const bool do_point_away = RNA_boolean_get(op->ptr, "point_away"); + const bool do_spherize = RNA_boolean_get(op->ptr, "spherize"); + const bool do_align = RNA_boolean_get(op->ptr, "align"); + const float null_vec[3] = { 0.0f }; + float center[3]; + + if (do_align) { + BMVert *v; + BMIter viter; + int i = 0; + zero_v3(center); + BM_ITER_MESH(v, &viter, bm, BM_VERTS_OF_MESH) { + if (BM_elem_flag_test(v, BM_ELEM_SELECT)) { + add_v3_v3(center, v->co); + i++; + } + } + mul_v3_fl(center, 1.0f / (float)i); + } + + BMLoopNorEditData *lnor_ed = lnors_ed_arr->lnor_editdata; + for (int i = 0; i < lnors_ed_arr->totloop; i++, lnor_ed++) { + if (do_spherize) { + const float strength = RNA_float_get(op->ptr, "strength"); + float spherized_normal[3] = { 0.0f }; + + sub_v3_v3v3(spherized_normal, target, lnor_ed->loc); + sub_v3_v3(spherized_normal, obedit->loc); + mul_v3_fl(spherized_normal, strength); + mul_v3_fl(lnor_ed->nloc, 1.0f - strength); + sub_v3_v3(lnor_ed->nloc, spherized_normal); + } + else if (do_align) { + sub_v3_v3v3(lnor_ed->nloc, target, center); + sub_v3_v3(lnor_ed->nloc, obedit->loc); + } + else { + sub_v3_v3v3(lnor_ed->nloc, target, lnor_ed->loc); + sub_v3_v3(lnor_ed->nloc, obedit->loc); + } + + if (do_point_away) { + negate_v3(lnor_ed->nloc); + } + if (lnor_ed->loop_index != -1 && !compare_v3v3(lnor_ed->nloc, null_vec, 1e-4f)) { + normalize_v3(lnor_ed->nloc); + BKE_lnor_space_custom_normal_to_data( + bm->lnor_spacearr->lspacearr[lnor_ed->loop_index], lnor_ed->nloc, lnor_ed->clnors_data); + } + } +} + +static void point_normals_free(bContext *C, wmOperator *op) +{ + BMLoopNorEditDataArray *lnors_ed_arr = op->customdata; + BM_loop_normal_editdata_array_free(lnors_ed_arr); + op->customdata = NULL; + ED_area_headerprint(CTX_wm_area(C), NULL); +} + +static int point_normals_mouse(bContext *C, wmOperator *op, const wmEvent *event) +{ + View3D *v3d = CTX_wm_view3d(C); + Object *obedit = CTX_data_edit_object(C); + ARegion *ar = CTX_wm_region(C); + BMEditMesh *em = BKE_editmesh_from_object(obedit); + BMesh *bm = em->bm; + BMVert *v; + BMIter viter; + int i = 0; + + float target[3], center[3]; + + RNA_float_get_array(op->ptr, "target_location", target); + + zero_v3(center); + BM_ITER_MESH(v, &viter, bm, BM_VERTS_OF_MESH) { + if (BM_elem_flag_test(v, BM_ELEM_SELECT)) { + add_v3_v3(center, v->co); + add_v3_v3(center, obedit->loc); + i++; + } + } + mul_v3_fl(center, 1.0f / (float)i); + + ED_view3d_win_to_3d_int(v3d, ar, center, event->mval, target); + + point_normals_apply(C, op, event, target); + EDBM_update_generic(em, true, false); + + if (event->type == LEFTMOUSE) { + RNA_float_set_array(op->ptr, "target_location", target); + point_normals_free(C, op); + return OPERATOR_FINISHED; + } + else if ((ISKEYBOARD(event->type) || event->type == RIGHTMOUSE) && event->type != MKEY) { + BMLoopNorEditDataArray *lnors_ed_arr = op->customdata; + + BMLoopNorEditData *lnor_ed = lnors_ed_arr->lnor_editdata; + for (i = 0; i < lnors_ed_arr->totloop; i++, lnor_ed++) { /* Reset custom normal data. */ + if (lnor_ed->loop_index != -1) { + BKE_lnor_space_custom_normal_to_data( + bm->lnor_spacearr->lspacearr[lnor_ed->loop_index], lnor_ed->niloc, lnor_ed->clnors_data); + } + } + point_normals_free(C, op); + return OPERATOR_CANCELLED; + } + return OPERATOR_PASS_THROUGH; +} + +static int edbm_point_normals_modal(bContext *C, wmOperator *op, const wmEvent *event) +{ + View3D *v3d = CTX_wm_view3d(C); + Scene *scene = CTX_data_scene(C); + Object *obedit = CTX_data_edit_object(C); + BMEditMesh *em = BKE_editmesh_from_object(obedit); + BMesh *bm = em->bm; + float target[3]; + + bool handled = false; + PropertyRNA *prop_target = RNA_struct_find_property(op->ptr, "target_location"); + BMLoopNorEditDataArray *lnors_ed_arr = op->customdata; + + if (lnors_ed_arr->funcdata) { /* Executes and transfers control to point_normals_mouse. */ + int (*apply)(bContext *, wmOperator *, const wmEvent *); + apply = lnors_ed_arr->funcdata; + return apply(C, op, event); + } + + if (event->val == KM_PRESS) { + BMVert *v; + BMIter viter; + + if (event->type == LEFTMOUSE) { + ED_view3d_cursor3d_update(C, event->mval); + copy_v3_v3(target, ED_view3d_cursor3d_get(scene, v3d)); + RNA_property_float_set_array(op->ptr, prop_target, target); + + handled = true; + } + if (event->type == RIGHTMOUSE) { + view3d_operator_needs_opengl(C); + const bool retval = EDBM_select_pick(C, event->mval, false, false, false); + if (!retval) { + point_normals_free(C, op); + return OPERATOR_CANCELLED; + } + ED_object_editmode_calc_active_center(obedit, false, target); /* Point to newly selected active */ + add_v3_v3(target, obedit->loc); + RNA_property_float_set_array(op->ptr, prop_target, target); + handled = true; + } + else if (event->type == LKEY) { + switch (v3d->around) { + case V3D_AROUND_CENTER_BOUNDS: /* calculateCenterBound */ + { + float min[3], max[3]; + int i = 0; + BM_ITER_MESH(v, &viter, bm, BM_VERTS_OF_MESH) { + if (BM_elem_flag_test(v, BM_ELEM_SELECT)) { + if (i) { + minmax_v3v3_v3(min, max, v->co); + } + else { + copy_v3_v3(min, v->co); + copy_v3_v3(max, v->co); + } + i++; + } + } + mid_v3_v3v3(target, min, max); + add_v3_v3(target, obedit->loc); + RNA_property_float_set_array(op->ptr, prop_target, target); + break; + } + + case V3D_AROUND_CENTER_MEAN: + { + zero_v3(target); + int i = 0; + BM_ITER_MESH(v, &viter, bm, BM_VERTS_OF_MESH) { + if (BM_elem_flag_test(v, BM_ELEM_SELECT)) { + add_v3_v3(target, v->co); + add_v3_v3(target, obedit->loc); + i++; + } + } + mul_v3_fl(target, 1.0f / (float)i); + RNA_property_float_set_array(op->ptr, prop_target, target); + break; + } + + case V3D_AROUND_CURSOR: + copy_v3_v3(target, ED_view3d_cursor3d_get(scene, v3d)); + RNA_property_float_set_array(op->ptr, prop_target, target); + break; + + case V3D_AROUND_ACTIVE: + if (!ED_object_editmode_calc_active_center(obedit, false, target)) { + point_normals_free(C, op); + return OPERATOR_CANCELLED; + } + add_v3_v3(target, obedit->loc); + RNA_property_float_set_array(op->ptr, prop_target, target); + break; + + default: + BKE_report(op->reports, RPT_ERROR, "Does not support Individual Origin as pivot"); + point_normals_free(C, op); + return OPERATOR_CANCELLED; + } + handled = true; + } + else if (event->type == MKEY) { + lnors_ed_arr->funcdata = point_normals_mouse; + char header[UI_MAX_DRAW_STR]; + BLI_snprintf(header, sizeof(header), IFACE_("Left Click to Confirm, Right click to Cancel")); + + ED_area_headerprint(CTX_wm_area(C), header); + } + else if (event->type == OKEY) { + copy_v3_v3(target, obedit->loc); + RNA_property_float_set_array(op->ptr, prop_target, target); + handled = true; + } + else if (ISKEYBOARD(event->type) && event->type != RIGHTALTKEY) { + point_normals_free(C, op); + return OPERATOR_CANCELLED; + } + } + + if (handled || event->type == RIGHTMOUSE) { + RNA_boolean_set(op->ptr, "align", false); + } + + if (handled) { + point_normals_apply(C, op, event, target); + EDBM_update_generic(em, true, false); /* Recheck bools. */ + point_normals_free(C, op); + + return OPERATOR_FINISHED; + } + + return OPERATOR_PASS_THROUGH; +} + +static int edbm_point_normals_invoke(bContext *C, wmOperator *op, const wmEvent *event) +{ + if (!point_normals_init(C, op, event)) { + point_normals_free(C, op); + return OPERATOR_CANCELLED; + } + + WM_event_add_modal_handler(C, op); + + char header[UI_MAX_DRAW_STR]; + BLI_snprintf(header, sizeof(header), + IFACE_("L Key to use Pivot as target, M Key to point to mouse, O Key to point to object origin, " + "Left Click to point to new cursor location, Right Click on mesh to point to mesh")); + + ED_area_headerprint(CTX_wm_area(C), header); + + op->flag |= OP_IS_MODAL_GRAB_CURSOR; + return OPERATOR_RUNNING_MODAL; +} + +static int edbm_point_normals_exec(bContext *C, wmOperator *op) +{ + Object *obedit = CTX_data_edit_object(C); + BMEditMesh *em = BKE_editmesh_from_object(obedit); + + if (!point_normals_init(C, op, NULL)) { + point_normals_free(C, op); + return OPERATOR_CANCELLED; + } + + float target[3]; + RNA_float_get_array(op->ptr, "target_location", target); + + point_normals_apply(C, op, NULL, target); + + EDBM_update_generic(em, true, false); + point_normals_free(C, op); + + return OPERATOR_FINISHED; +} + +static bool point_normals_draw_check_prop(PointerRNA *ptr, PropertyRNA *prop) +{ + const char *prop_id = RNA_property_identifier(prop); + const bool spherize = RNA_boolean_get(ptr, "spherize"); + + /* Only show strength option if spherize is enabled. */ + if (STREQ(prop_id, "strength")) { + return spherize; + } + + /* Else, show it! */ + return true; +} + +static void edbm_point_normals_ui(bContext *C, wmOperator *op) +{ + uiLayout *layout = op->layout; + wmWindowManager *wm = CTX_wm_manager(C); + PointerRNA ptr; + + RNA_pointer_create(&wm->id, op->type->srna, op->properties, &ptr); + + /* Main auto-draw call */ + uiDefAutoButsRNA(layout, &ptr, point_normals_draw_check_prop, '\0'); +} + +void MESH_OT_point_normals(struct wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Point normals to Target"; + ot->description = "Point selected normals to specified Target"; + ot->idname = "MESH_OT_point_normals"; + + /* api callbacks */ + ot->exec = edbm_point_normals_exec; + ot->invoke = edbm_point_normals_invoke; + ot->modal = edbm_point_normals_modal; + ot->poll = ED_operator_editmesh_auto_smooth; + ot->ui = edbm_point_normals_ui; + + /* flags */ + ot->flag = OPTYPE_BLOCKING | OPTYPE_REGISTER | OPTYPE_UNDO; + + RNA_def_boolean(ot->srna, "point_away", false, "Point Away", "Point Away from target"); + + RNA_def_boolean(ot->srna, "align", false, "Align", "Align normal with mouse location"); + + RNA_def_float_vector(ot->srna, "target_location", 3, (float[3]){0.0f, 0.0f, 0.0f}, -FLT_MAX, FLT_MAX, + "Target", "Target location to which normals will point", -1000.0f, 1000.0f); + + RNA_def_boolean(ot->srna, "spherize", false, + "Spherize Normal", "Add normal vector of target to custom normal with given proportion"); + + RNA_def_float(ot->srna, "strength", 0.1, 0.0f, 1.0f, + "Strength", "Ratio of spherized normal to original normal", 0.0f, 1.0f); +} + +/********************** Split/Merge Loop Normals **********************/ + +static void custom_loops_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); +} + +static void loop_normal_merge(bContext *C, BMLoopNorEditDataArray *lnors_ed_arr) +{ + Object *obedit = CTX_data_edit_object(C); + BMEditMesh *em = BKE_editmesh_from_object(obedit); + BMesh *bm = em->bm; + + BMLoopNorEditData *lnor_ed = lnors_ed_arr->lnor_editdata; + + BLI_SMALLSTACK_DECLARE(clnors, short *); + + BLI_assert(bm->lnor_spacearr->data_type == MLNOR_SPACEARR_BMLOOP_PTR); + + custom_loops_tag(bm, false); + + for (int i = 0; i < lnors_ed_arr->totloop; i++, lnor_ed++) { + if (BM_elem_flag_test(lnor_ed->loop, BM_ELEM_TAG)) { + continue; + } + + MLoopNorSpace *lnor_space = bm->lnor_spacearr->lspacearr[lnor_ed->loop_index]; + + if ((lnor_space->flags & MLNOR_SPACE_IS_SINGLE) == 0) { + LinkNode *loops = lnor_space->loops; + float avg_normal[3] = {0.0f, 0.0f, 0.0f}; + short *clnors_data; + + for (; loops; loops = loops->next) { + BMLoop *l = loops->link; + const int loop_index = BM_elem_index_get(l); + + BMLoopNorEditData *lnor_ed_tmp = lnors_ed_arr->lidx_to_lnor_editdata[loop_index]; + BLI_assert(lnor_ed_tmp->loop_index == loop_index && lnor_ed_tmp->loop == l); + add_v3_v3(avg_normal, lnor_ed_tmp->nloc); + BLI_SMALLSTACK_PUSH(clnors, lnor_ed_tmp->clnors_data); + BM_elem_flag_enable(l, BM_ELEM_TAG); + } + if (len_squared_v3(avg_normal) < 1e-4f) { /* If avg normal is nearly 0, set clnor to default value. */ + while ((clnors_data = BLI_SMALLSTACK_POP(clnors))) { + copy_v2_v2_short(clnors_data, (short[2]){0, 0}); + } + } + else { + normalize_v3(avg_normal); /* Else set all clnors to this avg. */ + while ((clnors_data = BLI_SMALLSTACK_POP(clnors))) { + BKE_lnor_space_custom_normal_to_data(lnor_space, avg_normal, clnors_data); + } + } + } + } +} + +static void loop_normla_split(bContext *C) +{ + Object *obedit = CTX_data_edit_object(C); + BMEditMesh *em = BKE_editmesh_from_object(obedit); + BMesh *bm = em->bm; + BMFace *f; + BMLoop *l, *l_curr, *l_first; + BMIter fiter; + + BLI_assert(bm->lnor_spacearr->data_type == MLNOR_SPACEARR_BMLOOP_PTR); + + custom_loops_tag(bm, true); + + const int cd_clnors_offset = CustomData_get_offset(&bm->ldata, CD_CUSTOMLOOPNORMAL); + BM_ITER_MESH(f, &fiter, bm, BM_FACES_OF_MESH) { + l_curr = l_first = BM_FACE_FIRST_LOOP(f); + do { + if (BM_elem_flag_test(l_curr->v, BM_ELEM_SELECT) && (!BM_elem_flag_test(l_curr->e, BM_ELEM_TAG) || + (!BM_elem_flag_test(l_curr, BM_ELEM_TAG) && BM_loop_check_cyclic_smooth_fan(l_curr)))) + { + if (!BM_elem_flag_test(l_curr->e, BM_ELEM_TAG) && !BM_elem_flag_test(l_curr->prev->e, BM_ELEM_TAG)) { + const int loop_index = BM_elem_index_get(l_curr); + short *clnors = BM_ELEM_CD_GET_VOID_P(l_curr, cd_clnors_offset); + BKE_lnor_space_custom_normal_to_data(bm->lnor_spacearr->lspacearr[loop_index], f->no, clnors); + } + else { + BMVert *v_pivot = l_curr->v; + BMEdge *e_next; + const BMEdge *e_org = l_curr->e; + BMLoop *lfan_pivot, *lfan_pivot_next; + + lfan_pivot = l_curr; + e_next = lfan_pivot->e; + BLI_SMALLSTACK_DECLARE(loops, BMLoop *); + float avg_normal[3] = { 0.0f }; + + 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); + add_v3_v3(avg_normal, lfan_pivot->f->no); + + if (!BM_elem_flag_test(e_next, BM_ELEM_TAG) || (e_next == e_org)) { + break; + } + lfan_pivot = lfan_pivot_next; + } + normalize_v3(avg_normal); + 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); + BKE_lnor_space_custom_normal_to_data(bm->lnor_spacearr->lspacearr[l_index], avg_normal, clnors); + } + } + } + } while ((l_curr = l_curr->next) != l_first); + } +} + +static int loop_normals_split_merge(bContext *C, const bool do_merge) +{ + Object *obedit = CTX_data_edit_object(C); + BMEditMesh *em = BKE_editmesh_from_object(obedit); + BMesh *bm = em->bm; + BMEdge *e; + BMIter eiter; + + BKE_editmesh_lnorspace_update(em); + + BMLoopNorEditDataArray *lnors_ed_arr = do_merge ? BM_loop_normal_editdata_array_init(bm) : NULL; + + mesh_set_smooth_faces(em, do_merge); + + BM_ITER_MESH(e, &eiter, bm, BM_EDGES_OF_MESH) { + if (BM_elem_flag_test(e, BM_ELEM_SELECT)) { + BM_elem_flag_set(e, BM_ELEM_SMOOTH, do_merge); + } + } + if (do_merge == 0) { + Mesh *me = obedit->data; + me->drawflag |= ME_DRAWSHARP; + } + + bm->spacearr_dirty |= BM_SPACEARR_DIRTY_ALL; + BKE_editmesh_lnorspace_update(em); + + if (do_merge) { + loop_normal_merge(C, lnors_ed_arr); + } + else { + loop_normla_split(C); + } + + if (lnors_ed_arr) { + BM_loop_normal_editdata_array_free(lnors_ed_arr); + } + + EDBM_update_generic(em, true, false); + + return OPERATOR_FINISHED; +} + +static int edbm_merge_loop_normals_exec(bContext *C, wmOperator *UNUSED(op)) +{ + return loop_normals_split_merge(C, true); +} + +void MESH_OT_merge_loop_normals(struct wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Merge Loop Normals"; + ot->description = "Merge loop normals of selected vertices"; + ot->idname = "MESH_OT_merge_loop_normals"; + + /* api callbacks */ + ot->exec = edbm_merge_loop_normals_exec; + ot->poll = ED_operator_editmesh_auto_smooth; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + +static int edbm_split_loop_normals_exec(bContext *C, wmOperator *UNUSED(op)) +{ + return loop_normals_split_merge(C, false); +} + +void MESH_OT_split_loop_normals(struct wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Split Loop Normals"; + ot->description = "Split loop normals of selected vertices"; + ot->idname = "MESH_OT_split_loop_normals"; + + /* api callbacks */ + ot->exec = edbm_split_loop_normals_exec; + ot->poll = ED_operator_editmesh_auto_smooth; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + +/********************** Average Loop Normals **********************/ + +enum { + EDBM_CLNOR_AVERAGE_LOOP = 1, + EDBM_CLNOR_AVERAGE_FACE_AREA = 2, + EDBM_CLNOR_AVERAGE_ANGLE = 3, +}; + +static EnumPropertyItem average_method_items[] = { + {EDBM_CLNOR_AVERAGE_LOOP, "CUSTOM_NORMAL", 0, "Custom Normal", "Take Average of vert Normals"}, + {EDBM_CLNOR_AVERAGE_FACE_AREA, "FACE_AREA", 0, "Face Area", "Set all vert normals by Face Area"}, + {EDBM_CLNOR_AVERAGE_ANGLE, "CORNER_ANGLE", 0, "Corner Angle", "Set all vert normals by Corner Angle"}, + {0, NULL, 0, NULL, NULL} +}; + +static int edbm_average_loop_normals_exec(bContext *C, wmOperator *op) +{ + Object *obedit = CTX_data_edit_object(C); + BMEditMesh *em = BKE_editmesh_from_object(obedit); + BMesh *bm = em->bm; + BMFace *f; + BMLoop *l, *l_curr, *l_first; + BMIter fiter; + + bm->spacearr_dirty |= BM_SPACEARR_DIRTY_ALL; + BKE_editmesh_lnorspace_update(em); + + const int average_type = RNA_enum_get(op->ptr, "average_type"); + const int cd_clnors_offset = CustomData_get_offset(&bm->ldata, CD_CUSTOMLOOPNORMAL); + const float absweight = (float) RNA_int_get(op->ptr, "weight"); + const float threshold = RNA_float_get(op->ptr, "threshold"); + + float weight = absweight / 50.0f; + if (absweight == 100.0f) { + weight = (float)SHRT_MAX; + } + else if (absweight == 1.0f) { + weight = 1 / (float)SHRT_MAX; + } + else if ((weight - 1) * 25 > 1) { + weight = (weight - 1) * 25; + } + + custom_loops_tag(bm, true); + + Heap *loop_weight = BLI_heap_new(); + + BM_ITER_MESH(f, &fiter, bm, BM_FACES_OF_MESH) { + l_curr = l_first = BM_FACE_FIRST_LOOP(f); + do { + if (BM_elem_flag_test(l_curr->v, BM_ELEM_SELECT) && (!BM_elem_flag_test(l_curr->e, BM_ELEM_TAG) || + (!BM_elem_flag_test(l_curr, BM_ELEM_TAG) && BM_loop_check_cyclic_smooth_fan(l_curr)))) + { + if (!BM_elem_flag_test(l_curr->e, BM_ELEM_TAG) && !BM_elem_flag_test(l_curr->prev->e, BM_ELEM_TAG)) { + const int loop_index = BM_elem_index_get(l_curr); + short *clnors = BM_ELEM_CD_GET_VOID_P(l_curr, cd_clnors_offset); + BKE_lnor_space_custom_normal_to_data(bm->lnor_spacearr->lspacearr[loop_index], f->no, clnors); + } + else { + BMVert *v_pivot = l_curr->v; + BMEdge *e_next; + const BMEdge *e_org = l_curr->e; + BMLoop *lfan_pivot, *lfan_pivot_next; + + lfan_pivot = l_curr; + e_next = lfan_pivot->e; + + 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; + } + + float val = 1.0f; + if (average_type == EDBM_CLNOR_AVERAGE_FACE_AREA) { + val = 1.0f / BM_face_calc_area(lfan_pivot->f); + } + else if (average_type == EDBM_CLNOR_AVERAGE_ANGLE) { + val = 1.0f / BM_loop_calc_face_angle(lfan_pivot); + } + + BLI_heap_insert(loop_weight, val, lfan_pivot); + + if (!BM_elem_flag_test(e_next, BM_ELEM_TAG) || (e_next == e_org)) { + break; + } + lfan_pivot = lfan_pivot_next; + } + + BLI_SMALLSTACK_DECLARE(loops, BMLoop *); + float wnor[3], avg_normal[3] = { 0.0f }, count = 0; + float val = BLI_heap_node_value(BLI_heap_top(loop_weight)); + + while (!BLI_heap_is_empty(loop_weight)) { + const float cur_val = BLI_heap_node_value(BLI_heap_top(loop_weight)); + if (!compare_ff(val, cur_val, threshold)) { + count++; + val = cur_val; + } + l = BLI_heap_pop_min(loop_weight); + BLI_SMALLSTACK_PUSH(loops, l); + + const float n_weight = pow(weight, count); + + if (average_type == EDBM_CLNOR_AVERAGE_LOOP) { + const int l_index = BM_elem_index_get(l); + short *clnors = BM_ELEM_CD_GET_VOID_P(l, cd_clnors_offset); + BKE_lnor_space_custom_data_to_normal(bm->lnor_spacearr->lspacearr[l_index], clnors, wnor); + } + else { + copy_v3_v3(wnor, l->f->no); + } + mul_v3_fl(wnor, (1.0f / cur_val) * (1.0f / n_weight)); + add_v3_v3(avg_normal, wnor); + } + + normalize_v3(avg_normal); + + 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); + BKE_lnor_space_custom_normal_to_data(bm->lnor_spacearr->lspacearr[l_index], avg_normal, clnors); + } + } + } + } while ((l_curr = l_curr->next) != l_first); + } + + BLI_heap_free(loop_weight, NULL); + EDBM_update_generic(em, true, false); + + return OPERATOR_FINISHED; +} + +static bool average_loop_normals_draw_check_prop(PointerRNA *ptr, PropertyRNA *prop) +{ + const char *prop_id = RNA_property_identifier(prop); + const int average_type = RNA_enum_get(ptr, "average_type"); + + /* Only show weight/threshold options in loop average type. */ + if (STREQ(prop_id, "weight")) { + return (average_type == EDBM_CLNOR_AVERAGE_LOOP); + } + else if (STREQ(prop_id, "threshold")) { + return (average_type == EDBM_CLNOR_AVERAGE_LOOP); + } + + /* Else, show it! */ + return true; +} + +static void edbm_average_loop_normals_ui(bContext *C, wmOperator *op) +{ + uiLayout *layout = op->layout; + wmWindowManager *wm = CTX_wm_manager(C); + PointerRNA ptr; + + RNA_pointer_create(&wm->id, op->type->srna, op->properties, &ptr); + + /* Main auto-draw call */ + uiDefAutoButsRNA(layout, &ptr, average_loop_normals_draw_check_prop, '\0'); +} + +void MESH_OT_average_loop_normals(struct wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Average"; + ot->description = "Average loop normals of selected vertices"; + ot->idname = "MESH_OT_average_loop_normals"; + + /* api callbacks */ + ot->exec = edbm_average_loop_normals_exec; + ot->poll = ED_operator_editmesh_auto_smooth; + ot->ui = edbm_average_loop_normals_ui; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + ot->prop = RNA_def_enum(ot->srna, "average_type", average_method_items, EDBM_CLNOR_AVERAGE_LOOP, "Type", "Averaging method"); + RNA_def_property_flag(ot->prop, PROP_HIDDEN); + + RNA_def_int(ot->srna, "weight", 50, 1, 100, "Weight", "Weight applied per face", 1, 100); + + RNA_def_float(ot->srna, "threshold", 0.01f, 0, 10, "Threshold", "Threshold value for different weights to be considered equal", 0, 5); +} + +/********************** Custom Normal Interface Tools **********************/ + +enum { + EDBM_CLNOR_TOOLS_COPY = 1, + EDBM_CLNOR_TOOLS_PASTE = 2, + EDBM_CLNOR_TOOLS_MULTIPLY = 3, + EDBM_CLNOR_TOOLS_ADD = 4, + EDBM_CLNOR_TOOLS_RESET = 5, +}; + +static EnumPropertyItem normal_vector_tool_items[] = { + {EDBM_CLNOR_TOOLS_COPY, "COPY", 0, "Copy Normal", "Copy normal to buffer"}, + {EDBM_CLNOR_TOOLS_PASTE, "PASTE", 0, "Paste Normal", "Paste normal from buffer"}, + {EDBM_CLNOR_TOOLS_ADD, "ADD", 0, "Add Normal", "Add normal vector with selection"}, + {EDBM_CLNOR_TOOLS_MULTIPLY, "MULTIPLY", 0, "Multiply Normal", "Multiply normal vector with selection"}, + {EDBM_CLNOR_TOOLS_RESET, "RESET", 0, "Reset Normal", "Reset buffer and/or normal of selected element"}, + {0, NULL, 0, NULL, NULL} +}; + +static int edbm_custom_normal_tools_exec(bContext *C, wmOperator *op) +{ + Object *obedit = CTX_data_edit_object(C); + Scene *scene = CTX_data_scene(C); + BMEditMesh *em = BKE_editmesh_from_object(obedit); + BMesh *bm = em->bm; + + const int mode = RNA_enum_get(op->ptr, "mode"); + const bool absolute = RNA_boolean_get(op->ptr, "absolute"); + + BKE_editmesh_lnorspace_update(em); + BMLoopNorEditDataArray *lnors_ed_arr = BM_loop_normal_editdata_array_init(bm); + BMLoopNorEditData *lnor_ed = lnors_ed_arr->lnor_editdata; + + float *normal_vector = scene->toolsettings->normal_vector; + + switch (mode) { + case EDBM_CLNOR_TOOLS_COPY: + if (bm->totfacesel != 1 && lnors_ed_arr->totloop != 1 && bm->totvertsel != 1) { + BKE_report(op->reports, RPT_ERROR, "Can only copy Split normal, Averaged vertex normal or Face normal"); + BM_loop_normal_editdata_array_free(lnors_ed_arr); + return OPERATOR_CANCELLED; + } + bool join = true; + for (int i = 0; i < lnors_ed_arr->totloop; i++, lnor_ed++) { + if (!compare_v3v3(lnors_ed_arr->lnor_editdata->nloc, lnor_ed->nloc, 1e-4f)) { + join = false; + } + } + if (lnors_ed_arr->totloop == 1) { + copy_v3_v3(scene->toolsettings->normal_vector, lnors_ed_arr->lnor_editdata->nloc); + } + else if (bm->totfacesel == 1) { + BMFace *f; + BMIter fiter; + BM_ITER_MESH(f, &fiter, bm, BM_FACES_OF_MESH) { + if (BM_elem_flag_test(f, BM_ELEM_SELECT)) { + copy_v3_v3(scene->toolsettings->normal_vector, f->no); + } + } + } + else if (join) { + copy_v3_v3(scene->toolsettings->normal_vector, lnors_ed_arr->lnor_editdata->nloc); + } + break; + + case EDBM_CLNOR_TOOLS_PASTE: + if (!absolute) { + normalize_v3(normal_vector); + } + for (int i = 0; i < lnors_ed_arr->totloop; i++, lnor_ed++) { + if (absolute) { + float abs_normal[3]; + copy_v3_v3(abs_normal, lnor_ed->loc); + negate_v3(abs_normal); + add_v3_v3(abs_normal, normal_vector); + normalize_v3(abs_normal); + + BKE_lnor_space_custom_normal_to_data(bm->lnor_spacearr->lspacearr[lnor_ed->loop_index], abs_normal, lnor_ed->clnors_data); + } + else { + BKE_lnor_space_custom_normal_to_data(bm->lnor_spacearr->lspacearr[lnor_ed->loop_index], normal_vector, lnor_ed->clnors_data); + } + } + break; + + case EDBM_CLNOR_TOOLS_MULTIPLY: + for (int i = 0; i < lnors_ed_arr->totloop; i++, lnor_ed++) { + mul_v3_v3(lnor_ed->nloc, normal_vector); + normalize_v3(lnor_ed->nloc); + BKE_lnor_space_custom_normal_to_data(bm->lnor_spacearr->lspacearr[lnor_ed->loop_index], lnor_ed->nloc, lnor_ed->clnors_data); + } + break; + + case EDBM_CLNOR_TOOLS_ADD: + for (int i = 0; i < lnors_ed_arr->totloop; i++, lnor_ed++) { + add_v3_v3(lnor_ed->nloc, normal_vector); + normalize_v3(lnor_ed->nloc); + BKE_lnor_space_custom_normal_to_data(bm->lnor_spacearr->lspacearr[lnor_ed->loop_index], lnor_ed->nloc, lnor_ed->clnors_data); + } + break; + + case EDBM_CLNOR_TOOLS_RESET: + zero_v3(normal_vector); + for (int i = 0; i < lnors_ed_arr->totloop; i++, lnor_ed++) { + zero_v3(lnor_ed->nloc); + BKE_lnor_space_custom_normal_to_data(bm->lnor_spacearr->lspacearr[lnor_ed->loop_index], lnor_ed->nloc, lnor_ed->clnors_data); + } + break; + + default: + BLI_assert(0); + break; + } + + BM_loop_normal_editdata_array_free(lnors_ed_arr); + + EDBM_update_generic(em, true, false); + return OPERATOR_FINISHED; +} + +static bool custom_normal_tools_draw_check_prop(PointerRNA *ptr, PropertyRNA *prop) +{ + const char *prop_id = RNA_property_identifier(prop); + const int mode = RNA_enum_get(ptr, "mode"); + + /* Only show absolute option in paste mode. */ + if (STREQ(prop_id, "absolute")) { + return (mode == EDBM_CLNOR_TOOLS_PASTE); + } + + /* Else, show it! */ + return true; +} + +static void edbm_custom_normal_tools_ui(bContext *C, wmOperator *op) +{ + uiLayout *layout = op->layout; + wmWindowManager *wm = CTX_wm_manager(C); + PointerRNA ptr; + + RNA_pointer_create(&wm->id, op->type->srna, op->properties, &ptr); + + /* Main auto-draw call */ + uiDefAutoButsRNA(layout, &ptr, custom_normal_tools_draw_check_prop, '\0'); +} + +void MESH_OT_custom_normal_tools(struct wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Custom Normal Vector tools"; + ot->description = "Normal tools using Normal Vector of Interface"; + ot->idname = "MESH_OT_custom_normal_tools"; + + /* api callbacks */ + ot->exec = edbm_custom_normal_tools_exec; + ot->poll = ED_operator_editmesh_auto_smooth; + ot->ui = edbm_custom_normal_tools_ui; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + ot->prop = RNA_def_enum(ot->srna, "mode", normal_vector_tool_items, EDBM_CLNOR_TOOLS_COPY, + "Mode", "Mode of tools taking input from Interface"); + RNA_def_property_flag(ot->prop, PROP_HIDDEN); + + RNA_def_boolean(ot->srna, "absolute", false, "Absolute Coordinates", "Copy Absolute coordinates or Normal vector"); +} + +static int edbm_set_normals_from_faces_exec(bContext *C, wmOperator *op) +{ + Object *obedit = CTX_data_edit_object(C); + BMEditMesh *em = BKE_editmesh_from_object(obedit); + BMesh *bm = em->bm; + BMFace *f; + BMVert *v; + BMEdge *e; + BMLoop *l; + BMIter fiter, viter, eiter, liter; + + const bool keep_sharp = RNA_boolean_get(op->ptr, "keep_sharp"); + + BKE_editmesh_lnorspace_update(em); + + float(*vnors)[3] = MEM_callocN(sizeof(*vnors) * bm->totvert, __func__); + BM_ITER_MESH(f, &fiter, bm, BM_FACES_OF_MESH) { + if (BM_elem_flag_test(f, BM_ELEM_SELECT)) { + BM_ITER_ELEM(v, &viter, f, BM_VERTS_OF_FACE) { + const int v_index = BM_elem_index_get(v); + add_v3_v3(vnors[v_index], f->no); + } + } + } + for (int i = 0; i < bm->totvert; i++) { + if (!is_zero_v3(vnors[i])) { + normalize_v3(vnors[i]); + } + } + + BLI_bitmap *loop_set = BLI_BITMAP_NEW(bm->totloop, __func__); + const int cd_clnors_offset = CustomData_get_offset(&bm->ldata, CD_CUSTOMLOOPNORMAL); + + BM_ITER_MESH(f, &fiter, bm, BM_FACES_OF_MESH) { + BM_ITER_ELEM(e, &eiter, f, BM_EDGES_OF_FACE) { + if (!keep_sharp || (BM_elem_flag_test(e, BM_ELEM_SMOOTH) && BM_elem_flag_test(e, BM_ELEM_SELECT))) { + BM_ITER_ELEM(v, &viter, e, BM_VERTS_OF_EDGE) { + l = BM_face_vert_share_loop(f, v); + const int loop_index = BM_elem_index_get(l); + const int v_index = BM_elem_index_get(l->v); + BLI_assert(l->f == f); + BLI_assert(l->v == v); + if (!is_zero_v3(vnors[v_index])) { + short *clnors = BM_ELEM_CD_GET_VOID_P(l, cd_clnors_offset); + BKE_lnor_space_custom_normal_to_data(bm->lnor_spacearr->lspacearr[loop_index], vnors[v_index], clnors); + + if (bm->lnor_spacearr->lspacearr[loop_index]->flags & MLNOR_SPACE_IS_SINGLE) { + BLI_BITMAP_ENABLE(loop_set, loop_index); + } + else { + LinkNode *loops = bm->lnor_spacearr->lspacearr[loop_index]->loops; + for (; loops; loops = loops->next) { + BLI_BITMAP_ENABLE(loop_set, BM_elem_index_get((BMLoop *)loops->link)); + } + } + } + } + } + } + } + + int v_index; + BM_ITER_MESH_INDEX(v, &viter, bm, BM_VERTS_OF_MESH, v_index) { + BM_ITER_ELEM(l, &liter, v, BM_LOOPS_OF_VERT) { + if (BLI_BITMAP_TEST(loop_set, BM_elem_index_get(l))) { + const int loop_index = BM_elem_index_get(l); + short *clnors = BM_ELEM_CD_GET_VOID_P(l, cd_clnors_offset); + BKE_lnor_space_custom_normal_to_data(bm->lnor_spacearr->lspacearr[loop_index], vnors[v_index], clnors); + } + } + } + + MEM_freeN(loop_set); + MEM_freeN(vnors); + EDBM_update_generic(em, true, false); + + return OPERATOR_FINISHED; +} + +void MESH_OT_set_normals_from_faces(struct wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Set Normals from faces"; + ot->description = "Set the custom vertex normals from the selected faces ones"; + ot->idname = "MESH_OT_set_normals_from_faces"; + + /* api callbacks */ + ot->exec = edbm_set_normals_from_faces_exec; + ot->poll = ED_operator_editmesh_auto_smooth; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + RNA_def_boolean(ot->srna, "keep_sharp", 0, "Keep Sharp Edges", "Do not set sharp edges to face"); +} + +static int edbm_smoothen_normals_exec(bContext *C, wmOperator *op) +{ + Object *obedit = CTX_data_edit_object(C); + BMEditMesh *em = BKE_editmesh_from_object(obedit); + BMesh *bm = em->bm; + BMFace *f; + BMLoop *l; + BMIter fiter, liter; + + BKE_editmesh_lnorspace_update(em); + BMLoopNorEditDataArray *lnors_ed_arr = BM_loop_normal_editdata_array_init(bm); + + float(*smooth_normal)[3] = MEM_callocN(sizeof(*smooth_normal) * lnors_ed_arr->totloop, __func__); + + BMLoopNorEditData *lnor_ed = lnors_ed_arr->lnor_editdata; + for (int i = 0; i < lnors_ed_arr->totloop; i++, lnor_ed++) { + l = lnor_ed->loop; + float loop_normal[3]; + + BM_ITER_ELEM(f, &fiter, l->v, BM_FACES_OF_VERT) { + BMLoop *l_other; + BM_ITER_ELEM(l_other, &liter, f, BM_LOOPS_OF_FACE) { + const int index_other = BM_elem_index_get(l_other); + short *clnors = BM_ELEM_CD_GET_VOID_P(l_other, lnors_ed_arr->cd_custom_normal_offset); + BKE_lnor_space_custom_data_to_normal(bm->lnor_spacearr->lspacearr[index_other], clnors, loop_normal); + add_v3_v3(smooth_normal[i], loop_normal); + } + } + normalize_v3(smooth_normal[i]); + } + + const float factor = RNA_float_get(op->ptr, "factor"); + + lnor_ed = lnors_ed_arr->lnor_editdata; + for (int i = 0; i < lnors_ed_arr->totloop; i++, lnor_ed++) { + float current_normal[3]; + + BKE_lnor_space_custom_data_to_normal(bm->lnor_spacearr->lspacearr[lnor_ed->loop_index], lnor_ed->clnors_data, current_normal); + + mul_v3_fl(current_normal, 1.0f - factor); + mul_v3_fl(smooth_normal[i], factor); + add_v3_v3(current_normal, smooth_normal[i]); + normalize_v3(current_normal); + + BKE_lnor_space_custom_normal_to_data(bm->lnor_spacearr->lspacearr[lnor_ed->loop_index], current_normal, lnor_ed->clnors_data); + } + + BM_loop_normal_editdata_array_free(lnors_ed_arr); + MEM_freeN(smooth_normal); + + EDBM_update_generic(em, true, false); + + return OPERATOR_FINISHED; +} + +void MESH_OT_smoothen_custom_normals(struct wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Smoothen Custom Normal"; + ot->description = "Smoothen custom normal based on adjacent vertex normals"; + ot->idname = "MESH_OT_smoothen_custom_normals"; + + /* api callbacks */ + ot->exec = edbm_smoothen_normals_exec; + ot->poll = ED_operator_editmesh_auto_smooth; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + RNA_def_float(ot->srna, "factor", 0.5f, 0.0f, 1.0f, "Factor", + "Specifies weight of smooth vs original normal", 0.0f, 1.0f); +} + +/********************** Weighted Normal Modifier Face Strength **********************/ + +static int edbm_mod_weighted_strength_exec(bContext *C, wmOperator *op) +{ + Scene *scene = CTX_data_scene(C); + Object *obedit = CTX_data_edit_object(C); + BMEditMesh *em = BKE_editmesh_from_object(obedit); + BMesh *bm = em->bm; + BMFace *f; + BMIter fiter; + + BM_select_history_clear(bm); + + int cd_prop_int_offset = CustomData_get_offset(&bm->pdata, CD_PROP_INT); + if (cd_prop_int_offset == -1) { + BM_data_layer_add(bm, &bm->pdata, CD_PROP_INT); + cd_prop_int_offset = CustomData_get_offset(&bm->pdata, CD_PROP_INT); + } + + const int face_strength = scene->toolsettings->face_strength; + const bool set = RNA_boolean_get(op->ptr, "set"); + BM_mesh_elem_index_ensure(bm, BM_FACE); + + if (set) { + BM_ITER_MESH(f, &fiter, bm, BM_FACES_OF_MESH) { + if (BM_elem_flag_test(f, BM_ELEM_SELECT)) { + int *strength = BM_ELEM_CD_GET_VOID_P(f, cd_prop_int_offset); + *strength = face_strength; + } + } + } + else { + BM_ITER_MESH(f, &fiter, bm, BM_FACES_OF_MESH) { + int *strength = BM_ELEM_CD_GET_VOID_P(f, cd_prop_int_offset); + if (*strength == face_strength) { + BM_face_select_set(bm, f, true); + BM_select_history_store(bm, f); + } + else { + BM_face_select_set(bm, f, false); + } + } + } + + EDBM_update_generic(em, false, false); + return OPERATOR_FINISHED; +} + +void MESH_OT_mod_weighted_strength(struct wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Face Strength"; + ot->description = "Set/Get strength of face (used in Weighted Normal modifier)"; + ot->idname = "MESH_OT_mod_weighted_strength"; + + /* api callbacks */ + ot->exec = edbm_mod_weighted_strength_exec; + ot->poll = ED_operator_editmesh_auto_smooth; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + ot->prop = RNA_def_boolean(ot->srna, "set", 0, "Set value", "Set Value of faces"); + RNA_def_property_flag(ot->prop, PROP_HIDDEN); +} diff --git a/source/blender/editors/mesh/editmesh_undo.c b/source/blender/editors/mesh/editmesh_undo.c index 11667ed5710..ed9b26ba5b1 100644 --- a/source/blender/editors/mesh/editmesh_undo.c +++ b/source/blender/editors/mesh/editmesh_undo.c @@ -583,6 +583,8 @@ static void undoMesh_to_editbtMesh(void *um_v, void *em_v, void *obdata) bm->selectmode = um->selectmode; em->ob = ob; + bm->spacearr_dirty = BM_SPACEARR_DIRTY_ALL; + /* T35170: Restore the active key on the RealMesh. Otherwise 'fake' offset propagation happens * if the active is a basis for any other. */ if (key && (key->type == KEY_RELATIVE)) { diff --git a/source/blender/editors/mesh/editmesh_utils.c b/source/blender/editors/mesh/editmesh_utils.c index c4440fa190a..1c6d75bb928 100644 --- a/source/blender/editors/mesh/editmesh_utils.c +++ b/source/blender/editors/mesh/editmesh_utils.c @@ -1285,7 +1285,10 @@ void EDBM_update_generic(BMEditMesh *em, const bool do_tessface, const bool is_d /* in debug mode double check we didn't need to recalculate */ BLI_assert(BM_mesh_elem_table_check(em->bm) == true); } - + if (em->bm->spacearr_dirty & BM_SPACEARR_BMO_SET) { + BM_lnorspace_invalidate(em->bm, false); + em->bm->spacearr_dirty &= ~BM_SPACEARR_BMO_SET; + } /* don't keep stale derivedMesh data around, see: [#38872] */ BKE_editmesh_free_derivedmesh(em); diff --git a/source/blender/editors/mesh/mesh_intern.h b/source/blender/editors/mesh/mesh_intern.h index 300b21a052d..a5043744f13 100644 --- a/source/blender/editors/mesh/mesh_intern.h +++ b/source/blender/editors/mesh/mesh_intern.h @@ -229,6 +229,14 @@ void MESH_OT_duplicate(struct wmOperatorType *ot); void MESH_OT_merge(struct wmOperatorType *ot); void MESH_OT_remove_doubles(struct wmOperatorType *ot); void MESH_OT_poke(struct wmOperatorType *ot); +void MESH_OT_point_normals(struct wmOperatorType *ot); +void MESH_OT_merge_loop_normals(struct wmOperatorType *ot); +void MESH_OT_split_loop_normals(struct wmOperatorType *ot); +void MESH_OT_custom_normal_tools(struct wmOperatorType *ot); +void MESH_OT_set_normals_from_faces(struct wmOperatorType *ot); +void MESH_OT_average_loop_normals(struct wmOperatorType *ot); +void MESH_OT_smoothen_custom_normals(struct wmOperatorType *ot); +void MESH_OT_mod_weighted_strength(struct wmOperatorType *ot); #ifdef WITH_FREESTYLE void MESH_OT_mark_freestyle_edge(struct wmOperatorType *ot); diff --git a/source/blender/editors/mesh/mesh_ops.c b/source/blender/editors/mesh/mesh_ops.c index 697a92f36d1..6843a761133 100644 --- a/source/blender/editors/mesh/mesh_ops.c +++ b/source/blender/editors/mesh/mesh_ops.c @@ -192,6 +192,15 @@ void ED_operatortypes_mesh(void) WM_operatortype_append(MESH_OT_symmetrize); WM_operatortype_append(MESH_OT_symmetry_snap); + WM_operatortype_append(MESH_OT_point_normals); + WM_operatortype_append(MESH_OT_merge_loop_normals); + WM_operatortype_append(MESH_OT_split_loop_normals); + WM_operatortype_append(MESH_OT_custom_normal_tools); + WM_operatortype_append(MESH_OT_set_normals_from_faces); + WM_operatortype_append(MESH_OT_average_loop_normals); + WM_operatortype_append(MESH_OT_smoothen_custom_normals); + WM_operatortype_append(MESH_OT_mod_weighted_strength); + #ifdef WITH_GAMEENGINE WM_operatortype_append(MESH_OT_navmesh_make); WM_operatortype_append(MESH_OT_navmesh_face_copy); @@ -423,6 +432,8 @@ void ED_keymap_mesh(wmKeyConfig *keyconf) WM_keymap_add_item(keymap, "MESH_OT_split", YKEY, KM_PRESS, 0, 0); WM_keymap_add_item(keymap, "MESH_OT_vert_connect_path", JKEY, KM_PRESS, 0, 0); + WM_keymap_add_item(keymap, "MESH_OT_point_normals", LKEY, KM_PRESS, KM_ALT, 0); + /* Vertex Slide */ WM_keymap_add_item(keymap, "TRANSFORM_OT_vert_slide", VKEY, KM_PRESS, KM_SHIFT, 0); /* use KM_CLICK because same key is used for tweaks */ diff --git a/source/blender/editors/screen/screen_ops.c b/source/blender/editors/screen/screen_ops.c index 2e5f93ff521..d9746c6cab8 100644 --- a/source/blender/editors/screen/screen_ops.c +++ b/source/blender/editors/screen/screen_ops.c @@ -46,6 +46,7 @@ #include "DNA_gpencil_types.h" #include "DNA_scene_types.h" #include "DNA_meta_types.h" +#include "DNA_mesh_types.h" #include "DNA_mask_types.h" #include "DNA_node_types.h" #include "DNA_userdef_types.h" @@ -352,6 +353,15 @@ int ED_operator_editmesh_region_view3d(bContext *C) return 0; } +int ED_operator_editmesh_auto_smooth(bContext *C) +{ + Object *obedit = CTX_data_edit_object(C); + if (obedit && obedit->type == OB_MESH && (((Mesh *)(obedit->data))->flag & ME_AUTOSMOOTH)) { + return NULL != BKE_editmesh_from_object(obedit); + } + return 0; +} + int ED_operator_editarmature(bContext *C) { Object *obedit = CTX_data_edit_object(C); diff --git a/source/blender/editors/space_outliner/outliner_draw.c b/source/blender/editors/space_outliner/outliner_draw.c index d8d6b09ac6b..10ffa8eb629 100644 --- a/source/blender/editors/space_outliner/outliner_draw.c +++ b/source/blender/editors/space_outliner/outliner_draw.c @@ -1181,6 +1181,7 @@ static void tselem_draw_icon(uiBlock *block, int xmax, float x, float y, TreeSto case eModifierType_DataTransfer: UI_icon_draw(x, y, ICON_MOD_DATA_TRANSFER); break; case eModifierType_NormalEdit: + case eModifierType_WeightedNormal: UI_icon_draw(x, y, ICON_MOD_NORMALEDIT); break; /* Default */ case eModifierType_None: diff --git a/source/blender/editors/transform/transform.c b/source/blender/editors/transform/transform.c index 6ca9485599c..95a78e818f0 100644 --- a/source/blender/editors/transform/transform.c +++ b/source/blender/editors/transform/transform.c @@ -41,6 +41,7 @@ #include "DNA_armature_types.h" #include "DNA_constraint_types.h" #include "DNA_mask_types.h" +#include "DNA_mesh_types.h" #include "DNA_movieclip_types.h" #include "DNA_scene_types.h" /* PET modes */ @@ -62,6 +63,7 @@ #include "BKE_particle.h" #include "BKE_unit.h" #include "BKE_mask.h" +#include "BKE_mesh.h" #include "BKE_report.h" #include "BIF_gl.h" @@ -86,6 +88,7 @@ #include "UI_resources.h" #include "RNA_access.h" +#include "RNA_define.h" #include "BLF_api.h" #include "BLT_translation.h" @@ -106,6 +109,7 @@ static void postInputRotation(TransInfo *t, float values[3]); static void ElementRotation(TransInfo *t, TransData *td, float mat[3][3], const short around); static void initSnapSpatial(TransInfo *t, float r_snap[3]); +static void StoreCustomlnorValue(TransInfo *t, BMesh *bm); /* Transform Callbacks */ static void initBend(TransInfo *t); @@ -131,6 +135,9 @@ static void applyToSphere(TransInfo *t, const int mval[2]); static void initRotation(TransInfo *t); static void applyRotation(TransInfo *t, const int mval[2]); +static void initNormalRotation(TransInfo *t); +static void applyNormalRotation(TransInfo *t, const int mval[2]); + static void initShrinkFatten(TransInfo *t); static void applyShrinkFatten(TransInfo *t, const int mval[2]); @@ -1465,6 +1472,20 @@ int transformEvent(TransInfo *t, const wmEvent *event) handled = true; } break; + case NKEY: + if (ELEM(t->mode, TFM_ROTATION)) { + if (t->obedit && t->obedit->type == OB_MESH) { + if (((Mesh *)(t->obedit->data))->flag & ME_AUTOSMOOTH) { + restoreTransObjects(t); + resetTransModal(t); + resetTransRestrictions(t); + initNormalRotation(t); + t->redraw = TREDRAW_HARD; + handled = true; + } + } + } + break; default: break; } @@ -2313,6 +2334,9 @@ bool initTransform(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *eve case TFM_SEQ_SLIDE: initSeqSlide(t); break; + case TFM_NORMAL_ROTATION: + initNormalRotation(t); + break; } if (t->state == TRANS_CANCEL) { @@ -2369,6 +2393,32 @@ bool initTransform(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *eve t->flag |= T_AUTOVALUES; } + if ((prop = RNA_struct_find_property(op->ptr, "preserve_clnor"))) { + if (t->obedit && t->obedit->type == OB_MESH && (((Mesh *)(t->obedit->data))->flag & ME_AUTOSMOOTH)) { + BMEditMesh *em = BKE_editmesh_from_object(t->obedit); + bool all_select = false; + + /* Currently only used for 3 most frequent transform ops, can include more ops. */ + if (ELEM(t->mode, TFM_TRANSLATION, TFM_ROTATION, TFM_RESIZE)) { + if (em->bm->totvertsel == em->bm->totvert) { + /* No need to invalidate if whole mesh is selected. */ + all_select = true; + } + } + if (t->flag & T_MODAL) { + RNA_property_boolean_set(op->ptr, prop, false); + } + if (!all_select) { + const bool preserve_clnor = RNA_property_boolean_get(op->ptr, prop); + if (preserve_clnor) { + BKE_editmesh_lnorspace_update(em); + t->flag |= T_CLNOR_REBUILD; + } + BM_lnorspace_invalidate(em->bm, true); + } + } + } + t->context = NULL; return 1; @@ -2429,6 +2479,10 @@ int transformEnd(bContext *C, TransInfo *t) restoreTransObjects(t); // calls recalcData() } else { + if (t->flag & T_CLNOR_REBUILD) { + BMEditMesh *em = BKE_editmesh_from_object(t->obedit); + BM_lnorspace_rebuild(em->bm, true); + } exit_code = OPERATOR_FINISHED; } @@ -3724,6 +3778,28 @@ static void initRotation(TransInfo *t) copy_v3_v3(t->axis_orig, t->axis); } +/* Used by Transform Rotation and Transform Normal Rotation */ +static void headerRotation(TransInfo *t, char str[UI_MAX_DRAW_STR], float final) +{ + size_t ofs = 0; + + if (hasNumInput(&t->num)) { + char c[NUM_STR_REP_LEN]; + + outputNumInput(&(t->num), c, &t->scene->unit); + + ofs += BLI_snprintf(str + ofs, UI_MAX_DRAW_STR - ofs, IFACE_("Rot: %s %s %s"), &c[0], t->con.text, t->proptext); + } + else { + ofs += BLI_snprintf(str + ofs, UI_MAX_DRAW_STR - ofs, IFACE_("Rot: %.2f%s %s"), + RAD2DEGF(final), t->con.text, t->proptext); + } + + if (t->flag & T_PROP_EDIT_ALL) { + ofs += BLI_snprintf(str + ofs, UI_MAX_DRAW_STR - ofs, IFACE_(" Proportional size: %.2f"), t->prop_size); + } +} + /** * Applies values of rotation to `td->loc` and `td->ext->quat` * based on a rotation matrix (mat) and a pivot (center). @@ -3989,7 +4065,6 @@ static void applyRotationValue(TransInfo *t, float angle, float axis[3]) static void applyRotation(TransInfo *t, const int UNUSED(mval[2])) { char str[UI_MAX_DRAW_STR]; - size_t ofs = 0; float final; @@ -4012,21 +4087,7 @@ static void applyRotation(TransInfo *t, const int UNUSED(mval[2])) t->values[0] = final; - if (hasNumInput(&t->num)) { - char c[NUM_STR_REP_LEN]; - - outputNumInput(&(t->num), c, &t->scene->unit); - - ofs += BLI_snprintf(str + ofs, sizeof(str) - ofs, IFACE_("Rot: %s %s %s"), &c[0], t->con.text, t->proptext); - } - else { - ofs += BLI_snprintf(str + ofs, sizeof(str) - ofs, IFACE_("Rot: %.2f%s %s"), - RAD2DEGF(final), t->con.text, t->proptext); - } - - if (t->flag & T_PROP_EDIT_ALL) { - ofs += BLI_snprintf(str + ofs, sizeof(str) - ofs, IFACE_(" Proportional size: %.2f"), t->prop_size); - } + headerRotation(t, str, final); applyRotationValue(t, final, t->axis); @@ -4153,6 +4214,143 @@ static void applyTrackball(TransInfo *t, const int UNUSED(mval[2])) /* -------------------------------------------------------------------- */ +/* Transform (Normal Rotation) */ + +/** \name Transform Normal Rotation +* \{ */ + +static void StoreCustomlnorValue(TransInfo *t, BMesh *bm) +{ + float mtx[3][3], smtx[3][3]; + + BMLoopNorEditDataArray *ld = BM_loop_normal_editdata_array_init(bm); + + copy_m3_m4(mtx, t->obedit->obmat); + pseudoinverse_m3_m3(smtx, mtx, PSEUDOINVERSE_EPSILON); + + BMLoopNorEditData *tld = ld->lnor_editdata; + for (int i = 0; i < ld->totloop; i++, tld++) { + copy_m3_m3(tld->mtx, mtx); + copy_m3_m3(tld->smtx, smtx); + } + + t->custom.mode.data = ld; + t->custom.mode.free_cb = freeCustomNormalArray; +} + +void freeCustomNormalArray(TransInfo *t, TransCustomData *custom_data) +{ + BMLoopNorEditDataArray *ld = custom_data->data; + + if (t->state == TRANS_CANCEL) { + BMLoopNorEditData *tld = ld->lnor_editdata; + BMEditMesh *em = BKE_editmesh_from_object(t->obedit); + BMesh *bm = em->bm; + + for (int i = 0; i < ld->totloop; i++, tld++) { /* Restore custom loop normal on cancel */ + BKE_lnor_space_custom_normal_to_data(bm->lnor_spacearr->lspacearr[tld->loop_index], tld->niloc, tld->clnors_data); + } + } + + BM_loop_normal_editdata_array_free(ld); + + t->custom.mode.data = NULL; + t->custom.mode.free_cb = NULL; +} + +static void initNormalRotation(TransInfo *t) +{ + t->mode = TFM_NORMAL_ROTATION; + t->transform = applyNormalRotation; + + setInputPostFct(&t->mouse, postInputRotation); + initMouseInputMode(t, &t->mouse, INPUT_ANGLE); + + t->idx_max = 0; + t->num.idx_max = 0; + t->snap[0] = 0.0f; + t->snap[1] = DEG2RAD(5.0); + t->snap[2] = DEG2RAD(1.0); + + copy_v3_fl(t->num.val_inc, t->snap[2]); + t->num.unit_sys = t->scene->unit.system; + t->num.unit_use_radians = (t->scene->unit.system_rotation == USER_UNIT_ROT_RADIANS); + t->num.unit_type[0] = B_UNIT_ROTATION; + + BMEditMesh *em = BKE_editmesh_from_object(t->obedit); + BMesh *bm = em->bm; + + BKE_editmesh_lnorspace_update(em); + + StoreCustomlnorValue(t, bm); + + negate_v3_v3(t->axis, t->viewinv[2]); + normalize_v3(t->axis); + + copy_v3_v3(t->axis_orig, t->axis); +} + +/* Works by getting custom normal from clnor_data, transform, then store */ +static void applyNormalRotation(TransInfo *t, const int UNUSED(mval[2])) +{ + BMEditMesh *em = BKE_editmesh_from_object(t->obedit); + BMesh *bm = em->bm; + char str[UI_MAX_DRAW_STR]; + + if ((t->con.mode & CON_APPLY) && t->con.applyRot) { + t->con.applyRot(t, NULL, t->axis, NULL); + } + else { + /* reset axis if constraint is not set */ + copy_v3_v3(t->axis, t->axis_orig); + } + + BMLoopNorEditDataArray *ld = t->custom.mode.data; + BMLoopNorEditData *tld = ld->lnor_editdata; + + float axis[3]; + float mat[3][3]; + float angle = t->values[0]; + copy_v3_v3(axis, t->axis); + + snapGridIncrement(t, &angle); + + applySnapping(t, &angle); + + applyNumInput(&t->num, &angle); + + headerRotation(t, str, angle); + + axis_angle_normalized_to_mat3(mat, axis, angle); + + for (int i = 0; i < ld->totloop; i++, tld++) { + float center[3]; + float vec[3], totmat[3][3], smat[3][3]; + zero_v3(center); + + mul_m3_m3m3(totmat, mat, tld->mtx); + mul_m3_m3m3(smat, tld->smtx, totmat); + + sub_v3_v3v3(vec, tld->niloc, center); + mul_m3_v3(smat, vec); + + add_v3_v3v3(tld->nloc, vec, center); + + sub_v3_v3v3(vec, tld->nloc, tld->niloc); + add_v3_v3v3(tld->nloc, tld->niloc, vec); + + BKE_lnor_space_custom_normal_to_data(bm->lnor_spacearr->lspacearr[tld->loop_index], tld->nloc, tld->clnors_data); + } + + recalcData(t); + + ED_area_headerprint(t->sa, str); +} + +/** \} */ + + +/* -------------------------------------------------------------------- */ /* Transform (Translation) */ static void initSnapSpatial(TransInfo *t, float r_snap[3]) diff --git a/source/blender/editors/transform/transform.h b/source/blender/editors/transform/transform.h index 06a60456cdb..3cf0fda0a7a 100644 --- a/source/blender/editors/transform/transform.h +++ b/source/blender/editors/transform/transform.h @@ -536,6 +536,8 @@ typedef struct TransInfo { /** #TransInfo.center has been set, don't change it. */ #define T_OVERRIDE_CENTER (1 << 25) +#define T_CLNOR_REBUILD (1 << 26) + /* TransInfo->modifiers */ #define MOD_CONSTRAINT_SELECT 0x01 #define MOD_PRECISION 0x02 @@ -798,6 +800,8 @@ bool applyTransformOrientation(const struct bContext *C, float mat[3][3], char r int getTransformOrientation_ex(const struct bContext *C, float normal[3], float plane[3], const short around); int getTransformOrientation(const struct bContext *C, float normal[3], float plane[3]); +void freeCustomNormalArray(TransInfo *t, TransCustomData *custom_data); + void freeEdgeSlideTempFaces(EdgeSlideData *sld); void freeEdgeSlideVerts(TransInfo *t, TransCustomData *custom_data); void projectEdgeSlideData(TransInfo *t, bool is_final); diff --git a/source/blender/editors/transform/transform_generics.c b/source/blender/editors/transform/transform_generics.c index 277e01d1e2b..7578607195d 100644 --- a/source/blender/editors/transform/transform_generics.c +++ b/source/blender/editors/transform/transform_generics.c @@ -1072,6 +1072,9 @@ void resetTransModal(TransInfo *t) else if (t->mode == TFM_VERT_SLIDE) { freeVertSlideVerts(t, &t->custom.mode); } + else if (t->mode == TFM_NORMAL_ROTATION) { + freeCustomNormalArray(t, &t->custom.mode); + } } void resetTransRestrictions(TransInfo *t) diff --git a/source/blender/editors/transform/transform_ops.c b/source/blender/editors/transform/transform_ops.c index 46bd83b5d35..be465d12aad 100644 --- a/source/blender/editors/transform/transform_ops.c +++ b/source/blender/editors/transform/transform_ops.c @@ -81,6 +81,7 @@ static const char OP_VERT_SLIDE[] = "TRANSFORM_OT_vert_slide"; static const char OP_EDGE_CREASE[] = "TRANSFORM_OT_edge_crease"; static const char OP_EDGE_BWEIGHT[] = "TRANSFORM_OT_edge_bevelweight"; static const char OP_SEQ_SLIDE[] = "TRANSFORM_OT_seq_slide"; +static const char OP_NORMAL_ROTATION[] = "TRANSFORM_OT_rotate_normal"; static void TRANSFORM_OT_translate(struct wmOperatorType *ot); static void TRANSFORM_OT_rotate(struct wmOperatorType *ot); @@ -99,6 +100,7 @@ static void TRANSFORM_OT_vert_slide(struct wmOperatorType *ot); static void TRANSFORM_OT_edge_crease(struct wmOperatorType *ot); static void TRANSFORM_OT_edge_bevelweight(struct wmOperatorType *ot); static void TRANSFORM_OT_seq_slide(struct wmOperatorType *ot); +static void TRANSFORM_OT_rotate_normal(struct wmOperatorType *ot); static TransformModeItem transform_modes[] = { @@ -119,6 +121,7 @@ static TransformModeItem transform_modes[] = {OP_EDGE_CREASE, TFM_CREASE, TRANSFORM_OT_edge_crease}, {OP_EDGE_BWEIGHT, TFM_BWEIGHT, TRANSFORM_OT_edge_bevelweight}, {OP_SEQ_SLIDE, TFM_SEQ_SLIDE, TRANSFORM_OT_seq_slide}, + {OP_NORMAL_ROTATION, TFM_NORMAL_ROTATION, TRANSFORM_OT_rotate_normal}, {NULL, 0} }; @@ -495,6 +498,39 @@ static int transform_invoke(bContext *C, wmOperator *op, const wmEvent *event) } } +static bool transform_draw_check_prop(PointerRNA *ptr, PropertyRNA *prop) +{ + const char *prop_id = RNA_property_identifier(prop); + + /* Only show preserve_clnor option if requested (kinda hackish, we need to take that decision based on context... */ + if (STREQ(prop_id, "preserve_clnor")) { + return RNA_boolean_get(ptr, "show_preserve_clnor");; + } + + /* Else, show it! */ + return true; +} + +static void transform_ui(bContext *C, wmOperator *op) +{ + uiLayout *layout = op->layout; + wmWindowManager *wm = CTX_wm_manager(C); + PointerRNA ptr; + Object *obedit = CTX_data_edit_object(C); + + PropertyRNA *prop = RNA_struct_find_property(op->ptr, "show_preserve_clnor"); + if (prop) { + RNA_property_boolean_set( + op->ptr, prop, + (obedit && obedit->type == OB_MESH && (((Mesh *)(obedit->data))->flag & ME_AUTOSMOOTH))); + } + + RNA_pointer_create(&wm->id, op->type->srna, op->properties, &ptr); + + /* Main auto-draw call */ + uiDefAutoButsRNA(layout, &ptr, transform_draw_check_prop, '\0'); +} + void Transform_Properties(struct wmOperatorType *ot, int flags) { PropertyRNA *prop; @@ -580,6 +616,12 @@ void Transform_Properties(struct wmOperatorType *ot, int flags) prop = RNA_def_boolean(ot->srna, "use_accurate", 0, "Accurate", "Use accurate transformation"); RNA_def_property_flag(prop, PROP_HIDDEN); } + + if (flags & P_CLNOR_INVALIDATE) { + RNA_def_boolean(ot->srna, "preserve_clnor", false, "Preserve Normals", "Keep custom normals during transform"); + prop = RNA_def_boolean(ot->srna, "show_preserve_clnor", false, "Show Preserve Normals", ""); + RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); + } } static void TRANSFORM_OT_translate(struct wmOperatorType *ot) @@ -596,10 +638,11 @@ static void TRANSFORM_OT_translate(struct wmOperatorType *ot) ot->modal = transform_modal; ot->cancel = transform_cancel; ot->poll = ED_operator_screenactive; + ot->ui = transform_ui; RNA_def_float_vector_xyz(ot->srna, "value", 3, NULL, -FLT_MAX, FLT_MAX, "Vector", "", -FLT_MAX, FLT_MAX); - Transform_Properties(ot, P_CONSTRAINT | P_PROPORTIONAL | P_MIRROR | P_ALIGN_SNAP | P_OPTIONS | P_GPENCIL_EDIT); + Transform_Properties(ot, P_CONSTRAINT | P_PROPORTIONAL | P_MIRROR | P_ALIGN_SNAP | P_OPTIONS | P_GPENCIL_EDIT | P_CLNOR_INVALIDATE); } static void TRANSFORM_OT_resize(struct wmOperatorType *ot) @@ -616,12 +659,12 @@ static void TRANSFORM_OT_resize(struct wmOperatorType *ot) ot->modal = transform_modal; ot->cancel = transform_cancel; ot->poll = ED_operator_screenactive; + ot->ui = transform_ui; RNA_def_float_vector(ot->srna, "value", 3, VecOne, -FLT_MAX, FLT_MAX, "Vector", "", -FLT_MAX, FLT_MAX); Transform_Properties( - ot, P_CONSTRAINT | P_PROPORTIONAL | P_MIRROR | P_GEO_SNAP | P_OPTIONS | P_GPENCIL_EDIT | P_CENTER); -} + ot, P_CONSTRAINT | P_PROPORTIONAL | P_MIRROR | P_GEO_SNAP | P_OPTIONS | P_GPENCIL_EDIT | P_CENTER | P_CLNOR_INVALIDATE);} static int skin_resize_poll(bContext *C) { @@ -647,6 +690,7 @@ static void TRANSFORM_OT_skin_resize(struct wmOperatorType *ot) ot->modal = transform_modal; ot->cancel = transform_cancel; ot->poll = skin_resize_poll; + ot->ui = transform_ui; RNA_def_float_vector(ot->srna, "value", 3, VecOne, -FLT_MAX, FLT_MAX, "Vector", "", -FLT_MAX, FLT_MAX); @@ -667,12 +711,12 @@ static void TRANSFORM_OT_trackball(struct wmOperatorType *ot) ot->modal = transform_modal; ot->cancel = transform_cancel; ot->poll = ED_operator_screenactive; + ot->ui = transform_ui; /* Maybe we could use float_vector_xyz here too? */ RNA_def_float_rotation(ot->srna, "value", 2, NULL, -FLT_MAX, FLT_MAX, "Angle", "", -FLT_MAX, FLT_MAX); - Transform_Properties(ot, P_PROPORTIONAL | P_MIRROR | P_SNAP | P_GPENCIL_EDIT | P_CENTER); -} + Transform_Properties(ot, P_PROPORTIONAL | P_MIRROR | P_SNAP | P_GPENCIL_EDIT | P_CENTER | P_CLNOR_INVALIDATE);} static void TRANSFORM_OT_rotate(struct wmOperatorType *ot) { @@ -688,12 +732,12 @@ static void TRANSFORM_OT_rotate(struct wmOperatorType *ot) ot->modal = transform_modal; ot->cancel = transform_cancel; ot->poll = ED_operator_screenactive; + ot->ui = transform_ui; RNA_def_float_rotation(ot->srna, "value", 0, NULL, -FLT_MAX, FLT_MAX, "Angle", "", -M_PI * 2, M_PI * 2); Transform_Properties( - ot, P_AXIS | P_CONSTRAINT | P_PROPORTIONAL | P_MIRROR | P_GEO_SNAP | P_GPENCIL_EDIT | P_CENTER); -} + ot, P_AXIS | P_CONSTRAINT | P_PROPORTIONAL | P_MIRROR | P_GEO_SNAP | P_GPENCIL_EDIT | P_CENTER | P_CLNOR_INVALIDATE);} static void TRANSFORM_OT_tilt(struct wmOperatorType *ot) { @@ -712,6 +756,7 @@ static void TRANSFORM_OT_tilt(struct wmOperatorType *ot) ot->modal = transform_modal; ot->cancel = transform_cancel; ot->poll = ED_operator_editcurve_3d; + ot->ui = transform_ui; RNA_def_float_rotation(ot->srna, "value", 0, NULL, -FLT_MAX, FLT_MAX, "Angle", "", -M_PI * 2, M_PI * 2); @@ -732,11 +777,11 @@ static void TRANSFORM_OT_bend(struct wmOperatorType *ot) ot->modal = transform_modal; ot->cancel = transform_cancel; ot->poll = ED_operator_region_view3d_active; + ot->ui = transform_ui; RNA_def_float_rotation(ot->srna, "value", 1, NULL, -FLT_MAX, FLT_MAX, "Angle", "", -M_PI * 2, M_PI * 2); - Transform_Properties(ot, P_PROPORTIONAL | P_MIRROR | P_SNAP | P_GPENCIL_EDIT | P_CENTER); -} + Transform_Properties(ot, P_PROPORTIONAL | P_MIRROR | P_SNAP | P_GPENCIL_EDIT | P_CENTER | P_CLNOR_INVALIDATE);} static void TRANSFORM_OT_shear(struct wmOperatorType *ot) { @@ -752,10 +797,11 @@ static void TRANSFORM_OT_shear(struct wmOperatorType *ot) ot->modal = transform_modal; ot->cancel = transform_cancel; ot->poll = ED_operator_screenactive; + ot->ui = transform_ui; RNA_def_float(ot->srna, "value", 0, -FLT_MAX, FLT_MAX, "Offset", "", -FLT_MAX, FLT_MAX); - Transform_Properties(ot, P_PROPORTIONAL | P_MIRROR | P_SNAP | P_GPENCIL_EDIT); + Transform_Properties(ot, P_PROPORTIONAL | P_MIRROR | P_SNAP | P_GPENCIL_EDIT | P_CLNOR_INVALIDATE); // XXX Shear axis? } @@ -773,11 +819,11 @@ static void TRANSFORM_OT_push_pull(struct wmOperatorType *ot) ot->modal = transform_modal; ot->cancel = transform_cancel; ot->poll = ED_operator_screenactive; + ot->ui = transform_ui; RNA_def_float(ot->srna, "value", 0, -FLT_MAX, FLT_MAX, "Distance", "", -FLT_MAX, FLT_MAX); - Transform_Properties(ot, P_PROPORTIONAL | P_MIRROR | P_SNAP | P_CENTER); -} + Transform_Properties(ot, P_PROPORTIONAL | P_MIRROR | P_SNAP | P_CENTER | P_CLNOR_INVALIDATE);} static void TRANSFORM_OT_shrink_fatten(struct wmOperatorType *ot) { @@ -793,12 +839,13 @@ static void TRANSFORM_OT_shrink_fatten(struct wmOperatorType *ot) ot->modal = transform_modal; ot->cancel = transform_cancel; ot->poll = ED_operator_editmesh; + ot->ui = transform_ui; RNA_def_float(ot->srna, "value", 0, -FLT_MAX, FLT_MAX, "Offset", "", -FLT_MAX, FLT_MAX); RNA_def_boolean(ot->srna, "use_even_offset", true, "Offset Even", "Scale the offset to give more even thickness"); - Transform_Properties(ot, P_PROPORTIONAL | P_MIRROR | P_SNAP); + Transform_Properties(ot, P_PROPORTIONAL | P_MIRROR | P_SNAP | P_CLNOR_INVALIDATE); } static void TRANSFORM_OT_tosphere(struct wmOperatorType *ot) @@ -816,11 +863,11 @@ static void TRANSFORM_OT_tosphere(struct wmOperatorType *ot) ot->modal = transform_modal; ot->cancel = transform_cancel; ot->poll = ED_operator_screenactive; + ot->ui = transform_ui; RNA_def_float_factor(ot->srna, "value", 0, 0, 1, "Factor", "", 0, 1); - Transform_Properties(ot, P_PROPORTIONAL | P_MIRROR | P_SNAP | P_GPENCIL_EDIT | P_CENTER); -} + Transform_Properties(ot, P_PROPORTIONAL | P_MIRROR | P_SNAP | P_GPENCIL_EDIT | P_CENTER | P_CLNOR_INVALIDATE);} static void TRANSFORM_OT_mirror(struct wmOperatorType *ot) { @@ -836,9 +883,9 @@ static void TRANSFORM_OT_mirror(struct wmOperatorType *ot) ot->modal = transform_modal; ot->cancel = transform_cancel; ot->poll = ED_operator_screenactive; + ot->ui = transform_ui; - Transform_Properties(ot, P_CONSTRAINT | P_PROPORTIONAL | P_GPENCIL_EDIT | P_CENTER); -} + Transform_Properties(ot, P_CONSTRAINT | P_PROPORTIONAL | P_GPENCIL_EDIT | P_CENTER | P_CLNOR_INVALIDATE);} static void TRANSFORM_OT_edge_slide(struct wmOperatorType *ot) { @@ -856,6 +903,7 @@ static void TRANSFORM_OT_edge_slide(struct wmOperatorType *ot) ot->modal = transform_modal; ot->cancel = transform_cancel; ot->poll = ED_operator_editmesh_region_view3d; + ot->ui = transform_ui; RNA_def_float_factor(ot->srna, "value", 0, -10.0f, 10.0f, "Factor", "", -1.0f, 1.0f); @@ -868,7 +916,7 @@ static void TRANSFORM_OT_edge_slide(struct wmOperatorType *ot) RNA_def_boolean(ot->srna, "use_clamp", true, "Clamp", "Clamp within the edge extents"); - Transform_Properties(ot, P_MIRROR | P_SNAP | P_CORRECT_UV); + Transform_Properties(ot, P_MIRROR | P_SNAP | P_CORRECT_UV | P_CLNOR_INVALIDATE); } static void TRANSFORM_OT_vert_slide(struct wmOperatorType *ot) @@ -885,6 +933,7 @@ static void TRANSFORM_OT_vert_slide(struct wmOperatorType *ot) ot->modal = transform_modal; ot->cancel = transform_cancel; ot->poll = ED_operator_editmesh_region_view3d; + ot->ui = transform_ui; RNA_def_float_factor(ot->srna, "value", 0, -10.0f, 10.0f, "Factor", "", -1.0f, 1.0f); RNA_def_boolean(ot->srna, "use_even", false, "Even", @@ -894,7 +943,7 @@ static void TRANSFORM_OT_vert_slide(struct wmOperatorType *ot) RNA_def_boolean(ot->srna, "use_clamp", true, "Clamp", "Clamp within the edge extents"); - Transform_Properties(ot, P_MIRROR | P_SNAP | P_CORRECT_UV); + Transform_Properties(ot, P_MIRROR | P_SNAP | P_CORRECT_UV | P_CLNOR_INVALIDATE); } static void TRANSFORM_OT_edge_crease(struct wmOperatorType *ot) @@ -911,6 +960,7 @@ static void TRANSFORM_OT_edge_crease(struct wmOperatorType *ot) ot->modal = transform_modal; ot->cancel = transform_cancel; ot->poll = ED_operator_editmesh; + ot->ui = transform_ui; RNA_def_float_factor(ot->srna, "value", 0, -1.0f, 1.0f, "Factor", "", -1.0f, 1.0f); @@ -952,6 +1002,7 @@ static void TRANSFORM_OT_edge_bevelweight(struct wmOperatorType *ot) ot->modal = transform_modal; ot->cancel = transform_cancel; ot->poll = ED_operator_editmesh; + ot->ui = transform_ui; RNA_def_float_factor(ot->srna, "value", 0, -1.0f, 1.0f, "Factor", "", -1.0f, 1.0f); @@ -972,12 +1023,34 @@ static void TRANSFORM_OT_seq_slide(struct wmOperatorType *ot) ot->modal = transform_modal; ot->cancel = transform_cancel; ot->poll = ED_operator_sequencer_active; + ot->ui = transform_ui; RNA_def_float_vector_xyz(ot->srna, "value", 2, NULL, -FLT_MAX, FLT_MAX, "Vector", "", -FLT_MAX, FLT_MAX); Transform_Properties(ot, P_SNAP); } +static void TRANSFORM_OT_rotate_normal(struct wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Normal Rotate"; + ot->description = "Rotate split normal of selected items"; + ot->idname = OP_NORMAL_ROTATION; + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_BLOCKING; + + /* api callbacks */ + ot->invoke = transform_invoke; + ot->exec = transform_exec; + ot->modal = transform_modal; + ot->cancel = transform_cancel; + ot->poll = ED_operator_editmesh_auto_smooth; + ot->ui = transform_ui; + + RNA_def_float_rotation(ot->srna, "value", 0, NULL, -FLT_MAX, FLT_MAX, "Angle", "", -M_PI * 2, M_PI * 2); + + Transform_Properties(ot, P_AXIS | P_CONSTRAINT | P_MIRROR); +} + static void TRANSFORM_OT_transform(struct wmOperatorType *ot) { PropertyRNA *prop; @@ -994,6 +1067,7 @@ static void TRANSFORM_OT_transform(struct wmOperatorType *ot) ot->modal = transform_modal; ot->cancel = transform_cancel; ot->poll = ED_operator_screenactive; + ot->ui = transform_ui; prop = RNA_def_enum(ot->srna, "mode", rna_enum_transform_mode_types, TFM_TRANSLATION, "Mode", ""); RNA_def_property_flag(prop, PROP_HIDDEN); |