diff --git a/scripts/presets/keyconfig/keymap_data/blender_default.py b/scripts/presets/keyconfig/keymap_data/blender_default.py index c1514c6d649..79b7db15cc7 100644 --- a/scripts/presets/keyconfig/keymap_data/blender_default.py +++ b/scripts/presets/keyconfig/keymap_data/blender_default.py @@ -6084,6 +6084,10 @@ def km_edit_curves(params): ("curves.select_less", {"type": 'NUMPAD_MINUS', "value": 'PRESS', "ctrl": True, "repeat": True}, None), *_template_items_proportional_editing( params, connected=True, toggle_data_path='tool_settings.use_proportional_edit'), + ("curves.tilt_clear", {"type": 'T', "value": 'PRESS', "alt": True}, None), + op_tool_optional( + ("transform.tilt", {"type": 'T', "value": 'PRESS', "ctrl": True}, None), + (op_tool_cycle, "builtin.tilt"), params), ("transform.transform", {"type": 'S', "value": 'PRESS', "alt": True}, {"properties": [("mode", 'CURVE_SHRINKFATTEN')]}), ]) diff --git a/scripts/startup/bl_ui/space_toolsystem_toolbar.py b/scripts/startup/bl_ui/space_toolsystem_toolbar.py index c12ef7336cd..04b20abc0e1 100644 --- a/scripts/startup/bl_ui/space_toolsystem_toolbar.py +++ b/scripts/startup/bl_ui/space_toolsystem_toolbar.py @@ -3026,8 +3026,10 @@ class VIEW3D_PT_tools_active(ToolSelectPanelHelper, Panel): 'EDIT_CURVES': [ *_tools_default, None, - _defs_edit_curve.curve_radius, _defs_edit_curves.draw, + None, + _defs_edit_curve.curve_radius, + _defs_edit_curve.tilt, ], 'EDIT_SURFACE': [ *_tools_default, diff --git a/source/blender/editors/curves/intern/curves_ops.cc b/source/blender/editors/curves/intern/curves_ops.cc index 2ab9f5a97e7..d8ede5b2b3f 100644 --- a/source/blender/editors/curves/intern/curves_ops.cc +++ b/source/blender/editors/curves/intern/curves_ops.cc @@ -1282,6 +1282,46 @@ static void CURVES_OT_duplicate(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } +namespace clear_tilt { + +static int exec(bContext *C, wmOperator * /*op*/) +{ + for (Curves *curves_id : get_unique_editable_curves(*C)) { + bke::CurvesGeometry &curves = curves_id->geometry.wrap(); + IndexMaskMemory memory; + const IndexMask selection = retrieve_selected_points(*curves_id, memory); + if (selection.is_empty()) { + continue; + } + + if (selection.size() == curves.points_num()) { + curves.attributes_for_write().remove("tilt"); + } + else { + index_mask::masked_fill(curves.tilt_for_write(), 0.0f, selection); + } + + curves.tag_normals_changed(); + DEG_id_tag_update(&curves_id->id, ID_RECALC_GEOMETRY); + WM_event_add_notifier(C, NC_GEOM | ND_DATA, curves_id); + } + return OPERATOR_FINISHED; +} + +} // namespace clear_tilt + +static void CURVES_OT_tilt_clear(wmOperatorType *ot) +{ + ot->name = "Clear Tilt"; + ot->idname = __func__; + ot->description = "Clear the tilt of selected control points"; + + ot->exec = clear_tilt::exec; + ot->poll = editable_curves_in_edit_mode_poll; + + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + } // namespace blender::ed::curves void ED_operatortypes_curves() @@ -1302,6 +1342,7 @@ void ED_operatortypes_curves() WM_operatortype_append(CURVES_OT_surface_set); WM_operatortype_append(CURVES_OT_delete); WM_operatortype_append(CURVES_OT_duplicate); + WM_operatortype_append(CURVES_OT_tilt_clear); } void ED_operatormacros_curves() diff --git a/source/blender/editors/transform/transform_convert_curves.cc b/source/blender/editors/transform/transform_convert_curves.cc index 6bad9f68922..b1d28b541f1 100644 --- a/source/blender/editors/transform/transform_convert_curves.cc +++ b/source/blender/editors/transform/transform_convert_curves.cc @@ -105,7 +105,11 @@ static void createTransCurvesVerts(bContext * /*C*/, TransInfo *t) "radius", ATTR_DOMAIN_POINT, bke::AttributeInitVArray(VArray::ForSingle(0.01f, curves.points_num()))); - + value_attribute = attribute_writer.span; + } + else if (t->mode == TFM_TILT) { + bke::MutableAttributeAccessor attributes = curves.attributes_for_write(); + attribute_writer = attributes.lookup_or_add_for_write_span("tilt", ATTR_DOMAIN_POINT); value_attribute = attribute_writer.span; } @@ -117,6 +121,8 @@ static void createTransCurvesVerts(bContext * /*C*/, TransInfo *t) curves.curves_range(), use_connected_only, 0 /* No data offset for curves. */); + + /* TODO: This is wrong. The attribute writer should live at least as long as the span. */ attribute_writer.finish(); } } @@ -127,9 +133,16 @@ static void recalcData_curves(TransInfo *t) for (const TransDataContainer &tc : trans_data_contrainers) { Curves *curves_id = static_cast(tc.obedit->data); bke::CurvesGeometry &curves = curves_id->geometry.wrap(); - - curves.calculate_bezier_auto_handles(); - curves.tag_positions_changed(); + if (t->mode == TFM_CURVE_SHRINKFATTEN) { + /* No cache to update currently. */ + } + else if (t->mode == TFM_TILT) { + curves.tag_normals_changed(); + } + else { + curves.tag_positions_changed(); + curves.calculate_bezier_auto_handles(); + } DEG_id_tag_update(&curves_id->id, ID_RECALC_GEOMETRY); } } diff --git a/source/blender/editors/transform/transform_ops.cc b/source/blender/editors/transform/transform_ops.cc index 27039700184..a9ddc2ed4bf 100644 --- a/source/blender/editors/transform/transform_ops.cc +++ b/source/blender/editors/transform/transform_ops.cc @@ -8,6 +8,7 @@ #include "MEM_guardedalloc.h" +#include "DNA_curve_types.h" #include "DNA_object_types.h" #include "DNA_scene_types.h" @@ -960,6 +961,22 @@ static void TRANSFORM_OT_rotate(wmOperatorType *ot) P_GEO_SNAP | P_GPENCIL_EDIT | P_CENTER); } +static bool tilt_poll(bContext *C) +{ + Object *obedit = CTX_data_edit_object(C); + if (!obedit) { + return false; + } + if (obedit->type == OB_CURVES_LEGACY) { + Curve *cu = (Curve *)obedit->data; + return (cu->flag & CU_3D) && (nullptr != cu->editnurb); + } + if (obedit->type == OB_CURVES) { + return true; + } + return true; +} + static void TRANSFORM_OT_tilt(wmOperatorType *ot) { /* identifiers */ @@ -976,7 +993,7 @@ static void TRANSFORM_OT_tilt(wmOperatorType *ot) ot->exec = transform_exec; ot->modal = transform_modal; ot->cancel = transform_cancel; - ot->poll = ED_operator_editcurve_3d; + ot->poll = tilt_poll; ot->poll_property = transform_poll_property; RNA_def_float_rotation(