From f1fbc06cc327f65a89725f437423fc4a3d5cace4 Mon Sep 17 00:00:00 2001 From: Michael Alexsander Date: Wed, 18 Mar 2026 12:39:53 -0300 Subject: [PATCH] Add option for 3D viewports to override the `GridMap` editor axis --- editor/scene/3d/node_3d_editor_viewport.h | 1 + .../gridmap/editor/grid_map_editor_plugin.cpp | 109 +++++++++++++++--- .../gridmap/editor/grid_map_editor_plugin.h | 23 +++- 3 files changed, 109 insertions(+), 24 deletions(-) diff --git a/editor/scene/3d/node_3d_editor_viewport.h b/editor/scene/3d/node_3d_editor_viewport.h index 4d4bcb9068..dd9b5af6f9 100644 --- a/editor/scene/3d/node_3d_editor_viewport.h +++ b/editor/scene/3d/node_3d_editor_viewport.h @@ -570,6 +570,7 @@ public: SubViewport *get_viewport_node() { return viewport; } Camera3D *get_camera_3d() { return camera; } // return the default camera object. Control *get_surface() { return surface; } + Ref get_controller() { return view_3d_controller; } Node3DEditorViewport(Node3DEditor *p_spatial_editor, int p_index); ~Node3DEditorViewport(); diff --git a/modules/gridmap/editor/grid_map_editor_plugin.cpp b/modules/gridmap/editor/grid_map_editor_plugin.cpp index 1e9cc1b241..4ed7a798b2 100644 --- a/modules/gridmap/editor/grid_map_editor_plugin.cpp +++ b/modules/gridmap/editor/grid_map_editor_plugin.cpp @@ -69,7 +69,7 @@ void GridMapEditor::_menu_option(int p_option) { case MENU_OPTION_PREV_LEVEL: { floor->set_value(floor->get_value() - 1); if (selection.active && input_action == INPUT_SELECT) { - selection.current[edit_axis]--; + selection.current[_get_edit_axis()]--; _validate_selection(); } } break; @@ -77,7 +77,7 @@ void GridMapEditor::_menu_option(int p_option) { case MENU_OPTION_NEXT_LEVEL: { floor->set_value(floor->get_value() + 1); if (selection.active && input_action == INPUT_SELECT) { - selection.current[edit_axis]++; + selection.current[_get_edit_axis()]++; _validate_selection(); } } break; @@ -90,17 +90,16 @@ void GridMapEditor::_menu_option(int p_option) { int idx = options->get_popup()->get_item_index(MENU_OPTION_X_AXIS + i); options->get_popup()->set_item_checked(idx, i == new_axis); } + edit_axis_select = Vector3::Axis(new_axis); - if (edit_axis != new_axis) { - if (edit_axis == Vector3::AXIS_Y) { - floor->set_tooltip_text("Change Grid Plane"); - } else if (new_axis == Vector3::AXIS_Y) { - floor->set_tooltip_text("Change Grid Floor"); - } - } - edit_axis = Vector3::Axis(new_axis); - update_grid(); + _update_edit_axis(); + } break; + case MENU_OPTION_VIEWPORT_OVERRIDE: { + allow_viewport_override = !allow_viewport_override; + options->get_popup()->set_item_checked(options->get_popup()->get_item_index(MENU_OPTION_VIEWPORT_OVERRIDE), allow_viewport_override); + + _update_edit_axis(); } break; case MENU_OPTION_CURSOR_ROTATE_X: @@ -261,6 +260,7 @@ void GridMapEditor::_update_selection_transform() { RenderingServer::get_singleton()->instance_set_transform(selection_instance, node->get_global_transform() * xf); + Vector3::Axis edit_axis = _get_edit_axis(); for (int i = 0; i < 3; i++) { if (i != edit_axis || (edit_floor[edit_axis] < selection.begin[edit_axis]) || (edit_floor[edit_axis] > selection.end[edit_axis] + 1)) { RenderingServer::get_singleton()->instance_set_transform(selection_level_instance[i], xf_zero); @@ -357,6 +357,67 @@ Array GridMapEditor::_get_selected_cells() const { return ret; } +void GridMapEditor::_update_edit_axis() { + viewport_axis = edit_axis_select; + + if (allow_viewport_override) { + Node3DEditorViewport *editor_viewport = nullptr; + for (uint32_t i = 0; i < Node3DEditor::VIEWPORTS_COUNT; i++) { + Node3DEditorViewport *viewport = Node3DEditor::get_singleton()->get_editor_viewport(i); + if (last_viewport == viewport->get_viewport_node()) { + editor_viewport = viewport; + break; + } + } + + if (editor_viewport) { + View3DController::ViewType view_type = editor_viewport->get_controller()->get_view_type(); + switch (view_type) { + case View3DController::VIEW_TYPE_TOP: + case View3DController::VIEW_TYPE_BOTTOM: { + viewport_axis = _get_facing_axis(node->get_global_basis().orthonormalized(), Vector3::UP); + } break; + + case View3DController::VIEW_TYPE_LEFT: + case View3DController::VIEW_TYPE_RIGHT: { + viewport_axis = _get_facing_axis(node->get_global_basis().orthonormalized(), Vector3::LEFT); + } break; + + case View3DController::VIEW_TYPE_FRONT: + case View3DController::VIEW_TYPE_REAR: { + viewport_axis = _get_facing_axis(node->get_global_basis().orthonormalized(), Vector3::FORWARD); + } break; + + default: { + } + } + } + } + + floor->set_tooltip_text(_get_edit_axis() == Vector3::AXIS_Y ? TTRC("Change Grid Floor") : TTRC("Change Grid Plane")); + update_grid(); +} + +Vector3::Axis GridMapEditor::_get_facing_axis(const Basis &p_grid_basis, const Vector3 &p_direction) const { + float dir_yz = Math::abs(p_grid_basis.get_column(0).dot(p_direction)); + float dir_xz = Math::abs(p_grid_basis.get_column(1).dot(p_direction)); + float dir_xy = Math::abs(p_grid_basis.get_column(2).dot(p_direction)); + + if (dir_xy >= dir_xz && dir_xy >= dir_yz) { + return Vector3::AXIS_Z; + } + if (dir_xz >= dir_xy && dir_xz >= dir_yz) { + return Vector3::AXIS_Y; + } + return Vector3::AXIS_X; +} + +void GridMapEditor::_view_state_changed(Node3DEditorViewport *p_viewport) { + if (node && last_viewport == p_viewport->get_viewport_node()) { + _update_edit_axis(); + } +} + String GridMapEditor::_get_cursor_coordinates() const { String text; if (cursor_visible || !set_items.is_empty() || !clipboard_items.is_empty()) { @@ -377,9 +438,6 @@ String GridMapEditor::_get_cursor_coordinates() const { } bool GridMapEditor::do_input_action(Camera3D *p_camera, const Point2 &p_point, bool p_click) { - if (!spatial_editor) { - return false; - } if (input_action == INPUT_TRANSFORM) { return false; } @@ -401,6 +459,8 @@ bool GridMapEditor::do_input_action(Camera3D *p_camera, const Point2 &p_point, b from = local_xform.xform(from); normal = local_xform.basis.xform(normal).normalized(); + Vector3::Axis edit_axis = _get_edit_axis(); + Plane p; p.normal[edit_axis] = 1.0; p.d = edit_floor[edit_axis] * node->get_cell_size()[edit_axis]; @@ -816,6 +876,11 @@ EditorPlugin::AfterGUIInput GridMapEditor::forward_spatial_input_event(Camera3D return EditorPlugin::AFTER_GUI_INPUT_PASS; } + if (allow_viewport_override && last_viewport != p_camera->get_viewport()) { + last_viewport = p_camera->get_viewport(); + _update_edit_axis(); + } + Ref k = p_event; if (k.is_valid() && k->is_pressed() && !k->is_echo()) { // Transform mode (toggle button): @@ -1360,8 +1425,6 @@ void GridMapEditor::edit(GridMap *p_gridmap) { _update_selection_transform(); _update_paste_indicator(); - spatial_editor = Object::cast_to(EditorNode::get_singleton()->get_editor_main_screen()->get_selected_plugin()); - if (!node) { set_process(false); for (int i = 0; i < 3; i++) { @@ -1377,6 +1440,7 @@ void GridMapEditor::edit(GridMap *p_gridmap) { update_palette(); _update_cursor_instance(); + _update_edit_axis(); set_process(true); @@ -1394,6 +1458,7 @@ void GridMapEditor::edit(GridMap *p_gridmap) { void GridMapEditor::update_grid() { grid_xform.origin.x -= 1; // Force update in hackish way. + Vector3::Axis edit_axis = _get_edit_axis(); grid_ofs[edit_axis] = edit_floor[edit_axis] * node->get_cell_size()[edit_axis]; edit_grid_xform.origin = grid_ofs; @@ -1501,6 +1566,12 @@ void GridMapEditor::_notification(int p_what) { _update_selection_transform(); _update_paste_indicator(); + + last_viewport = Node3DEditor::get_singleton()->get_editor_viewport(0)->get_viewport_node(); + for (uint32_t i = 0; i < Node3DEditor::VIEWPORTS_COUNT; i++) { + Node3DEditorViewport *viewport = Node3DEditor::get_singleton()->get_editor_viewport(i); + viewport->get_controller()->connect("view_state_changed", callable_mp(this, &GridMapEditor::_view_state_changed).bind(viewport)); + } } break; case NOTIFICATION_PROCESS: { @@ -1517,6 +1588,7 @@ void GridMapEditor::_notification(int p_what) { grid_xform = xf; _update_cursor_transform(); _update_selection_transform(); + _update_edit_axis(); } } break; @@ -1623,7 +1695,7 @@ void GridMapEditor::_floor_changed(float p_value) { return; } - edit_floor[edit_axis] = p_value; + edit_floor[_get_edit_axis()] = p_value; node->set_meta("_editor_floor_", Vector3(edit_floor[0], edit_floor[1], edit_floor[2])); update_grid(); _update_selection_transform(); @@ -1886,6 +1958,8 @@ GridMapEditor::GridMapEditor() { options->get_popup()->add_radio_check_shortcut(ED_GET_SHORTCUT("grid_map/edit_y_axis"), MENU_OPTION_Y_AXIS); options->get_popup()->add_radio_check_shortcut(ED_GET_SHORTCUT("grid_map/edit_z_axis"), MENU_OPTION_Z_AXIS); options->get_popup()->set_item_checked(options->get_popup()->get_item_index(MENU_OPTION_Y_AXIS), true); + options->get_popup()->add_check_item(TTRC("Allow Viewport Override"), MENU_OPTION_VIEWPORT_OVERRIDE); + options->get_popup()->set_item_checked(options->get_popup()->get_item_index(MENU_OPTION_VIEWPORT_OVERRIDE), true); options->get_popup()->add_separator(); // TRANSLATORS: This is a toggle to select after pasting the new content. options->get_popup()->add_shortcut(ED_GET_SHORTCUT("grid_map/clear_rotation"), MENU_OPTION_CURSOR_CLEAR_ROTATION); @@ -1939,7 +2013,6 @@ GridMapEditor::GridMapEditor() { info_message->set_anchors_and_offsets_preset(PRESET_FULL_RECT, PRESET_MODE_KEEP_SIZE, 8 * EDSCALE); mesh_library_palette->add_child(info_message); - edit_axis = Vector3::AXIS_Y; edit_floor[0] = -1; edit_floor[1] = -1; edit_floor[2] = -1; diff --git a/modules/gridmap/editor/grid_map_editor_plugin.h b/modules/gridmap/editor/grid_map_editor_plugin.h index 2f54b17fd8..f54e26418f 100644 --- a/modules/gridmap/editor/grid_map_editor_plugin.h +++ b/modules/gridmap/editor/grid_map_editor_plugin.h @@ -36,16 +36,17 @@ #include "editor/plugins/editor_plugin.h" #include "scene/gui/box_container.h" +class BaseButton; class Button; +class ButtonGroup; class ConfirmationDialog; +class EditorZoomWidget; class FilterLineEdit; class HSlider; class ItemList; class MenuButton; class Node3DEditorPlugin; -class ButtonGroup; -class EditorZoomWidget; -class BaseButton; +class Node3DEditorViewport; class SpinBox; class Tree; class TreeItem; @@ -122,10 +123,15 @@ class GridMapEditor : public EditorDock { Transform3D grid_xform; Transform3D edit_grid_xform; - Vector3::Axis edit_axis; + Vector3::Axis edit_axis_select = Vector3::AXIS_Y; int edit_floor[3]; + int edit_main_vp = 0; Vector3 grid_ofs; + bool allow_viewport_override = true; + Viewport *last_viewport = nullptr; + Vector3::Axis viewport_axis = edit_axis_select; + RID grid[3]; RID grid_instance[3]; RID cursor_mesh; @@ -195,6 +201,7 @@ class GridMapEditor : public EditorDock { MENU_OPTION_X_AXIS, MENU_OPTION_Y_AXIS, MENU_OPTION_Z_AXIS, + MENU_OPTION_VIEWPORT_OVERRIDE, MENU_OPTION_CURSOR_ROTATE_Y, MENU_OPTION_CURSOR_ROTATE_X, MENU_OPTION_CURSOR_ROTATE_Z, @@ -211,8 +218,6 @@ class GridMapEditor : public EditorDock { }; - Node3DEditorPlugin *spatial_editor = nullptr; - struct AreaDisplay { RID mesh; RID instance; @@ -254,6 +259,12 @@ class GridMapEditor : public EditorDock { bool _has_selection() const; Array _get_selected_cells() const; + void _update_edit_axis(); + Vector3::Axis _get_facing_axis(const Basis &p_grid_basis, const Vector3 &p_direction) const; + Vector3::Axis _get_edit_axis() const { return allow_viewport_override ? viewport_axis : edit_axis_select; } + + void _view_state_changed(Node3DEditorViewport *p_viewport); + String _get_cursor_coordinates() const; void _floor_changed(float p_value);