summaryrefslogtreecommitdiffstats
path: root/modules
diff options
context:
space:
mode:
Diffstat (limited to 'modules')
-rw-r--r--modules/enet/enet_connection.cpp2
-rw-r--r--modules/gdscript/doc_classes/@GDScript.xml14
-rw-r--r--modules/gdscript/doc_classes/GDScript.xml3
-rw-r--r--modules/gltf/doc_classes/GLTFState.xml2
-rw-r--r--modules/gltf/gltf_document.cpp6
-rw-r--r--modules/gltf/tests/test_gltf_extras.h2
-rw-r--r--modules/godot_physics_3d/godot_shape_3d.cpp6
-rw-r--r--modules/gridmap/editor/grid_map_editor_plugin.cpp587
-rw-r--r--modules/gridmap/editor/grid_map_editor_plugin.h38
-rw-r--r--modules/gridmap/grid_map.cpp8
-rw-r--r--modules/minimp3/resource_importer_mp3.cpp2
-rw-r--r--modules/minimp3/resource_importer_mp3.h2
-rw-r--r--modules/mono/csharp_script.cpp2
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/GeneratedSources/EventSignals_ScriptSignals.generated.cs4
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptSignalsGenerator.cs6
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs38
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantUtils.cs10
-rw-r--r--modules/multiplayer/scene_multiplayer.cpp6
-rw-r--r--modules/multiplayer/scene_rpc_interface.cpp12
-rw-r--r--modules/multiplayer/scene_rpc_interface.h2
-rw-r--r--modules/navigation/2d/nav_mesh_generator_2d.cpp206
-rw-r--r--modules/navigation/3d/nav_mesh_generator_3d.cpp19
-rw-r--r--modules/noise/doc_classes/FastNoiseLite.xml2
-rw-r--r--modules/openxr/extensions/openxr_composition_layer_extension.cpp2
-rw-r--r--modules/regex/doc_classes/RegEx.xml4
-rw-r--r--modules/theora/video_stream_theora.cpp2
-rw-r--r--modules/vorbis/resource_importer_ogg_vorbis.cpp2
-rw-r--r--modules/vorbis/resource_importer_ogg_vorbis.h2
-rw-r--r--modules/websocket/doc_classes/WebSocketPeer.xml4
-rw-r--r--modules/websocket/websocket_peer.cpp14
-rw-r--r--modules/websocket/websocket_peer.h4
-rw-r--r--modules/websocket/wsl_peer.cpp67
-rw-r--r--modules/websocket/wsl_peer.h2
33 files changed, 691 insertions, 391 deletions
diff --git a/modules/enet/enet_connection.cpp b/modules/enet/enet_connection.cpp
index 2ccfd5d326..9c9302a51c 100644
--- a/modules/enet/enet_connection.cpp
+++ b/modules/enet/enet_connection.cpp
@@ -113,7 +113,7 @@ Ref<ENetPacketPeer> ENetConnection::connect_to_host(const String &p_address, int
if (peer == nullptr) {
return nullptr;
}
- out = Ref<ENetPacketPeer>(memnew(ENetPacketPeer(peer)));
+ out.instantiate(peer);
peers.push_back(out);
return out;
}
diff --git a/modules/gdscript/doc_classes/@GDScript.xml b/modules/gdscript/doc_classes/@GDScript.xml
index ede4ce6617..0355119442 100644
--- a/modules/gdscript/doc_classes/@GDScript.xml
+++ b/modules/gdscript/doc_classes/@GDScript.xml
@@ -666,7 +666,19 @@
@export var car_label = "Speedy"
@export var car_number = 3
[/codeblock]
- [b]Note:[/b] Subgroups cannot be nested, they only provide one extra level of depth. Just like the next group ends the previous group, so do the subsequent subgroups.
+ [b]Note:[/b] Subgroups cannot be nested, but you can use the slash separator ([code]/[/code]) to achieve the desired effect:
+ [codeblock]
+ @export_group("Car Properties")
+ @export_subgroup("Wheels", "wheel_")
+ @export_subgroup("Wheels/Front", "front_wheel_")
+ @export var front_wheel_strength = 10
+ @export var front_wheel_mobility = 5
+ @export_subgroup("Wheels/Rear", "rear_wheel_")
+ @export var rear_wheel_strength = 8
+ @export var rear_wheel_mobility = 3
+ @export_subgroup("Wheels", "wheel_")
+ @export var wheel_material: PhysicsMaterial
+ [/codeblock]
</description>
</annotation>
<annotation name="@export_tool_button">
diff --git a/modules/gdscript/doc_classes/GDScript.xml b/modules/gdscript/doc_classes/GDScript.xml
index 5f7a7e2915..c3fa59dc23 100644
--- a/modules/gdscript/doc_classes/GDScript.xml
+++ b/modules/gdscript/doc_classes/GDScript.xml
@@ -16,11 +16,10 @@
<return type="Variant" />
<description>
Returns a new instance of the script.
- For example:
[codeblock]
var MyClass = load("myclass.gd")
var instance = MyClass.new()
- assert(instance.get_script() == MyClass)
+ print(instance.get_script() == MyClass) # Prints true
[/codeblock]
</description>
</method>
diff --git a/modules/gltf/doc_classes/GLTFState.xml b/modules/gltf/doc_classes/GLTFState.xml
index 376c3c89f5..c81c2f09f0 100644
--- a/modules/gltf/doc_classes/GLTFState.xml
+++ b/modules/gltf/doc_classes/GLTFState.xml
@@ -306,7 +306,7 @@
The binary buffer attached to a .glb file.
</member>
<member name="import_as_skeleton_bones" type="bool" setter="set_import_as_skeleton_bones" getter="get_import_as_skeleton_bones" default="false">
- True to force all GLTFNodes in the document to be bones of a single Skeleton3D godot node.
+ If [code]true[/code], forces all GLTFNodes in the document to be bones of a single [Skeleton3D] Godot node.
</member>
<member name="json" type="Dictionary" setter="set_json" getter="get_json" default="{}">
The original raw JSON document corresponding to this GLTFState.
diff --git a/modules/gltf/gltf_document.cpp b/modules/gltf/gltf_document.cpp
index aa482615da..7cac61304f 100644
--- a/modules/gltf/gltf_document.cpp
+++ b/modules/gltf/gltf_document.cpp
@@ -117,7 +117,7 @@ static Ref<ImporterMesh> _mesh_to_importer_mesh(Ref<Mesh> p_mesh) {
mat_name = mat->get_name();
} else {
// Assign default material when no material is assigned.
- mat = Ref<StandardMaterial3D>(memnew(StandardMaterial3D));
+ mat.instantiate();
}
importer_mesh->add_surface(p_mesh->surface_get_primitive_type(surface_i),
array, p_mesh->surface_get_blend_shape_arrays(surface_i), p_mesh->surface_get_lods(surface_i), mat,
@@ -5913,7 +5913,7 @@ void GLTFDocument::_convert_csg_shape_to_gltf(CSGShape3D *p_current, GLTFNodeInd
mat_name = mat->get_name();
} else {
// Assign default material when no material is assigned.
- mat = Ref<StandardMaterial3D>(memnew(StandardMaterial3D));
+ mat.instantiate();
}
mesh->add_surface(csg_mesh->surface_get_primitive_type(surface_i),
@@ -8560,7 +8560,7 @@ Error GLTFDocument::append_from_file(String p_path, Ref<GLTFState> p_state, uint
Error err;
Ref<FileAccess> file = FileAccess::open(p_path, FileAccess::READ, &err);
- ERR_FAIL_COND_V(err != OK, ERR_FILE_CANT_OPEN);
+ ERR_FAIL_COND_V_MSG(err != OK, err, vformat(R"(Can't open file at path "%s")", p_path));
ERR_FAIL_COND_V(file.is_null(), ERR_FILE_CANT_OPEN);
String base_path = p_base_path;
if (base_path.is_empty()) {
diff --git a/modules/gltf/tests/test_gltf_extras.h b/modules/gltf/tests/test_gltf_extras.h
index 37c8f6925c..73ef02e9f1 100644
--- a/modules/gltf/tests/test_gltf_extras.h
+++ b/modules/gltf/tests/test_gltf_extras.h
@@ -91,7 +91,7 @@ static Node *_gltf_export_then_import(Node *p_root, String &p_tempfilebase) {
options["gltf/naming_version"] = 1;
// Process gltf file, note that this generates `.scn` resource from the 2nd argument.
- err = import_scene->import(p_tempfilebase + ".gltf", p_tempfilebase, options, nullptr, nullptr, nullptr);
+ err = import_scene->import(0, p_tempfilebase + ".gltf", p_tempfilebase, options, nullptr, nullptr, nullptr);
CHECK_MESSAGE(err == OK, "GLTF import failed.");
ResourceImporterScene::remove_scene_importer(import_gltf);
diff --git a/modules/godot_physics_3d/godot_shape_3d.cpp b/modules/godot_physics_3d/godot_shape_3d.cpp
index a7cccc5cb8..4356ebe2f2 100644
--- a/modules/godot_physics_3d/godot_shape_3d.cpp
+++ b/modules/godot_physics_3d/godot_shape_3d.cpp
@@ -1996,7 +1996,11 @@ bool GodotHeightMapShape3D::intersect_segment(const Vector3 &p_begin, const Vect
Vector3 bounds_from = p_begin / BOUNDS_CHUNK_SIZE;
Vector3 bounds_to = p_end / BOUNDS_CHUNK_SIZE;
Vector3 bounds_offset = local_origin / BOUNDS_CHUNK_SIZE;
- return _intersect_grid_segment(_heightmap_chunk_cull_segment, bounds_from, bounds_to, bounds_grid_width, bounds_grid_depth, bounds_offset, r_point, r_normal);
+ // Plus 1 here to width and depth of the chunk because _intersect_grid_segment() is used by cell level as well,
+ // and in _intersect_grid_segment() the loop will exit 1 early because for cell point triangle lookup, it dose x + 1, z + 1 etc for the vertex.
+ int bounds_width = bounds_grid_width + 1;
+ int bounds_depth = bounds_grid_depth + 1;
+ return _intersect_grid_segment(_heightmap_chunk_cull_segment, bounds_from, bounds_to, bounds_width, bounds_depth, bounds_offset, r_point, r_normal);
}
}
diff --git a/modules/gridmap/editor/grid_map_editor_plugin.cpp b/modules/gridmap/editor/grid_map_editor_plugin.cpp
index bd2792d92a..caa7a79874 100644
--- a/modules/gridmap/editor/grid_map_editor_plugin.cpp
+++ b/modules/gridmap/editor/grid_map_editor_plugin.cpp
@@ -34,11 +34,14 @@
#include "core/input/input.h"
#include "core/os/keyboard.h"
+#include "editor/editor_command_palette.h"
#include "editor/editor_main_screen.h"
#include "editor/editor_node.h"
#include "editor/editor_settings.h"
#include "editor/editor_string_names.h"
#include "editor/editor_undo_redo_manager.h"
+#include "editor/gui/editor_bottom_panel.h"
+#include "editor/gui/editor_zoom_widget.h"
#include "editor/plugins/node_3d_editor_plugin.h"
#include "editor/themes/editor_scale.h"
#include "scene/3d/camera_3d.h"
@@ -84,16 +87,10 @@ void GridMapEditor::_menu_option(int p_option) {
}
if (edit_axis != new_axis) {
- int item1 = options->get_popup()->get_item_index(MENU_OPTION_NEXT_LEVEL);
- int item2 = options->get_popup()->get_item_index(MENU_OPTION_PREV_LEVEL);
if (edit_axis == Vector3::AXIS_Y) {
- options->get_popup()->set_item_text(item1, TTR("Next Plane"));
- options->get_popup()->set_item_text(item2, TTR("Previous Plane"));
- spin_box_label->set_text(TTR("Plane:"));
+ floor->set_tooltip_text("Change Grid Plane");
} else if (new_axis == Vector3::AXIS_Y) {
- options->get_popup()->set_item_text(item1, TTR("Next Floor"));
- options->get_popup()->set_item_text(item2, TTR("Previous Floor"));
- spin_box_label->set_text(TTR("Floor:"));
+ floor->set_tooltip_text("Change Grid Floor");
}
}
edit_axis = Vector3::Axis(new_axis);
@@ -251,14 +248,22 @@ void GridMapEditor::_menu_option(int p_option) {
void GridMapEditor::_update_cursor_transform() {
cursor_transform = Transform3D();
cursor_transform.origin = cursor_origin;
- cursor_transform.basis = node->get_basis_with_orthogonal_index(cursor_rot);
cursor_transform.basis *= node->get_cell_scale();
cursor_transform = node->get_global_transform() * cursor_transform;
- if (selected_palette >= 0) {
- if (node && !node->get_mesh_library().is_null()) {
+ if (mode_buttons_group->get_pressed_button() == paint_mode_button) {
+ // Rotation is only applied in paint mode, we don't want the cursor box to rotate otherwise.
+ cursor_transform.basis = node->get_basis_with_orthogonal_index(cursor_rot);
+ if (selected_palette >= 0 && node && node->get_mesh_library().is_valid()) {
cursor_transform *= node->get_mesh_library()->get_item_mesh_transform(selected_palette);
}
+ } else {
+ Transform3D xf;
+ xf.scale(node->get_cell_size());
+ xf.origin.x = node->get_center_x() ? -node->get_cell_size().x / 2 : 0;
+ xf.origin.y = node->get_center_y() ? -node->get_cell_size().y / 2 : 0;
+ xf.origin.z = node->get_center_z() ? -node->get_cell_size().z / 2 : 0;
+ cursor_transform *= xf;
}
if (cursor_instance.is_valid()) {
@@ -301,7 +306,7 @@ void GridMapEditor::_update_selection_transform() {
xf2.basis.scale(scale);
xf2.origin = position;
- RenderingServer::get_singleton()->instance_set_transform(selection_level_instance[i], xf2);
+ RenderingServer::get_singleton()->instance_set_transform(selection_level_instance[i], node->get_global_transform() * xf2);
}
}
}
@@ -336,25 +341,22 @@ void GridMapEditor::_set_selection(bool p_active, const Vector3 &p_begin, const
if (is_visible_in_tree()) {
_update_selection_transform();
}
-
- options->get_popup()->set_item_disabled(options->get_popup()->get_item_index(MENU_OPTION_SELECTION_CLEAR), !selection.active);
- options->get_popup()->set_item_disabled(options->get_popup()->get_item_index(MENU_OPTION_SELECTION_CUT), !selection.active);
- options->get_popup()->set_item_disabled(options->get_popup()->get_item_index(MENU_OPTION_SELECTION_DUPLICATE), !selection.active);
- options->get_popup()->set_item_disabled(options->get_popup()->get_item_index(MENU_OPTION_SELECTION_FILL), !selection.active);
}
bool GridMapEditor::do_input_action(Camera3D *p_camera, const Point2 &p_point, bool p_click) {
if (!spatial_editor) {
return false;
}
-
- if (selected_palette < 0 && input_action != INPUT_PICK && input_action != INPUT_SELECT && input_action != INPUT_PASTE) {
+ if (input_action == INPUT_TRANSFORM) {
+ return false;
+ }
+ if (selected_palette < 0 && input_action != INPUT_NONE && input_action != INPUT_PICK && input_action != INPUT_SELECT && input_action != INPUT_PASTE) {
return false;
}
if (mesh_library.is_null()) {
return false;
}
- if (input_action != INPUT_PICK && input_action != INPUT_SELECT && input_action != INPUT_PASTE && !mesh_library->has_item(selected_palette)) {
+ if (input_action != INPUT_NONE && input_action != INPUT_PICK && input_action != INPUT_SELECT && input_action != INPUT_PASTE && !mesh_library->has_item(selected_palette)) {
return false;
}
@@ -405,13 +407,17 @@ bool GridMapEditor::do_input_action(Camera3D *p_camera, const Point2 &p_point, b
cursor_origin = (Vector3(cell[0], cell[1], cell[2]) + Vector3(0.5 * node->get_center_x(), 0.5 * node->get_center_y(), 0.5 * node->get_center_z())) * node->get_cell_size();
cursor_visible = true;
- if (input_action == INPUT_SELECT || input_action == INPUT_PASTE) {
+ if (input_action == INPUT_PASTE) {
cursor_visible = false;
}
_update_cursor_transform();
}
+ if (input_action == INPUT_NONE) {
+ return false;
+ }
+
if (input_action == INPUT_PASTE) {
paste_indicator.current = Vector3i(cell[0], cell[1], cell[2]);
_update_paste_indicator();
@@ -604,7 +610,18 @@ void GridMapEditor::_do_paste() {
}
if (reselect) {
- undo_redo->add_do_method(this, "_set_selection", true, paste_indicator.begin + ofs, paste_indicator.end + ofs);
+ // We need to rotate the paste_indicator to find the selection begin and end:
+ Vector3 temp_end = rot.xform(paste_indicator.end - paste_indicator.begin) + paste_indicator.begin + ofs;
+ Vector3 temp_begin = paste_indicator.begin + ofs;
+ // _set_selection expects that selection_begin is the corner closer to the origin:
+ for (int i = 0; i < 3; ++i) {
+ if (temp_begin[i] > temp_end[i]) {
+ float p = temp_begin[i];
+ temp_begin[i] = temp_end[i];
+ temp_end[i] = p;
+ }
+ }
+ undo_redo->add_do_method(this, "_set_selection", true, temp_begin, temp_end);
undo_redo->add_undo_method(this, "_set_selection", selection.active, selection.begin, selection.end);
}
@@ -613,13 +630,93 @@ void GridMapEditor::_do_paste() {
_clear_clipboard_data();
}
+void GridMapEditor::_show_viewports_transform_gizmo(bool p_value) {
+ Dictionary new_state;
+ new_state["transform_gizmo"] = p_value;
+ for (uint32_t i = 0; i < Node3DEditor::VIEWPORTS_COUNT; i++) {
+ Node3DEditorViewport *viewport = Node3DEditor::get_singleton()->get_editor_viewport(i);
+ viewport->set_state(new_state);
+ }
+}
+
EditorPlugin::AfterGUIInput GridMapEditor::forward_spatial_input_event(Camera3D *p_camera, const Ref<InputEvent> &p_event) {
if (!node) {
return EditorPlugin::AFTER_GUI_INPUT_PASS;
}
- Ref<InputEventMouseButton> mb = p_event;
+ Ref<InputEventKey> k = p_event;
+ if (k.is_valid() && k->is_pressed() && !k->is_echo()) {
+ // If we are in Transform mode we pass the events to the 3D editor,
+ // but if the Transform mode shortcut is pressed again, we go back to Selection mode.
+ if (mode_buttons_group->get_pressed_button() == transform_mode_button) {
+ if (transform_mode_button->get_shortcut().is_valid() && transform_mode_button->get_shortcut()->matches_event(p_event)) {
+ select_mode_button->set_pressed(true);
+ accept_event();
+ return EditorPlugin::AFTER_GUI_INPUT_STOP;
+ }
+ return EditorPlugin::AFTER_GUI_INPUT_PASS;
+ }
+
+ for (BaseButton *b : viewport_shortcut_buttons) {
+ if (b->is_disabled()) {
+ continue;
+ }
+
+ if (b->get_shortcut().is_valid() && b->get_shortcut()->matches_event(p_event)) {
+ if (b->is_toggle_mode()) {
+ b->set_pressed(b->get_button_group().is_valid() || !b->is_pressed());
+ } else {
+ // Can't press a button without toggle mode, so just emit the signal directly.
+ b->emit_signal(SceneStringName(pressed));
+ }
+ accept_event();
+ return EditorPlugin::AFTER_GUI_INPUT_STOP;
+ }
+ }
+ }
+
+ if (k.is_valid() && k->is_pressed() && !k->is_echo()) {
+ if (k->get_keycode() == Key::ESCAPE) {
+ if (input_action == INPUT_PASTE) {
+ _clear_clipboard_data();
+ input_action = INPUT_NONE;
+ _update_paste_indicator();
+ return EditorPlugin::AFTER_GUI_INPUT_STOP;
+ } else if (selection.active) {
+ _set_selection(false);
+ return EditorPlugin::AFTER_GUI_INPUT_STOP;
+ } else {
+ input_action = INPUT_NONE;
+ update_palette();
+ _update_cursor_instance();
+ return EditorPlugin::AFTER_GUI_INPUT_STOP;
+ }
+ }
+
+ Ref<Shortcut> ed_shortcut = ED_GET_SHORTCUT("grid_map/previous_floor");
+ if (ed_shortcut.is_valid() && ed_shortcut->matches_event(p_event)) {
+ accept_event();
+ _menu_option(MENU_OPTION_PREV_LEVEL);
+ return EditorPlugin::AFTER_GUI_INPUT_STOP;
+ }
+ ed_shortcut = ED_GET_SHORTCUT("grid_map/next_floor");
+ if (ed_shortcut.is_valid() && ed_shortcut->matches_event(p_event)) {
+ accept_event();
+ _menu_option(MENU_OPTION_NEXT_LEVEL);
+ return EditorPlugin::AFTER_GUI_INPUT_STOP;
+ }
+ for (int i = 0; i < options->get_popup()->get_item_count(); ++i) {
+ const Ref<Shortcut> &shortcut = options->get_popup()->get_item_shortcut(i);
+ if (shortcut.is_valid() && shortcut->matches_event(p_event)) {
+ // Consume input to avoid conflicts with other plugins.
+ accept_event();
+ _menu_option(options->get_popup()->get_item_id(i));
+ return EditorPlugin::AFTER_GUI_INPUT_STOP;
+ }
+ }
+ }
+ Ref<InputEventMouseButton> mb = p_event;
if (mb.is_valid()) {
if (mb->get_button_index() == MouseButton::WHEEL_UP && (mb->is_command_or_control_pressed())) {
if (mb->is_pressed()) {
@@ -645,14 +742,17 @@ EditorPlugin::AfterGUIInput GridMapEditor::forward_spatial_input_event(Camera3D
input_action = INPUT_NONE;
_update_paste_indicator();
return EditorPlugin::AFTER_GUI_INPUT_STOP;
- } else if (mb->is_shift_pressed() && can_edit) {
+ } else if (mode_buttons_group->get_pressed_button() == select_mode_button && can_edit) {
input_action = INPUT_SELECT;
last_selection = selection;
- } else if (mb->is_command_or_control_pressed() && can_edit) {
+ } else if (mode_buttons_group->get_pressed_button() == pick_mode_button && can_edit) {
input_action = INPUT_PICK;
- } else {
+ } else if (mode_buttons_group->get_pressed_button() == paint_mode_button && can_edit) {
input_action = INPUT_PAINT;
set_items.clear();
+ } else if (mode_buttons_group->get_pressed_button() == erase_mode_button && can_edit) {
+ input_action = INPUT_ERASE;
+ set_items.clear();
}
} else if (mb->get_button_index() == MouseButton::RIGHT) {
if (input_action == INPUT_PASTE) {
@@ -663,9 +763,6 @@ EditorPlugin::AfterGUIInput GridMapEditor::forward_spatial_input_event(Camera3D
} else if (selection.active) {
_set_selection(false);
return EditorPlugin::AFTER_GUI_INPUT_STOP;
- } else {
- input_action = INPUT_ERASE;
- set_items.clear();
}
} else {
return EditorPlugin::AFTER_GUI_INPUT_PASS;
@@ -676,7 +773,7 @@ EditorPlugin::AfterGUIInput GridMapEditor::forward_spatial_input_event(Camera3D
}
return EditorPlugin::AFTER_GUI_INPUT_PASS;
} else {
- if ((mb->get_button_index() == MouseButton::RIGHT && input_action == INPUT_ERASE) || (mb->get_button_index() == MouseButton::LEFT && input_action == INPUT_PAINT)) {
+ if ((mb->get_button_index() == MouseButton::LEFT && input_action == INPUT_ERASE) || (mb->get_button_index() == MouseButton::LEFT && input_action == INPUT_PAINT)) {
if (set_items.size()) {
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
undo_redo->create_action(TTR("GridMap Paint"));
@@ -731,42 +828,6 @@ EditorPlugin::AfterGUIInput GridMapEditor::forward_spatial_input_event(Camera3D
return EditorPlugin::AFTER_GUI_INPUT_PASS;
}
- Ref<InputEventKey> k = p_event;
-
- if (k.is_valid()) {
- if (k->is_pressed()) {
- if (k->get_keycode() == Key::ESCAPE) {
- if (input_action == INPUT_PASTE) {
- _clear_clipboard_data();
- input_action = INPUT_NONE;
- _update_paste_indicator();
- return EditorPlugin::AFTER_GUI_INPUT_STOP;
- } else if (selection.active) {
- _set_selection(false);
- return EditorPlugin::AFTER_GUI_INPUT_STOP;
- } else {
- selected_palette = -1;
- mesh_library_palette->deselect_all();
- update_palette();
- _update_cursor_instance();
- return EditorPlugin::AFTER_GUI_INPUT_STOP;
- }
- }
-
- // Consume input to avoid conflicts with other plugins.
- if (k.is_valid() && k->is_pressed() && !k->is_echo()) {
- for (int i = 0; i < options->get_popup()->get_item_count(); ++i) {
- const Ref<Shortcut> &shortcut = options->get_popup()->get_item_shortcut(i);
- if (shortcut.is_valid() && shortcut->matches_event(p_event)) {
- accept_event();
- _menu_option(options->get_popup()->get_item_id(i));
- return EditorPlugin::AFTER_GUI_INPUT_STOP;
- }
- }
- }
- }
- }
-
Ref<InputEventPanGesture> pan_gesture = p_event;
if (pan_gesture.is_valid()) {
if (pan_gesture->is_alt_pressed() && pan_gesture->is_command_or_control_pressed()) {
@@ -833,11 +894,13 @@ void GridMapEditor::_mesh_library_palette_input(const Ref<InputEvent> &p_ie) {
// Zoom in/out using Ctrl + mouse wheel
if (mb.is_valid() && mb->is_pressed() && mb->is_command_or_control_pressed()) {
if (mb->is_pressed() && mb->get_button_index() == MouseButton::WHEEL_UP) {
- size_slider->set_value(size_slider->get_value() + 0.2);
+ zoom_widget->set_zoom(zoom_widget->get_zoom() + 0.2);
+ zoom_widget->emit_signal(SNAME("zoom_changed"), zoom_widget->get_zoom());
}
if (mb->is_pressed() && mb->get_button_index() == MouseButton::WHEEL_DOWN) {
- size_slider->set_value(size_slider->get_value() - 0.2);
+ zoom_widget->set_zoom(zoom_widget->get_zoom() - 0.2);
+ zoom_widget->emit_signal(SNAME("zoom_changed"), zoom_widget->get_zoom());
}
}
}
@@ -855,9 +918,9 @@ void GridMapEditor::update_palette() {
if (display_mode == DISPLAY_THUMBNAIL) {
mesh_library_palette->set_max_columns(0);
mesh_library_palette->set_icon_mode(ItemList::ICON_MODE_TOP);
- mesh_library_palette->set_fixed_column_width(min_size * MAX(size_slider->get_value(), 1.5));
+ mesh_library_palette->set_fixed_column_width(min_size * MAX(zoom_widget->get_zoom(), 1.5));
} else if (display_mode == DISPLAY_LIST) {
- mesh_library_palette->set_max_columns(1);
+ mesh_library_palette->set_max_columns(0);
mesh_library_palette->set_icon_mode(ItemList::ICON_MODE_LEFT);
mesh_library_palette->set_fixed_column_width(0);
}
@@ -938,6 +1001,11 @@ void GridMapEditor::_update_mesh_library() {
}
update_palette();
+ // Make sure we select the first tile as default possible.
+ if (mesh_library_palette->get_current() == -1 && mesh_library_palette->get_item_count() > 0) {
+ mesh_library_palette->set_current(0);
+ selected_palette = mesh_library_palette->get_item_metadata(0);
+ }
// Update the cursor and grid in case the library is changed or removed.
_update_cursor_instance();
update_grid();
@@ -1058,10 +1126,22 @@ void GridMapEditor::_draw_grids(const Vector3 &cell_size) {
}
void GridMapEditor::_update_theme() {
- options->set_button_icon(get_theme_icon(SNAME("GridMap"), EditorStringName(EditorIcons)));
+ transform_mode_button->set_button_icon(get_theme_icon(SNAME("ToolMove"), EditorStringName(EditorIcons)));
+ select_mode_button->set_button_icon(get_theme_icon(SNAME("ToolSelect"), EditorStringName(EditorIcons)));
+ erase_mode_button->set_button_icon(get_theme_icon(SNAME("Eraser"), EditorStringName(EditorIcons)));
+ paint_mode_button->set_button_icon(get_theme_icon(SNAME("Paint"), EditorStringName(EditorIcons)));
+ pick_mode_button->set_button_icon(get_theme_icon(SNAME("ColorPick"), EditorStringName(EditorIcons)));
+ fill_action_button->set_button_icon(get_theme_icon(SNAME("Bucket"), EditorStringName(EditorIcons)));
+ move_action_button->set_button_icon(get_theme_icon(SNAME("ActionCut"), EditorStringName(EditorIcons)));
+ duplicate_action_button->set_button_icon(get_theme_icon(SNAME("ActionCopy"), EditorStringName(EditorIcons)));
+ delete_action_button->set_button_icon(get_theme_icon(SNAME("Clear"), EditorStringName(EditorIcons)));
+ rotate_x_button->set_button_icon(get_theme_icon(SNAME("RotateLeft"), EditorStringName(EditorIcons)));
+ rotate_y_button->set_button_icon(get_theme_icon(SNAME("ToolRotate"), EditorStringName(EditorIcons)));
+ rotate_z_button->set_button_icon(get_theme_icon(SNAME("RotateRight"), EditorStringName(EditorIcons)));
search_box->set_right_icon(get_theme_icon(SNAME("Search"), EditorStringName(EditorIcons)));
mode_thumbnail->set_button_icon(get_theme_icon(SNAME("FileThumbnail"), EditorStringName(EditorIcons)));
mode_list->set_button_icon(get_theme_icon(SNAME("FileList"), EditorStringName(EditorIcons)));
+ options->set_button_icon(get_theme_icon(SNAME("Tools"), EditorStringName(EditorIcons)));
}
void GridMapEditor::_notification(int p_what) {
@@ -1076,6 +1156,9 @@ void GridMapEditor::_notification(int p_what) {
RenderingServer::get_singleton()->instance_set_layer_mask(selection_level_instance[i], 1 << Node3DEditorViewport::MISC_TOOL_LAYER);
}
+ cursor_instance = RenderingServer::get_singleton()->instance_create2(cursor_mesh, get_tree()->get_root()->get_world_3d()->get_scenario());
+ RenderingServer::get_singleton()->instance_set_layer_mask(cursor_instance, 1 << Node3DEditorViewport::MISC_TOOL_LAYER);
+ RenderingServer::get_singleton()->instance_set_visible(cursor_instance, false);
selection_instance = RenderingServer::get_singleton()->instance_create2(selection_mesh, get_tree()->get_root()->get_world_3d()->get_scenario());
RenderingServer::get_singleton()->instance_set_layer_mask(selection_instance, 1 << Node3DEditorViewport::MISC_TOOL_LAYER);
paste_instance = RenderingServer::get_singleton()->instance_create2(paste_mesh, get_tree()->get_root()->get_world_3d()->get_scenario());
@@ -1097,8 +1180,10 @@ void GridMapEditor::_notification(int p_what) {
RenderingServer::get_singleton()->free(selection_level_instance[i]);
}
+ RenderingServer::get_singleton()->free(cursor_instance);
RenderingServer::get_singleton()->free(selection_instance);
RenderingServer::get_singleton()->free(paste_instance);
+ cursor_instance = RID();
selection_instance = RID();
paste_instance = RID();
} break;
@@ -1144,15 +1229,32 @@ void GridMapEditor::_update_cursor_instance() {
}
cursor_instance = RID();
- if (selected_palette >= 0) {
- if (node && !node->get_mesh_library().is_null()) {
+ if (mode_buttons_group->get_pressed_button() == paint_mode_button) {
+ if (selected_palette >= 0 && node && node->get_mesh_library().is_valid()) {
Ref<Mesh> mesh = node->get_mesh_library()->get_item_mesh(selected_palette);
if (!mesh.is_null() && mesh->get_rid().is_valid()) {
cursor_instance = RenderingServer::get_singleton()->instance_create2(mesh->get_rid(), get_tree()->get_root()->get_world_3d()->get_scenario());
- RenderingServer::get_singleton()->instance_set_transform(cursor_instance, cursor_transform);
}
}
+ } else if (mode_buttons_group->get_pressed_button() == select_mode_button) {
+ cursor_inner_mat->set_albedo(Color(default_color, 0.2));
+ cursor_outer_mat->set_albedo(Color(default_color, 0.8));
+ cursor_instance = RenderingServer::get_singleton()->instance_create2(cursor_mesh, get_tree()->get_root()->get_world_3d()->get_scenario());
+ } else if (mode_buttons_group->get_pressed_button() == erase_mode_button) {
+ cursor_inner_mat->set_albedo(Color(erase_color, 0.2));
+ cursor_outer_mat->set_albedo(Color(erase_color, 0.8));
+ cursor_instance = RenderingServer::get_singleton()->instance_create2(cursor_mesh, get_tree()->get_root()->get_world_3d()->get_scenario());
+ } else if (mode_buttons_group->get_pressed_button() == pick_mode_button) {
+ cursor_inner_mat->set_albedo(Color(pick_color, 0.2));
+ cursor_outer_mat->set_albedo(Color(pick_color, 0.8));
+ cursor_instance = RenderingServer::get_singleton()->instance_create2(cursor_mesh, get_tree()->get_root()->get_world_3d()->get_scenario());
}
+ _update_cursor_transform();
+}
+
+void GridMapEditor::_on_tool_mode_changed() {
+ _show_viewports_transform_gizmo(mode_buttons_group->get_pressed_button() == transform_mode_button);
+ _update_cursor_instance();
}
void GridMapEditor::_item_selected_cbk(int idx) {
@@ -1182,80 +1284,26 @@ void GridMapEditor::_bind_methods() {
}
GridMapEditor::GridMapEditor() {
- ED_SHORTCUT("grid_map/previous_floor", TTR("Previous Floor"), Key::Q, true);
- ED_SHORTCUT("grid_map/next_floor", TTR("Next Floor"), Key::E, true);
- ED_SHORTCUT("grid_map/edit_x_axis", TTR("Edit X Axis"), Key::Z, true);
- ED_SHORTCUT("grid_map/edit_y_axis", TTR("Edit Y Axis"), Key::X, true);
- ED_SHORTCUT("grid_map/edit_z_axis", TTR("Edit Z Axis"), Key::C, true);
- ED_SHORTCUT("grid_map/cursor_rotate_x", TTR("Cursor Rotate X"), Key::A, true);
- ED_SHORTCUT("grid_map/cursor_rotate_y", TTR("Cursor Rotate Y"), Key::S, true);
- ED_SHORTCUT("grid_map/cursor_rotate_z", TTR("Cursor Rotate Z"), Key::D, true);
- ED_SHORTCUT("grid_map/cursor_back_rotate_x", TTR("Cursor Back Rotate X"), KeyModifierMask::SHIFT + Key::A, true);
- ED_SHORTCUT("grid_map/cursor_back_rotate_y", TTR("Cursor Back Rotate Y"), KeyModifierMask::SHIFT + Key::S, true);
- ED_SHORTCUT("grid_map/cursor_back_rotate_z", TTR("Cursor Back Rotate Z"), KeyModifierMask::SHIFT + Key::D, true);
- ED_SHORTCUT("grid_map/cursor_clear_rotation", TTR("Cursor Clear Rotation"), Key::W, true);
- ED_SHORTCUT("grid_map/paste_selects", TTR("Paste Selects"));
- ED_SHORTCUT("grid_map/duplicate_selection", TTR("Duplicate Selection"), KeyModifierMask::CTRL + Key::C);
- ED_SHORTCUT("grid_map/cut_selection", TTR("Cut Selection"), KeyModifierMask::CTRL + Key::X);
- ED_SHORTCUT("grid_map/clear_selection", TTR("Clear Selection"), Key::KEY_DELETE);
- ED_SHORTCUT("grid_map/fill_selection", TTR("Fill Selection"), KeyModifierMask::CTRL + Key::F);
-
- int mw = EDITOR_GET("editors/grid_map/palette_min_width");
- Control *ec = memnew(Control);
- ec->set_custom_minimum_size(Size2(mw, 0) * EDSCALE);
- add_child(ec);
-
- spatial_editor_hb = memnew(HBoxContainer);
- spatial_editor_hb->set_h_size_flags(SIZE_EXPAND_FILL);
- spatial_editor_hb->set_alignment(BoxContainer::ALIGNMENT_END);
- Node3DEditor::get_singleton()->add_control_to_menu_panel(spatial_editor_hb);
-
- spin_box_label = memnew(Label);
- spin_box_label->set_text(TTR("Floor:"));
- spatial_editor_hb->add_child(spin_box_label);
-
- floor = memnew(SpinBox);
- floor->set_min(-32767);
- floor->set_max(32767);
- floor->set_step(1);
- floor->get_line_edit()->add_theme_constant_override("minimum_character_width", 16);
-
- spatial_editor_hb->add_child(floor);
- floor->connect(SceneStringName(value_changed), callable_mp(this, &GridMapEditor::_floor_changed));
- floor->connect(SceneStringName(mouse_exited), callable_mp(this, &GridMapEditor::_floor_mouse_exited));
- floor->get_line_edit()->connect(SceneStringName(mouse_exited), callable_mp(this, &GridMapEditor::_floor_mouse_exited));
-
- spatial_editor_hb->add_child(memnew(VSeparator));
+ ED_SHORTCUT("grid_map/previous_floor", TTR("Previous Floor"), Key::KEY_1, true);
+ ED_SHORTCUT("grid_map/next_floor", TTR("Next Floor"), Key::KEY_3, true);
+ ED_SHORTCUT("grid_map/edit_x_axis", TTR("Edit X Axis"), KeyModifierMask::SHIFT + Key::Z, true);
+ ED_SHORTCUT("grid_map/edit_y_axis", TTR("Edit Y Axis"), KeyModifierMask::SHIFT + Key::X, true);
+ ED_SHORTCUT("grid_map/edit_z_axis", TTR("Edit Z Axis"), KeyModifierMask::SHIFT + Key::C, true);
+ ED_SHORTCUT("grid_map/keep_selected", TTR("Keep Selection"));
+ ED_SHORTCUT("grid_map/clear_rotation", TTR("Clear Rotation"));
options = memnew(MenuButton);
- spatial_editor_hb->add_child(options);
- spatial_editor_hb->hide();
-
- options->set_text(TTR("Grid Map"));
- options->get_popup()->add_shortcut(ED_GET_SHORTCUT("grid_map/previous_floor"), MENU_OPTION_PREV_LEVEL);
- options->get_popup()->add_shortcut(ED_GET_SHORTCUT("grid_map/next_floor"), MENU_OPTION_NEXT_LEVEL);
+ options->set_theme_type_variation("FlatButton");
options->get_popup()->add_separator();
options->get_popup()->add_radio_check_shortcut(ED_GET_SHORTCUT("grid_map/edit_x_axis"), MENU_OPTION_X_AXIS);
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_separator();
- options->get_popup()->add_shortcut(ED_GET_SHORTCUT("grid_map/cursor_rotate_x"), MENU_OPTION_CURSOR_ROTATE_X);
- options->get_popup()->add_shortcut(ED_GET_SHORTCUT("grid_map/cursor_rotate_y"), MENU_OPTION_CURSOR_ROTATE_Y);
- options->get_popup()->add_shortcut(ED_GET_SHORTCUT("grid_map/cursor_rotate_z"), MENU_OPTION_CURSOR_ROTATE_Z);
- options->get_popup()->add_shortcut(ED_GET_SHORTCUT("grid_map/cursor_back_rotate_x"), MENU_OPTION_CURSOR_BACK_ROTATE_X);
- options->get_popup()->add_shortcut(ED_GET_SHORTCUT("grid_map/cursor_back_rotate_y"), MENU_OPTION_CURSOR_BACK_ROTATE_Y);
- options->get_popup()->add_shortcut(ED_GET_SHORTCUT("grid_map/cursor_back_rotate_z"), MENU_OPTION_CURSOR_BACK_ROTATE_Z);
- options->get_popup()->add_shortcut(ED_GET_SHORTCUT("grid_map/cursor_clear_rotation"), MENU_OPTION_CURSOR_CLEAR_ROTATION);
- options->get_popup()->add_separator();
// TRANSLATORS: This is a toggle to select after pasting the new content.
- options->get_popup()->add_check_shortcut(ED_GET_SHORTCUT("grid_map/paste_selects"), MENU_OPTION_PASTE_SELECTS);
- options->get_popup()->add_separator();
- options->get_popup()->add_shortcut(ED_GET_SHORTCUT("grid_map/duplicate_selection"), MENU_OPTION_SELECTION_DUPLICATE);
- options->get_popup()->add_shortcut(ED_GET_SHORTCUT("grid_map/cut_selection"), MENU_OPTION_SELECTION_CUT);
- options->get_popup()->add_shortcut(ED_GET_SHORTCUT("grid_map/clear_selection"), MENU_OPTION_SELECTION_CLEAR);
- options->get_popup()->add_shortcut(ED_GET_SHORTCUT("grid_map/fill_selection"), MENU_OPTION_SELECTION_FILL);
-
+ options->get_popup()->add_shortcut(ED_GET_SHORTCUT("grid_map/clear_rotation"), MENU_OPTION_CURSOR_CLEAR_ROTATION);
+ options->get_popup()->add_check_shortcut(ED_GET_SHORTCUT("grid_map/keep_selected"), MENU_OPTION_PASTE_SELECTS);
+ options->get_popup()->set_item_checked(options->get_popup()->get_item_index(MENU_OPTION_PASTE_SELECTS), true);
options->get_popup()->add_separator();
options->get_popup()->add_item(TTR("Settings..."), MENU_OPTION_GRIDMAP_SETTINGS);
@@ -1275,40 +1323,180 @@ GridMapEditor::GridMapEditor() {
options->get_popup()->connect(SceneStringName(id_pressed), callable_mp(this, &GridMapEditor::_menu_option));
- HBoxContainer *hb = memnew(HBoxContainer);
- add_child(hb);
- hb->set_h_size_flags(SIZE_EXPAND_FILL);
+ toolbar = memnew(HBoxContainer);
+ add_child(toolbar);
+ toolbar->set_h_size_flags(SIZE_EXPAND_FILL);
+
+ HBoxContainer *mode_buttons = memnew(HBoxContainer);
+ toolbar->add_child(mode_buttons);
+ mode_buttons_group.instantiate();
+
+ transform_mode_button = memnew(Button);
+ transform_mode_button->set_theme_type_variation("FlatButton");
+ transform_mode_button->set_toggle_mode(true);
+ transform_mode_button->set_button_group(mode_buttons_group);
+ transform_mode_button->set_shortcut(ED_SHORTCUT("grid_map/transform_tool", TTR("Transform"), Key::T, true));
+ transform_mode_button->connect(SceneStringName(toggled),
+ callable_mp(this, &GridMapEditor::_on_tool_mode_changed).unbind(1));
+ mode_buttons->add_child(transform_mode_button);
+ viewport_shortcut_buttons.push_back(transform_mode_button);
+ VSeparator *vsep = memnew(VSeparator);
+ mode_buttons->add_child(vsep);
+
+ select_mode_button = memnew(Button);
+ select_mode_button->set_theme_type_variation("FlatButton");
+ select_mode_button->set_toggle_mode(true);
+ select_mode_button->set_button_group(mode_buttons_group);
+ select_mode_button->set_shortcut(ED_SHORTCUT("grid_map/selection_tool", TTR("Selection"), Key::Q, true));
+ select_mode_button->connect(SceneStringName(toggled),
+ callable_mp(this, &GridMapEditor::_on_tool_mode_changed).unbind(1));
+ mode_buttons->add_child(select_mode_button);
+ viewport_shortcut_buttons.push_back(select_mode_button);
+ select_mode_button->set_pressed(true);
+
+ erase_mode_button = memnew(Button);
+ erase_mode_button->set_theme_type_variation("FlatButton");
+ erase_mode_button->set_toggle_mode(true);
+ erase_mode_button->set_button_group(mode_buttons_group);
+ erase_mode_button->set_shortcut(ED_SHORTCUT("grid_map/erase_tool", TTR("Erase"), Key::W, true));
+ mode_buttons->add_child(erase_mode_button);
+ erase_mode_button->connect(SceneStringName(toggled),
+ callable_mp(this, &GridMapEditor::_on_tool_mode_changed).unbind(1));
+ viewport_shortcut_buttons.push_back(erase_mode_button);
+
+ paint_mode_button = memnew(Button);
+ paint_mode_button->set_theme_type_variation("FlatButton");
+ paint_mode_button->set_toggle_mode(true);
+ paint_mode_button->set_button_group(mode_buttons_group);
+ paint_mode_button->set_shortcut(ED_SHORTCUT("grid_map/paint_tool", TTR("Paint"), Key::E, true));
+ paint_mode_button->connect(SceneStringName(toggled),
+ callable_mp(this, &GridMapEditor::_on_tool_mode_changed).unbind(1));
+ mode_buttons->add_child(paint_mode_button);
+ viewport_shortcut_buttons.push_back(paint_mode_button);
+
+ pick_mode_button = memnew(Button);
+ pick_mode_button->set_theme_type_variation("FlatButton");
+ pick_mode_button->set_toggle_mode(true);
+ pick_mode_button->set_button_group(mode_buttons_group);
+ pick_mode_button->set_shortcut(ED_SHORTCUT("grid_map/pick_tool", TTR("Pick"), Key::R, true));
+ pick_mode_button->connect(SceneStringName(toggled),
+ callable_mp(this, &GridMapEditor::_on_tool_mode_changed).unbind(1));
+ mode_buttons->add_child(pick_mode_button);
+ viewport_shortcut_buttons.push_back(pick_mode_button);
+
+ vsep = memnew(VSeparator);
+ toolbar->add_child(vsep);
+
+ HBoxContainer *action_buttons = memnew(HBoxContainer);
+ toolbar->add_child(action_buttons);
+
+ fill_action_button = memnew(Button);
+ fill_action_button->set_theme_type_variation("FlatButton");
+ fill_action_button->set_shortcut(ED_SHORTCUT("grid_map/fill_tool", TTR("Fill"), Key::Z, true));
+ fill_action_button->connect(SceneStringName(pressed),
+ callable_mp(this, &GridMapEditor::_menu_option).bind(MENU_OPTION_SELECTION_FILL));
+ action_buttons->add_child(fill_action_button);
+
+ move_action_button = memnew(Button);
+ move_action_button->set_theme_type_variation("FlatButton");
+ move_action_button->set_shortcut(ED_SHORTCUT("grid_map/move_tool", TTR("Move"), Key::X, true));
+ move_action_button->connect(SceneStringName(pressed),
+ callable_mp(this, &GridMapEditor::_menu_option).bind(MENU_OPTION_SELECTION_CUT));
+ action_buttons->add_child(move_action_button);
+
+ duplicate_action_button = memnew(Button);
+ duplicate_action_button->set_theme_type_variation("FlatButton");
+ duplicate_action_button->set_shortcut(ED_SHORTCUT("grid_map/duplicate_tool", TTR("Duplicate"), Key::C, true));
+ duplicate_action_button->connect(SceneStringName(pressed),
+ callable_mp(this, &GridMapEditor::_menu_option).bind(MENU_OPTION_SELECTION_DUPLICATE));
+ action_buttons->add_child(duplicate_action_button);
+
+ delete_action_button = memnew(Button);
+ delete_action_button->set_theme_type_variation("FlatButton");
+ delete_action_button->set_shortcut(ED_SHORTCUT("grid_map/delete_tool", TTR("Delete"), Key::V, true));
+ delete_action_button->connect(SceneStringName(pressed),
+ callable_mp(this, &GridMapEditor::_menu_option).bind(MENU_OPTION_SELECTION_CLEAR));
+ action_buttons->add_child(delete_action_button);
+
+ vsep = memnew(VSeparator);
+ toolbar->add_child(vsep);
+
+ HBoxContainer *rotation_buttons = memnew(HBoxContainer);
+ toolbar->add_child(rotation_buttons);
+
+ rotate_x_button = memnew(Button);
+ rotate_x_button->set_theme_type_variation("FlatButton");
+ rotate_x_button->set_shortcut(ED_SHORTCUT("grid_map/cursor_rotate_x", TTR("Cursor Rotate X"), Key::A, true));
+ rotate_x_button->connect(SceneStringName(pressed),
+ callable_mp(this, &GridMapEditor::_menu_option).bind(MENU_OPTION_CURSOR_ROTATE_X));
+ rotation_buttons->add_child(rotate_x_button);
+
+ rotate_y_button = memnew(Button);
+ rotate_y_button->set_theme_type_variation("FlatButton");
+ rotate_y_button->set_shortcut(ED_SHORTCUT("grid_map/cursor_rotate_y", TTR("Cursor Rotate Y"), Key::S, true));
+ rotate_y_button->connect(SceneStringName(pressed),
+ callable_mp(this, &GridMapEditor::_menu_option).bind(MENU_OPTION_CURSOR_ROTATE_Y));
+ rotation_buttons->add_child(rotate_y_button);
+
+ rotate_z_button = memnew(Button);
+ rotate_z_button->set_theme_type_variation("FlatButton");
+ rotate_z_button->set_shortcut(ED_SHORTCUT("grid_map/cursor_rotate_z", TTR("Cursor Rotate Z"), Key::D, true));
+ rotate_z_button->connect(SceneStringName(pressed),
+ callable_mp(this, &GridMapEditor::_menu_option).bind(MENU_OPTION_CURSOR_ROTATE_Z));
+ rotation_buttons->add_child(rotate_z_button);
+
+ // Wide empty separation control. (like BoxContainer::add_spacer())
+ Control *c = memnew(Control);
+ c->set_mouse_filter(MOUSE_FILTER_PASS);
+ c->set_h_size_flags(SIZE_EXPAND_FILL);
+ toolbar->add_child(c);
+
+ floor = memnew(SpinBox);
+ floor->set_min(-32767);
+ floor->set_max(32767);
+ floor->set_step(1);
+ floor->set_tooltip_text(
+ TTR(vformat("Change Grid Floor:\nPrevious Plane (%s)\nNext Plane (%s)",
+ ED_GET_SHORTCUT("grid_map/previous_floor")->get_as_text(),
+ ED_GET_SHORTCUT("grid_map/next_floor")->get_as_text())));
+ toolbar->add_child(floor);
+ floor->get_line_edit()->add_theme_constant_override("minimum_character_width", 2);
+ floor->get_line_edit()->set_context_menu_enabled(false);
+ floor->connect(SceneStringName(value_changed), callable_mp(this, &GridMapEditor::_floor_changed));
+ floor->connect(SceneStringName(mouse_exited), callable_mp(this, &GridMapEditor::_floor_mouse_exited));
+ floor->get_line_edit()->connect(SceneStringName(mouse_exited), callable_mp(this, &GridMapEditor::_floor_mouse_exited));
search_box = memnew(LineEdit);
- search_box->set_h_size_flags(SIZE_EXPAND_FILL);
+ search_box->add_theme_constant_override("minimum_character_width", 10);
search_box->set_placeholder(TTR("Filter Meshes"));
search_box->set_clear_button_enabled(true);
- hb->add_child(search_box);
+ toolbar->add_child(search_box);
search_box->connect(SceneStringName(text_changed), callable_mp(this, &GridMapEditor::_text_changed));
search_box->connect(SceneStringName(gui_input), callable_mp(this, &GridMapEditor::_sbox_input));
+ zoom_widget = memnew(EditorZoomWidget);
+ toolbar->add_child(zoom_widget);
+ zoom_widget->setup_zoom_limits(0.2, 4);
+ zoom_widget->set_zoom(1.0);
+ zoom_widget->set_anchors_and_offsets_preset(Control::PRESET_TOP_LEFT, Control::PRESET_MODE_MINSIZE, 2 * EDSCALE);
+ zoom_widget->connect("zoom_changed", callable_mp(this, &GridMapEditor::_icon_size_changed));
+ zoom_widget->set_shortcut_context(this);
+
mode_thumbnail = memnew(Button);
mode_thumbnail->set_theme_type_variation("FlatButton");
mode_thumbnail->set_toggle_mode(true);
mode_thumbnail->set_pressed(true);
- hb->add_child(mode_thumbnail);
+ toolbar->add_child(mode_thumbnail);
mode_thumbnail->connect(SceneStringName(pressed), callable_mp(this, &GridMapEditor::_set_display_mode).bind(DISPLAY_THUMBNAIL));
mode_list = memnew(Button);
mode_list->set_theme_type_variation("FlatButton");
mode_list->set_toggle_mode(true);
mode_list->set_pressed(false);
- hb->add_child(mode_list);
+ toolbar->add_child(mode_list);
mode_list->connect(SceneStringName(pressed), callable_mp(this, &GridMapEditor::_set_display_mode).bind(DISPLAY_LIST));
- size_slider = memnew(HSlider);
- size_slider->set_h_size_flags(SIZE_EXPAND_FILL);
- size_slider->set_min(0.2f);
- size_slider->set_max(4.0f);
- size_slider->set_step(0.1f);
- size_slider->set_value(1.0f);
- size_slider->connect(SceneStringName(value_changed), callable_mp(this, &GridMapEditor::_icon_size_changed));
- add_child(size_slider);
+ toolbar->add_child(options);
mesh_library_palette = memnew(ItemList);
mesh_library_palette->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
@@ -1330,6 +1518,7 @@ GridMapEditor::GridMapEditor() {
edit_floor[1] = -1;
edit_floor[2] = -1;
+ cursor_mesh = RenderingServer::get_singleton()->mesh_create();
selection_mesh = RenderingServer::get_singleton()->mesh_create();
paste_mesh = RenderingServer::get_singleton()->mesh_create();
@@ -1405,20 +1594,32 @@ GridMapEditor::GridMapEditor() {
Array d;
d.resize(RS::ARRAY_MAX);
+ default_color = Color(0.0, 0.565, 1.0); // blue 0.7, 0.7, 1.0
+ erase_color = Color(1.0, 0.2, 0.2); // red
+ pick_color = Color(1, 0.7, 0); // orange/yellow
+
+ cursor_inner_mat.instantiate();
+ cursor_inner_mat->set_albedo(Color(default_color, 0.2));
+ cursor_inner_mat->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED);
+ cursor_inner_mat->set_flag(StandardMaterial3D::FLAG_DISABLE_FOG, true);
+ cursor_inner_mat->set_transparency(StandardMaterial3D::TRANSPARENCY_ALPHA);
+
+ cursor_outer_mat.instantiate();
+ cursor_outer_mat->set_albedo(Color(default_color, 0.8));
+ cursor_outer_mat->set_on_top_of_alpha();
+ cursor_outer_mat->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED);
+ cursor_outer_mat->set_transparency(StandardMaterial3D::TRANSPARENCY_ALPHA);
+ cursor_outer_mat->set_flag(StandardMaterial3D::FLAG_DISABLE_FOG, true);
+
inner_mat.instantiate();
- inner_mat->set_albedo(Color(0.7, 0.7, 1.0, 0.2));
+ inner_mat->set_albedo(Color(default_color, 0.2));
inner_mat->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED);
inner_mat->set_flag(StandardMaterial3D::FLAG_DISABLE_FOG, true);
inner_mat->set_transparency(StandardMaterial3D::TRANSPARENCY_ALPHA);
- d[RS::ARRAY_VERTEX] = triangles;
- RenderingServer::get_singleton()->mesh_add_surface_from_arrays(selection_mesh, RS::PRIMITIVE_TRIANGLES, d);
- RenderingServer::get_singleton()->mesh_surface_set_material(selection_mesh, 0, inner_mat->get_rid());
-
outer_mat.instantiate();
- outer_mat->set_albedo(Color(0.7, 0.7, 1.0, 0.8));
+ outer_mat->set_albedo(Color(default_color, 0.8));
outer_mat->set_on_top_of_alpha();
-
outer_mat->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED);
outer_mat->set_transparency(StandardMaterial3D::TRANSPARENCY_ALPHA);
outer_mat->set_flag(StandardMaterial3D::FLAG_DISABLE_FOG, true);
@@ -1429,6 +1630,18 @@ GridMapEditor::GridMapEditor() {
selection_floor_mat->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED);
selection_floor_mat->set_flag(StandardMaterial3D::FLAG_DISABLE_FOG, true);
+ d[RS::ARRAY_VERTEX] = triangles;
+ RenderingServer::get_singleton()->mesh_add_surface_from_arrays(cursor_mesh, RS::PRIMITIVE_TRIANGLES, d);
+ RenderingServer::get_singleton()->mesh_surface_set_material(cursor_mesh, 0, cursor_inner_mat->get_rid());
+
+ d[RS::ARRAY_VERTEX] = lines;
+ RenderingServer::get_singleton()->mesh_add_surface_from_arrays(cursor_mesh, RS::PRIMITIVE_LINES, d);
+ RenderingServer::get_singleton()->mesh_surface_set_material(cursor_mesh, 1, cursor_outer_mat->get_rid());
+
+ d[RS::ARRAY_VERTEX] = triangles;
+ RenderingServer::get_singleton()->mesh_add_surface_from_arrays(selection_mesh, RS::PRIMITIVE_TRIANGLES, d);
+ RenderingServer::get_singleton()->mesh_surface_set_material(selection_mesh, 0, inner_mat->get_rid());
+
d[RS::ARRAY_VERTEX] = lines;
RenderingServer::get_singleton()->mesh_add_surface_from_arrays(selection_mesh, RS::PRIMITIVE_LINES, d);
RenderingServer::get_singleton()->mesh_surface_set_material(selection_mesh, 1, outer_mat->get_rid());
@@ -1471,9 +1684,6 @@ GridMapEditor::~GridMapEditor() {
if (grid_instance[i].is_valid()) {
RenderingServer::get_singleton()->free(grid_instance[i]);
}
- if (cursor_instance.is_valid()) {
- RenderingServer::get_singleton()->free(cursor_instance);
- }
if (selection_level_instance[i].is_valid()) {
RenderingServer::get_singleton()->free(selection_level_instance[i]);
}
@@ -1482,6 +1692,11 @@ GridMapEditor::~GridMapEditor() {
}
}
+ RenderingServer::get_singleton()->free(cursor_mesh);
+ if (cursor_instance.is_valid()) {
+ RenderingServer::get_singleton()->free(cursor_instance);
+ }
+
RenderingServer::get_singleton()->free(selection_mesh);
if (selection_instance.is_valid()) {
RenderingServer::get_singleton()->free(selection_instance);
@@ -1493,24 +1708,6 @@ GridMapEditor::~GridMapEditor() {
}
}
-void GridMapEditorPlugin::_notification(int p_what) {
- switch (p_what) {
- case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: {
- if (!EditorSettings::get_singleton()->check_changed_settings_in_group("editors/grid_map")) {
- break;
- }
- switch ((int)EDITOR_GET("editors/grid_map/editor_side")) {
- case 0: { // Left.
- Node3DEditor::get_singleton()->move_control_to_left_panel(grid_map_editor);
- } break;
- case 1: { // Right.
- Node3DEditor::get_singleton()->move_control_to_right_panel(grid_map_editor);
- } break;
- }
- } break;
- }
-}
-
void GridMapEditorPlugin::edit(Object *p_object) {
grid_map_editor->edit(Object::cast_to<GridMap>(p_object));
}
@@ -1521,27 +1718,29 @@ bool GridMapEditorPlugin::handles(Object *p_object) const {
void GridMapEditorPlugin::make_visible(bool p_visible) {
if (p_visible) {
- grid_map_editor->show();
- grid_map_editor->spatial_editor_hb->show();
+ grid_map_editor->_on_tool_mode_changed();
+ panel_button->show();
+ EditorNode::get_bottom_panel()->make_item_visible(grid_map_editor);
grid_map_editor->set_process(true);
} else {
- grid_map_editor->spatial_editor_hb->hide();
- grid_map_editor->hide();
+ grid_map_editor->_show_viewports_transform_gizmo(true);
+ panel_button->hide();
+ if (grid_map_editor->is_visible_in_tree()) {
+ EditorNode::get_bottom_panel()->hide_bottom_panel();
+ }
grid_map_editor->set_process(false);
}
}
GridMapEditorPlugin::GridMapEditorPlugin() {
grid_map_editor = memnew(GridMapEditor);
- switch ((int)EDITOR_GET("editors/grid_map/editor_side")) {
- case 0: { // Left.
- Node3DEditor::get_singleton()->add_control_to_left_panel(grid_map_editor);
- } break;
- case 1: { // Right.
- Node3DEditor::get_singleton()->add_control_to_right_panel(grid_map_editor);
- } break;
- }
+ grid_map_editor->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+ grid_map_editor->set_v_size_flags(Control::SIZE_EXPAND_FILL);
+ grid_map_editor->set_custom_minimum_size(Size2(0, 200) * EDSCALE);
grid_map_editor->hide();
+
+ panel_button = EditorNode::get_bottom_panel()->add_item(TTR("GridMap"), grid_map_editor, ED_SHORTCUT_AND_COMMAND("bottom_panels/toggle_grid_map_bottom_panel", TTR("Toggle GridMap Bottom Panel")));
+ panel_button->hide();
}
GridMapEditorPlugin::~GridMapEditorPlugin() {
diff --git a/modules/gridmap/editor/grid_map_editor_plugin.h b/modules/gridmap/editor/grid_map_editor_plugin.h
index 4294c93c93..2d43a5c830 100644
--- a/modules/gridmap/editor/grid_map_editor_plugin.h
+++ b/modules/gridmap/editor/grid_map_editor_plugin.h
@@ -44,6 +44,9 @@
class ConfirmationDialog;
class MenuButton;
class Node3DEditorPlugin;
+class ButtonGroup;
+class EditorZoomWidget;
+class BaseButton;
class GridMapEditor : public VBoxContainer {
GDCLASS(GridMapEditor, VBoxContainer);
@@ -54,6 +57,7 @@ class GridMapEditor : public VBoxContainer {
enum InputAction {
INPUT_NONE,
+ INPUT_TRANSFORM,
INPUT_PAINT,
INPUT_ERASE,
INPUT_PICK,
@@ -71,11 +75,31 @@ class GridMapEditor : public VBoxContainer {
MenuButton *options = nullptr;
SpinBox *floor = nullptr;
double accumulated_floor_delta = 0.0;
+
+ HBoxContainer *toolbar = nullptr;
+ List<BaseButton *> viewport_shortcut_buttons;
+ Ref<ButtonGroup> mode_buttons_group;
+ // mode
+ Button *transform_mode_button = nullptr;
+ Button *select_mode_button = nullptr;
+ Button *erase_mode_button = nullptr;
+ Button *paint_mode_button = nullptr;
+ Button *pick_mode_button = nullptr;
+ // action
+ Button *fill_action_button = nullptr;
+ Button *move_action_button = nullptr;
+ Button *duplicate_action_button = nullptr;
+ Button *delete_action_button = nullptr;
+ // rotation
+ Button *rotate_x_button = nullptr;
+ Button *rotate_y_button = nullptr;
+ Button *rotate_z_button = nullptr;
+
+ EditorZoomWidget *zoom_widget = nullptr;
Button *mode_thumbnail = nullptr;
Button *mode_list = nullptr;
LineEdit *search_box = nullptr;
HSlider *size_slider = nullptr;
- HBoxContainer *spatial_editor_hb = nullptr;
ConfirmationDialog *settings_dialog = nullptr;
VBoxContainer *settings_vbc = nullptr;
SpinBox *settings_pick_distance = nullptr;
@@ -102,6 +126,7 @@ class GridMapEditor : public VBoxContainer {
RID grid[3];
RID grid_instance[3];
+ RID cursor_mesh;
RID cursor_instance;
RID selection_mesh;
RID selection_instance;
@@ -119,7 +144,12 @@ class GridMapEditor : public VBoxContainer {
List<ClipboardItem> clipboard_items;
+ Color default_color;
+ Color erase_color;
+ Color pick_color;
Ref<StandardMaterial3D> indicator_mat;
+ Ref<StandardMaterial3D> cursor_inner_mat;
+ Ref<StandardMaterial3D> cursor_outer_mat;
Ref<StandardMaterial3D> inner_mat;
Ref<StandardMaterial3D> outer_mat;
Ref<StandardMaterial3D> selection_floor_mat;
@@ -196,6 +226,7 @@ class GridMapEditor : public VBoxContainer {
void _item_selected_cbk(int idx);
void _update_cursor_transform();
void _update_cursor_instance();
+ void _on_tool_mode_changed();
void _update_theme();
void _text_changed(const String &p_text);
@@ -208,6 +239,7 @@ class GridMapEditor : public VBoxContainer {
void _set_clipboard_data();
void _update_paste_indicator();
void _do_paste();
+ void _show_viewports_transform_gizmo(bool p_value);
void _update_selection_transform();
void _validate_selection();
void _set_selection(bool p_active, const Vector3 &p_begin = Vector3(), const Vector3 &p_end = Vector3());
@@ -238,9 +270,7 @@ class GridMapEditorPlugin : public EditorPlugin {
GDCLASS(GridMapEditorPlugin, EditorPlugin);
GridMapEditor *grid_map_editor = nullptr;
-
-protected:
- void _notification(int p_what);
+ Button *panel_button = nullptr;
public:
virtual EditorPlugin::AfterGUIInput forward_3d_gui_input(Camera3D *p_camera, const Ref<InputEvent> &p_event) override { return grid_map_editor->forward_spatial_input_event(p_camera, p_event); }
diff --git a/modules/gridmap/grid_map.cpp b/modules/gridmap/grid_map.cpp
index 71171be3f1..0588ba034a 100644
--- a/modules/gridmap/grid_map.cpp
+++ b/modules/gridmap/grid_map.cpp
@@ -801,8 +801,8 @@ void GridMap::_octant_enter_world(const OctantKey &p_key) {
if (!g.navigation_debug_edge_connections_instance.is_valid()) {
g.navigation_debug_edge_connections_instance = RenderingServer::get_singleton()->instance_create();
}
- if (!g.navigation_debug_edge_connections_mesh.is_valid()) {
- g.navigation_debug_edge_connections_mesh = Ref<ArrayMesh>(memnew(ArrayMesh));
+ if (g.navigation_debug_edge_connections_mesh.is_null()) {
+ g.navigation_debug_edge_connections_mesh.instantiate();
}
_update_octant_navigation_debug_edge_connections_mesh(p_key);
@@ -1386,8 +1386,8 @@ void GridMap::_update_octant_navigation_debug_edge_connections_mesh(const Octant
g.navigation_debug_edge_connections_instance = RenderingServer::get_singleton()->instance_create();
}
- if (!g.navigation_debug_edge_connections_mesh.is_valid()) {
- g.navigation_debug_edge_connections_mesh = Ref<ArrayMesh>(memnew(ArrayMesh));
+ if (g.navigation_debug_edge_connections_mesh.is_null()) {
+ g.navigation_debug_edge_connections_mesh.instantiate();
}
g.navigation_debug_edge_connections_mesh->clear_surfaces();
diff --git a/modules/minimp3/resource_importer_mp3.cpp b/modules/minimp3/resource_importer_mp3.cpp
index e4b54ef050..f1f0a771ad 100644
--- a/modules/minimp3/resource_importer_mp3.cpp
+++ b/modules/minimp3/resource_importer_mp3.cpp
@@ -115,7 +115,7 @@ Ref<AudioStreamMP3> ResourceImporterMP3::import_mp3(const String &p_path) {
return mp3_stream;
}
-Error ResourceImporterMP3::import(const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files, Variant *r_metadata) {
+Error ResourceImporterMP3::import(ResourceUID::ID p_source_id, const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files, Variant *r_metadata) {
bool loop = p_options["loop"];
float loop_offset = p_options["loop_offset"];
double bpm = p_options["bpm"];
diff --git a/modules/minimp3/resource_importer_mp3.h b/modules/minimp3/resource_importer_mp3.h
index 037756328f..35cc761eb4 100644
--- a/modules/minimp3/resource_importer_mp3.h
+++ b/modules/minimp3/resource_importer_mp3.h
@@ -57,7 +57,7 @@ public:
#endif
static Ref<AudioStreamMP3> import_mp3(const String &p_path);
- virtual Error import(const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = nullptr, Variant *r_metadata = nullptr) override;
+ virtual Error import(ResourceUID::ID p_source_id, const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = nullptr, Variant *r_metadata = nullptr) override;
virtual bool can_import_threaded() const override { return true; }
diff --git a/modules/mono/csharp_script.cpp b/modules/mono/csharp_script.cpp
index 6ae9ce56c6..380b401683 100644
--- a/modules/mono/csharp_script.cpp
+++ b/modules/mono/csharp_script.cpp
@@ -2826,7 +2826,7 @@ Ref<Resource> ResourceFormatLoaderCSharpScript::load(const String &p_path, const
GDMonoCache::managed_callbacks.ScriptManagerBridge_GetOrCreateScriptBridgeForPath(&p_path, &scr);
ERR_FAIL_COND_V_MSG(scr.is_null(), Ref<Resource>(), "Could not create C# script '" + real_path + "'.");
} else {
- scr = Ref<CSharpScript>(memnew(CSharpScript));
+ scr.instantiate();
}
#if defined(DEBUG_ENABLED) || defined(TOOLS_ENABLED)
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/GeneratedSources/EventSignals_ScriptSignals.generated.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/GeneratedSources/EventSignals_ScriptSignals.generated.cs
index f54058b0d9..5af859c06b 100644
--- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/GeneratedSources/EventSignals_ScriptSignals.generated.cs
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/GeneratedSources/EventSignals_ScriptSignals.generated.cs
@@ -32,9 +32,9 @@ partial class EventSignals
add => backing_MySignal += value;
remove => backing_MySignal -= value;
}
- protected void EmitSignalMySignal(string str, int num)
+ protected void EmitSignalMySignal(string @str, int @num)
{
- EmitSignal(SignalName.MySignal, str, num);
+ EmitSignal(SignalName.MySignal, @str, @num);
}
/// <inheritdoc/>
[global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptSignalsGenerator.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptSignalsGenerator.cs
index 702c50d461..c7a7415851 100644
--- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptSignalsGenerator.cs
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptSignalsGenerator.cs
@@ -295,7 +295,7 @@ namespace Godot.SourceGenerators
for (int i = 0; i < paramCount; i++)
{
var paramSymbol = invokeMethodSymbol.Parameters[i];
- source.Append($"{paramSymbol.Type.FullQualifiedNameIncludeGlobal()} {paramSymbol.Name}");
+ source.Append($"{paramSymbol.Type.FullQualifiedNameIncludeGlobal()} @{paramSymbol.Name}");
if (i < paramCount - 1)
{
source.Append(", ");
@@ -310,11 +310,11 @@ namespace Godot.SourceGenerators
if (paramSymbol.Type.TypeKind == TypeKind.Enum)
{
var underlyingType = ((INamedTypeSymbol)paramSymbol.Type).EnumUnderlyingType;
- source.Append($", ({underlyingType.FullQualifiedNameIncludeGlobal()}){paramSymbol.Name}");
+ source.Append($", ({underlyingType.FullQualifiedNameIncludeGlobal()})@{paramSymbol.Name}");
continue;
}
- source.Append($", {paramSymbol.Name}");
+ source.Append($", @{paramSymbol.Name}");
}
source.Append(");\n");
source.Append(" }\n");
diff --git a/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs b/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs
index 788b46ab9a..74e04b46a1 100644
--- a/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs
@@ -177,7 +177,7 @@ namespace GodotTools
private static readonly string[] VsCodeNames =
{
- "code", "code-oss", "vscode", "vscode-oss", "visual-studio-code", "visual-studio-code-oss"
+ "code", "code-oss", "vscode", "vscode-oss", "visual-studio-code", "visual-studio-code-oss", "codium"
};
[UsedImplicitly]
@@ -330,7 +330,7 @@ namespace GodotTools
args.Add("-b");
args.Add(vscodeBundleId);
- // The reusing of existing windows made by the 'open' command might not choose a wubdiw that is
+ // The reusing of existing windows made by the 'open' command might not choose a window that is
// editing our folder. It's better to ask for a new window and let VSCode do the window management.
args.Add("-n");
@@ -339,6 +339,28 @@ namespace GodotTools
args.Add("--args");
}
+
+ // Try VSCodium as a fallback if Visual Studio Code can't be found.
+ if (!macOSAppBundleInstalled)
+ {
+ const string VscodiumBundleId = "com.vscodium.codium";
+ macOSAppBundleInstalled = Internal.IsMacOSAppBundleInstalled(VscodiumBundleId);
+
+ if (macOSAppBundleInstalled)
+ {
+ args.Add("-b");
+ args.Add(VscodiumBundleId);
+
+ // The reusing of existing windows made by the 'open' command might not choose a window that is
+ // editing our folder. It's better to ask for a new window and let VSCode do the window management.
+ args.Add("-n");
+
+ // The open process must wait until the application finishes (which is instant in VSCode's case)
+ args.Add("--wait-apps");
+
+ args.Add("--args");
+ }
+ }
}
args.Add(Path.GetDirectoryName(GodotSharpDirs.ProjectSlnPath)!);
@@ -361,7 +383,7 @@ namespace GodotTools
{
if (!macOSAppBundleInstalled && string.IsNullOrEmpty(_vsCodePath))
{
- GD.PushError("Cannot find code editor: VSCode");
+ GD.PushError("Cannot find code editor: Visual Studio Code or VSCodium");
return Error.FileNotFound;
}
@@ -371,7 +393,7 @@ namespace GodotTools
{
if (string.IsNullOrEmpty(_vsCodePath))
{
- GD.PushError("Cannot find code editor: VSCode");
+ GD.PushError("Cannot find code editor: Visual Studio Code or VSCodium");
return Error.FileNotFound;
}
@@ -384,7 +406,7 @@ namespace GodotTools
}
catch (Exception e)
{
- GD.PushError($"Error when trying to run code editor: VSCode. Exception message: '{e.Message}'");
+ GD.PushError($"Error when trying to run code editor: Visual Studio Code or VSCodium. Exception message: '{e.Message}'");
}
break;
@@ -550,7 +572,7 @@ namespace GodotTools
{
settingsHintStr += $",Visual Studio:{(int)ExternalEditorId.VisualStudio}" +
$",MonoDevelop:{(int)ExternalEditorId.MonoDevelop}" +
- $",Visual Studio Code:{(int)ExternalEditorId.VsCode}" +
+ $",Visual Studio Code and VSCodium:{(int)ExternalEditorId.VsCode}" +
$",JetBrains Rider and Fleet:{(int)ExternalEditorId.Rider}" +
$",Custom:{(int)ExternalEditorId.CustomEditor}";
}
@@ -558,14 +580,14 @@ namespace GodotTools
{
settingsHintStr += $",Visual Studio:{(int)ExternalEditorId.VisualStudioForMac}" +
$",MonoDevelop:{(int)ExternalEditorId.MonoDevelop}" +
- $",Visual Studio Code:{(int)ExternalEditorId.VsCode}" +
+ $",Visual Studio Code and VSCodium:{(int)ExternalEditorId.VsCode}" +
$",JetBrains Rider and Fleet:{(int)ExternalEditorId.Rider}" +
$",Custom:{(int)ExternalEditorId.CustomEditor}";
}
else if (OS.IsUnixLike)
{
settingsHintStr += $",MonoDevelop:{(int)ExternalEditorId.MonoDevelop}" +
- $",Visual Studio Code:{(int)ExternalEditorId.VsCode}" +
+ $",Visual Studio Code and VSCodium:{(int)ExternalEditorId.VsCode}" +
$",JetBrains Rider and Fleet:{(int)ExternalEditorId.Rider}" +
$",Custom:{(int)ExternalEditorId.CustomEditor}";
}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantUtils.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantUtils.cs
index dc151e2c3e..222ded6895 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantUtils.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantUtils.cs
@@ -534,7 +534,10 @@ namespace Godot.NativeInterop
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Callable ConvertToCallable(in godot_variant p_var)
- => Marshaling.ConvertCallableToManaged(ConvertToNativeCallable(p_var));
+ {
+ using var callable = ConvertToNativeCallable(p_var);
+ return Marshaling.ConvertCallableToManaged(callable);
+ }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static godot_signal ConvertToNativeSignal(in godot_variant p_var)
@@ -542,7 +545,10 @@ namespace Godot.NativeInterop
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Signal ConvertToSignal(in godot_variant p_var)
- => Marshaling.ConvertSignalToManaged(ConvertToNativeSignal(p_var));
+ {
+ using var signal = ConvertToNativeSignal(p_var);
+ return Marshaling.ConvertSignalToManaged(signal);
+ }
public static godot_array ConvertToNativeArray(in godot_variant p_var)
=> p_var.Type == Variant.Type.Array ?
diff --git a/modules/multiplayer/scene_multiplayer.cpp b/modules/multiplayer/scene_multiplayer.cpp
index e245101eeb..dde14034e6 100644
--- a/modules/multiplayer/scene_multiplayer.cpp
+++ b/modules/multiplayer/scene_multiplayer.cpp
@@ -684,9 +684,9 @@ void SceneMultiplayer::_bind_methods() {
SceneMultiplayer::SceneMultiplayer() {
relay_buffer.instantiate();
- cache = Ref<SceneCacheInterface>(memnew(SceneCacheInterface(this)));
- replicator = Ref<SceneReplicationInterface>(memnew(SceneReplicationInterface(this, cache.ptr())));
- rpc = Ref<SceneRPCInterface>(memnew(SceneRPCInterface(this, cache.ptr(), replicator.ptr())));
+ cache.instantiate(this);
+ replicator.instantiate(this, cache.ptr());
+ rpc.instantiate(this, cache.ptr(), replicator.ptr());
set_multiplayer_peer(Ref<OfflineMultiplayerPeer>(memnew(OfflineMultiplayerPeer)));
}
diff --git a/modules/multiplayer/scene_rpc_interface.cpp b/modules/multiplayer/scene_rpc_interface.cpp
index 0938d7ef99..b5f3889268 100644
--- a/modules/multiplayer/scene_rpc_interface.cpp
+++ b/modules/multiplayer/scene_rpc_interface.cpp
@@ -73,16 +73,6 @@ int get_packet_len(uint32_t p_node_target, int p_packet_len) {
}
}
-bool SceneRPCInterface::_sort_rpc_names(const Variant &p_l, const Variant &p_r) {
- if (likely(p_l.is_string() && p_r.is_string())) {
- return p_l.operator String() < p_r.operator String();
- }
- bool valid = false;
- Variant res;
- Variant::evaluate(Variant::OP_LESS, p_l, p_r, res, valid);
- return valid ? res.operator bool() : false;
-}
-
void SceneRPCInterface::_parse_rpc_config(const Variant &p_config, bool p_for_node, RPCConfigCache &r_cache) {
if (p_config.get_type() == Variant::NIL) {
return;
@@ -90,7 +80,7 @@ void SceneRPCInterface::_parse_rpc_config(const Variant &p_config, bool p_for_no
ERR_FAIL_COND(p_config.get_type() != Variant::DICTIONARY);
const Dictionary config = p_config;
Array names = config.keys();
- names.sort_custom(callable_mp_static(&SceneRPCInterface::_sort_rpc_names)); // Ensure ID order
+ names.sort_custom(callable_mp_static(&StringLikeVariantOrder::compare)); // Ensure ID order
for (int i = 0; i < names.size(); i++) {
ERR_CONTINUE(!names[i].is_string());
String name = names[i].operator String();
diff --git a/modules/multiplayer/scene_rpc_interface.h b/modules/multiplayer/scene_rpc_interface.h
index 852cef7830..5c9b66d5f5 100644
--- a/modules/multiplayer/scene_rpc_interface.h
+++ b/modules/multiplayer/scene_rpc_interface.h
@@ -91,8 +91,6 @@ private:
#endif
protected:
- static bool _sort_rpc_names(const Variant &p_l, const Variant &p_r);
-
void _process_rpc(Node *p_node, const uint16_t p_rpc_method_id, int p_from, const uint8_t *p_packet, int p_packet_len, int p_offset);
void _send_rpc(Node *p_from, int p_to, uint16_t p_rpc_id, const RPCConfig &p_config, const StringName &p_name, const Variant **p_arg, int p_argcount);
diff --git a/modules/navigation/2d/nav_mesh_generator_2d.cpp b/modules/navigation/2d/nav_mesh_generator_2d.cpp
index 16cef0dd34..c996c9e935 100644
--- a/modules/navigation/2d/nav_mesh_generator_2d.cpp
+++ b/modules/navigation/2d/nav_mesh_generator_2d.cpp
@@ -691,11 +691,15 @@ void NavMeshGenerator2D::generator_parse_navigationobstacle_node(const Ref<Navig
return;
}
- const Transform2D node_xform = p_source_geometry_data->root_node_transform * Transform2D(0.0, obstacle->get_global_position());
-
+ const Vector2 safe_scale = obstacle->get_global_scale().abs().maxf(0.001);
const float obstacle_radius = obstacle->get_radius();
if (obstacle_radius > 0.0) {
+ // Radius defined obstacle should be uniformly scaled from obstacle basis max scale axis.
+ const float scaling_max_value = safe_scale[safe_scale.max_axis_index()];
+ const Vector2 uniform_max_scale = Vector2(scaling_max_value, scaling_max_value);
+ const Transform2D obstacle_circle_transform = p_source_geometry_data->root_node_transform * Transform2D(obstacle->get_global_rotation(), uniform_max_scale, 0.0, obstacle->get_global_position());
+
Vector<Vector2> obstruction_circle_vertices;
// The point of this is that the moving obstacle can make a simple hole in the navigation mesh and affect the pathfinding.
@@ -709,12 +713,15 @@ void NavMeshGenerator2D::generator_parse_navigationobstacle_node(const Ref<Navig
for (int i = 0; i < circle_points; i++) {
const float angle = i * circle_point_step;
- circle_vertices_ptrw[i] = node_xform.xform(Vector2(Math::cos(angle) * obstacle_radius, Math::sin(angle) * obstacle_radius));
+ circle_vertices_ptrw[i] = obstacle_circle_transform.xform(Vector2(Math::cos(angle) * obstacle_radius, Math::sin(angle) * obstacle_radius));
}
p_source_geometry_data->add_projected_obstruction(obstruction_circle_vertices, obstacle->get_carve_navigation_mesh());
}
+ // Obstacles are projected to the xz-plane, so only rotation around the y-axis can be taken into account.
+ const Transform2D node_xform = p_source_geometry_data->root_node_transform * obstacle->get_global_transform();
+
const Vector<Vector2> &obstacle_vertices = obstacle->get_vertices();
if (obstacle_vertices.is_empty()) {
@@ -760,16 +767,14 @@ void NavMeshGenerator2D::generator_parse_source_geometry_data(Ref<NavigationPoly
static void generator_recursive_process_polytree_items(List<TPPLPoly> &p_tppl_in_polygon, const Clipper2Lib::PolyPathD *p_polypath_item) {
using namespace Clipper2Lib;
- Vector<Vector2> polygon_vertices;
+ TPPLPoly tp;
+ int size = p_polypath_item->Polygon().size();
+ tp.Init(size);
+ int j = 0;
for (const PointD &polypath_point : p_polypath_item->Polygon()) {
- polygon_vertices.push_back(Vector2(static_cast<real_t>(polypath_point.x), static_cast<real_t>(polypath_point.y)));
- }
-
- TPPLPoly tp;
- tp.Init(polygon_vertices.size());
- for (int j = 0; j < polygon_vertices.size(); j++) {
- tp[j] = polygon_vertices[j];
+ tp[j] = Vector2(static_cast<real_t>(polypath_point.x), static_cast<real_t>(polypath_point.y));
+ ++j;
}
if (p_polypath_item->IsHole()) {
@@ -842,87 +847,79 @@ void NavMeshGenerator2D::generator_bake_from_source_geometry_data(Ref<Navigation
return;
}
- if (p_navigation_mesh->get_outline_count() == 0 && !p_source_geometry_data->has_data()) {
- return;
- }
-
- int outline_count = p_navigation_mesh->get_outline_count();
-
- Vector<Vector<Vector2>> traversable_outlines;
- Vector<Vector<Vector2>> obstruction_outlines;
- Vector<NavigationMeshSourceGeometryData2D::ProjectedObstruction> projected_obstructions;
-
- p_source_geometry_data->get_data(
- traversable_outlines,
- obstruction_outlines,
- projected_obstructions);
-
- if (outline_count == 0 && traversable_outlines.size() == 0) {
- return;
- }
-
using namespace Clipper2Lib;
-
PathsD traversable_polygon_paths;
PathsD obstruction_polygon_paths;
+ int obstruction_polygon_path_size = 0;
+ {
+ RWLockRead read_lock(p_source_geometry_data->geometry_rwlock);
- traversable_polygon_paths.reserve(outline_count + traversable_outlines.size());
- obstruction_polygon_paths.reserve(obstruction_outlines.size());
+ const Vector<Vector<Vector2>> &traversable_outlines = p_source_geometry_data->traversable_outlines;
+ int outline_count = p_navigation_mesh->get_outline_count();
- for (int i = 0; i < outline_count; i++) {
- const Vector<Vector2> &traversable_outline = p_navigation_mesh->get_outline(i);
- PathD subject_path;
- subject_path.reserve(traversable_outline.size());
- for (const Vector2 &traversable_point : traversable_outline) {
- const PointD &point = PointD(traversable_point.x, traversable_point.y);
- subject_path.push_back(point);
+ if (outline_count == 0 && (!p_source_geometry_data->has_data() || (traversable_outlines.is_empty()))) {
+ return;
}
- traversable_polygon_paths.push_back(subject_path);
- }
- for (const Vector<Vector2> &traversable_outline : traversable_outlines) {
- PathD subject_path;
- subject_path.reserve(traversable_outline.size());
- for (const Vector2 &traversable_point : traversable_outline) {
- const PointD &point = PointD(traversable_point.x, traversable_point.y);
- subject_path.push_back(point);
- }
- traversable_polygon_paths.push_back(subject_path);
- }
+ const Vector<Vector<Vector2>> &obstruction_outlines = p_source_geometry_data->obstruction_outlines;
+ const Vector<NavigationMeshSourceGeometryData2D::ProjectedObstruction> &projected_obstructions = p_source_geometry_data->_projected_obstructions;
+
+ traversable_polygon_paths.reserve(outline_count + traversable_outlines.size());
+ obstruction_polygon_paths.reserve(obstruction_outlines.size());
- for (const Vector<Vector2> &obstruction_outline : obstruction_outlines) {
- PathD clip_path;
- clip_path.reserve(obstruction_outline.size());
- for (const Vector2 &obstruction_point : obstruction_outline) {
- const PointD &point = PointD(obstruction_point.x, obstruction_point.y);
- clip_path.push_back(point);
+ for (int i = 0; i < outline_count; i++) {
+ const Vector<Vector2> &traversable_outline = p_navigation_mesh->get_outline(i);
+ PathD subject_path;
+ subject_path.reserve(traversable_outline.size());
+ for (const Vector2 &traversable_point : traversable_outline) {
+ subject_path.emplace_back(traversable_point.x, traversable_point.y);
+ }
+ traversable_polygon_paths.push_back(std::move(subject_path));
}
- obstruction_polygon_paths.push_back(clip_path);
- }
- if (!projected_obstructions.is_empty()) {
- for (const NavigationMeshSourceGeometryData2D::ProjectedObstruction &projected_obstruction : projected_obstructions) {
- if (projected_obstruction.carve) {
- continue;
+ for (const Vector<Vector2> &traversable_outline : traversable_outlines) {
+ PathD subject_path;
+ subject_path.reserve(traversable_outline.size());
+ for (const Vector2 &traversable_point : traversable_outline) {
+ subject_path.emplace_back(traversable_point.x, traversable_point.y);
}
- if (projected_obstruction.vertices.is_empty() || projected_obstruction.vertices.size() % 2 != 0) {
- continue;
+ traversable_polygon_paths.push_back(std::move(subject_path));
+ }
+
+ if (!projected_obstructions.is_empty()) {
+ for (const NavigationMeshSourceGeometryData2D::ProjectedObstruction &projected_obstruction : projected_obstructions) {
+ if (projected_obstruction.carve) {
+ continue;
+ }
+ if (projected_obstruction.vertices.is_empty() || projected_obstruction.vertices.size() % 2 != 0) {
+ continue;
+ }
+
+ PathD clip_path;
+ clip_path.reserve(projected_obstruction.vertices.size() / 2);
+ for (int i = 0; i < projected_obstruction.vertices.size() / 2; i++) {
+ clip_path.emplace_back(projected_obstruction.vertices[i * 2], projected_obstruction.vertices[i * 2 + 1]);
+ }
+ if (!IsPositive(clip_path)) {
+ std::reverse(clip_path.begin(), clip_path.end());
+ }
+ obstruction_polygon_paths.push_back(std::move(clip_path));
}
+ }
+ obstruction_polygon_path_size = obstruction_polygon_paths.size();
+ for (const Vector<Vector2> &obstruction_outline : obstruction_outlines) {
PathD clip_path;
- clip_path.reserve(projected_obstruction.vertices.size() / 2);
- for (int i = 0; i < projected_obstruction.vertices.size() / 2; i++) {
- const PointD &point = PointD(projected_obstruction.vertices[i * 2], projected_obstruction.vertices[i * 2 + 1]);
- clip_path.push_back(point);
- }
- if (!IsPositive(clip_path)) {
- std::reverse(clip_path.begin(), clip_path.end());
+ clip_path.reserve(obstruction_outline.size());
+ for (const Vector2 &obstruction_point : obstruction_outline) {
+ clip_path.emplace_back(obstruction_point.x, obstruction_point.y);
}
- obstruction_polygon_paths.push_back(clip_path);
+ obstruction_polygon_paths.push_back(std::move(clip_path));
}
}
Rect2 baking_rect = p_navigation_mesh->get_baking_rect();
+ PathsD area_obstruction_polygon_paths;
if (baking_rect.has_area()) {
Vector2 baking_rect_offset = p_navigation_mesh->get_baking_rect_offset();
@@ -934,48 +931,27 @@ void NavMeshGenerator2D::generator_bake_from_source_geometry_data(Ref<Navigation
RectD clipper_rect = RectD(rect_begin_x, rect_begin_y, rect_end_x, rect_end_y);
traversable_polygon_paths = RectClip(clipper_rect, traversable_polygon_paths);
- obstruction_polygon_paths = RectClip(clipper_rect, obstruction_polygon_paths);
+ area_obstruction_polygon_paths = RectClip(clipper_rect, obstruction_polygon_paths);
+ } else {
+ area_obstruction_polygon_paths = obstruction_polygon_paths;
}
- PathsD path_solution;
-
// first merge all traversable polygons according to user specified fill rule
PathsD dummy_clip_path;
traversable_polygon_paths = Union(traversable_polygon_paths, dummy_clip_path, FillRule::NonZero);
// merge all obstruction polygons, don't allow holes for what is considered "solid" 2D geometry
- obstruction_polygon_paths = Union(obstruction_polygon_paths, dummy_clip_path, FillRule::NonZero);
+ area_obstruction_polygon_paths = Union(area_obstruction_polygon_paths, dummy_clip_path, FillRule::NonZero);
- path_solution = Difference(traversable_polygon_paths, obstruction_polygon_paths, FillRule::NonZero);
+ PathsD path_solution = Difference(traversable_polygon_paths, area_obstruction_polygon_paths, FillRule::NonZero);
real_t agent_radius_offset = p_navigation_mesh->get_agent_radius();
if (agent_radius_offset > 0.0) {
path_solution = InflatePaths(path_solution, -agent_radius_offset, JoinType::Miter, EndType::Polygon);
}
- if (!projected_obstructions.is_empty()) {
- obstruction_polygon_paths.resize(0);
- for (const NavigationMeshSourceGeometryData2D::ProjectedObstruction &projected_obstruction : projected_obstructions) {
- if (!projected_obstruction.carve) {
- continue;
- }
- if (projected_obstruction.vertices.is_empty() || projected_obstruction.vertices.size() % 2 != 0) {
- continue;
- }
-
- PathD clip_path;
- clip_path.reserve(projected_obstruction.vertices.size() / 2);
- for (int i = 0; i < projected_obstruction.vertices.size() / 2; i++) {
- const PointD &point = PointD(projected_obstruction.vertices[i * 2], projected_obstruction.vertices[i * 2 + 1]);
- clip_path.push_back(point);
- }
- if (!IsPositive(clip_path)) {
- std::reverse(clip_path.begin(), clip_path.end());
- }
- obstruction_polygon_paths.push_back(clip_path);
- }
- if (obstruction_polygon_paths.size() > 0) {
- path_solution = Difference(path_solution, obstruction_polygon_paths, FillRule::NonZero);
- }
+ if (obstruction_polygon_path_size > 0) {
+ obstruction_polygon_paths.resize(obstruction_polygon_path_size);
+ path_solution = Difference(path_solution, obstruction_polygon_paths, FillRule::NonZero);
}
//path_solution = RamerDouglasPeucker(path_solution, 0.025); //
@@ -994,33 +970,11 @@ void NavMeshGenerator2D::generator_bake_from_source_geometry_data(Ref<Navigation
path_solution = RectClip(clipper_rect, path_solution);
}
- Vector<Vector<Vector2>> new_baked_outlines;
-
- for (const PathD &scaled_path : path_solution) {
- Vector<Vector2> polypath;
- for (const PointD &scaled_point : scaled_path) {
- polypath.push_back(Vector2(static_cast<real_t>(scaled_point.x), static_cast<real_t>(scaled_point.y)));
- }
- new_baked_outlines.push_back(polypath);
- }
-
- if (new_baked_outlines.size() == 0) {
+ if (path_solution.size() == 0) {
p_navigation_mesh->clear();
return;
}
- PathsD polygon_paths;
- polygon_paths.reserve(new_baked_outlines.size());
-
- for (const Vector<Vector2> &baked_outline : new_baked_outlines) {
- PathD polygon_path;
- for (const Vector2 &baked_outline_point : baked_outline) {
- const PointD &point = PointD(baked_outline_point.x, baked_outline_point.y);
- polygon_path.push_back(point);
- }
- polygon_paths.push_back(polygon_path);
- }
-
ClipType clipper_cliptype = ClipType::Union;
List<TPPLPoly> tppl_in_polygon, tppl_out_polygon;
@@ -1028,7 +982,7 @@ void NavMeshGenerator2D::generator_bake_from_source_geometry_data(Ref<Navigation
PolyTreeD polytree;
ClipperD clipper_D;
- clipper_D.AddSubject(polygon_paths);
+ clipper_D.AddSubject(path_solution);
clipper_D.Execute(clipper_cliptype, FillRule::NonZero, polytree);
for (size_t i = 0; i < polytree.Count(); i++) {
diff --git a/modules/navigation/3d/nav_mesh_generator_3d.cpp b/modules/navigation/3d/nav_mesh_generator_3d.cpp
index ce1551e584..3d0697a7cf 100644
--- a/modules/navigation/3d/nav_mesh_generator_3d.cpp
+++ b/modules/navigation/3d/nav_mesh_generator_3d.cpp
@@ -595,11 +595,17 @@ void NavMeshGenerator3D::generator_parse_navigationobstacle_node(const Ref<Navig
return;
}
- const Transform3D node_xform = p_source_geometry_data->root_node_transform * Transform3D(Basis(), obstacle->get_global_position());
-
+ const float elevation = obstacle->get_global_position().y + p_source_geometry_data->root_node_transform.origin.y;
+ // Prevent non-positive scaling.
+ const Vector3 safe_scale = obstacle->get_global_basis().get_scale().abs().maxf(0.001);
const float obstacle_radius = obstacle->get_radius();
if (obstacle_radius > 0.0) {
+ // Radius defined obstacle should be uniformly scaled from obstacle basis max scale axis.
+ const float scaling_max_value = safe_scale[safe_scale.max_axis_index()];
+ const Vector3 uniform_max_scale = Vector3(scaling_max_value, scaling_max_value, scaling_max_value);
+ const Transform3D obstacle_circle_transform = p_source_geometry_data->root_node_transform * Transform3D(Basis().scaled(uniform_max_scale), obstacle->get_global_position());
+
Vector<Vector3> obstruction_circle_vertices;
// The point of this is that the moving obstacle can make a simple hole in the navigation mesh and affect the pathfinding.
@@ -613,12 +619,15 @@ void NavMeshGenerator3D::generator_parse_navigationobstacle_node(const Ref<Navig
for (int i = 0; i < circle_points; i++) {
const float angle = i * circle_point_step;
- circle_vertices_ptrw[i] = node_xform.xform(Vector3(Math::cos(angle) * obstacle_radius, 0.0, Math::sin(angle) * obstacle_radius));
+ circle_vertices_ptrw[i] = obstacle_circle_transform.xform(Vector3(Math::cos(angle) * obstacle_radius, 0.0, Math::sin(angle) * obstacle_radius));
}
- p_source_geometry_data->add_projected_obstruction(obstruction_circle_vertices, obstacle->get_global_position().y + p_source_geometry_data->root_node_transform.origin.y - obstacle_radius, obstacle_radius, obstacle->get_carve_navigation_mesh());
+ p_source_geometry_data->add_projected_obstruction(obstruction_circle_vertices, elevation - obstacle_radius, scaling_max_value * obstacle_radius, obstacle->get_carve_navigation_mesh());
}
+ // Obstacles are projected to the xz-plane, so only rotation around the y-axis can be taken into account.
+ const Transform3D node_xform = p_source_geometry_data->root_node_transform * Transform3D(Basis().scaled(safe_scale).rotated(Vector3(0.0, 1.0, 0.0), obstacle->get_global_rotation().y), obstacle->get_global_position());
+
const Vector<Vector3> &obstacle_vertices = obstacle->get_vertices();
if (obstacle_vertices.is_empty()) {
@@ -635,7 +644,7 @@ void NavMeshGenerator3D::generator_parse_navigationobstacle_node(const Ref<Navig
obstruction_shape_vertices_ptrw[i] = node_xform.xform(obstacle_vertices_ptr[i]);
obstruction_shape_vertices_ptrw[i].y = 0.0;
}
- p_source_geometry_data->add_projected_obstruction(obstruction_shape_vertices, obstacle->get_global_position().y + p_source_geometry_data->root_node_transform.origin.y, obstacle->get_height(), obstacle->get_carve_navigation_mesh());
+ p_source_geometry_data->add_projected_obstruction(obstruction_shape_vertices, elevation, safe_scale.y * obstacle->get_height(), obstacle->get_carve_navigation_mesh());
}
void NavMeshGenerator3D::generator_parse_source_geometry_data(const Ref<NavigationMesh> &p_navigation_mesh, Ref<NavigationMeshSourceGeometryData3D> p_source_geometry_data, Node *p_root_node) {
diff --git a/modules/noise/doc_classes/FastNoiseLite.xml b/modules/noise/doc_classes/FastNoiseLite.xml
index 6f6a637893..e29581693b 100644
--- a/modules/noise/doc_classes/FastNoiseLite.xml
+++ b/modules/noise/doc_classes/FastNoiseLite.xml
@@ -118,7 +118,7 @@
Manhattan distance (taxicab metric) to the nearest point.
</constant>
<constant name="DISTANCE_HYBRID" value="3" enum="CellularDistanceFunction">
- Blend of [constant DISTANCE_EUCLIDEAN] and [constant DISTANCE_MANHATTAN] to give curved cell boundaries
+ Blend of [constant DISTANCE_EUCLIDEAN] and [constant DISTANCE_MANHATTAN] to give curved cell boundaries.
</constant>
<constant name="RETURN_CELL_VALUE" value="0" enum="CellularReturnType">
The cellular distance function will return the same value for all points within a cell.
diff --git a/modules/openxr/extensions/openxr_composition_layer_extension.cpp b/modules/openxr/extensions/openxr_composition_layer_extension.cpp
index 2d29b8a82c..1e3490d1ed 100644
--- a/modules/openxr/extensions/openxr_composition_layer_extension.cpp
+++ b/modules/openxr/extensions/openxr_composition_layer_extension.cpp
@@ -281,7 +281,7 @@ void OpenXRViewportCompositionLayerProvider::create_android_surface() {
composition_layer_extension->create_android_surface_swapchain(&info, &android_surface.swapchain, &surface);
if (surface) {
- android_surface.surface = Ref<JavaObject>(memnew(JavaObject(JavaClassWrapper::get_singleton()->wrap("android.view.Surface"), surface)));
+ android_surface.surface.instantiate(JavaClassWrapper::get_singleton()->wrap("android.view.Surface"), surface);
}
}
#endif
diff --git a/modules/regex/doc_classes/RegEx.xml b/modules/regex/doc_classes/RegEx.xml
index e12dc43b6f..66e7cd5a0b 100644
--- a/modules/regex/doc_classes/RegEx.xml
+++ b/modules/regex/doc_classes/RegEx.xml
@@ -34,14 +34,14 @@
print(result.get_string("digit"))
# Would print 01 03 0 3f 42
[/codeblock]
- [b]Example of splitting a string using a RegEx:[/b]
+ [b]Example:[/b] Split a string using a RegEx:
[codeblock]
var regex = RegEx.new()
regex.compile("\\S+") # Negated whitespace character class.
var results = []
for result in regex.search_all("One Two \n\tThree"):
results.push_back(result.get_string())
- # The `results` array now contains "One", "Two", "Three".
+ # The `results` array now contains "One", "Two", and "Three".
[/codeblock]
[b]Note:[/b] Godot's regex implementation is based on the [url=https://www.pcre.org/]PCRE2[/url] library. You can view the full pattern reference [url=https://www.pcre.org/current/doc/html/pcre2pattern.html]here[/url].
[b]Tip:[/b] You can use [url=https://regexr.com/]Regexr[/url] to test regular expressions online.
diff --git a/modules/theora/video_stream_theora.cpp b/modules/theora/video_stream_theora.cpp
index 8b2c58acd5..372885b0b4 100644
--- a/modules/theora/video_stream_theora.cpp
+++ b/modules/theora/video_stream_theora.cpp
@@ -629,7 +629,7 @@ void VideoStreamPlaybackTheora::_streaming_thread(void *ud) {
#endif
VideoStreamPlaybackTheora::VideoStreamPlaybackTheora() {
- texture = Ref<ImageTexture>(memnew(ImageTexture));
+ texture.instantiate();
#ifdef THEORA_USE_THREAD_STREAMING
int rb_power = nearest_shift(RB_SIZE_KB * 1024);
diff --git a/modules/vorbis/resource_importer_ogg_vorbis.cpp b/modules/vorbis/resource_importer_ogg_vorbis.cpp
index 729a6f5561..a7423e2d7b 100644
--- a/modules/vorbis/resource_importer_ogg_vorbis.cpp
+++ b/modules/vorbis/resource_importer_ogg_vorbis.cpp
@@ -95,7 +95,7 @@ void ResourceImporterOggVorbis::show_advanced_options(const String &p_path) {
}
#endif
-Error ResourceImporterOggVorbis::import(const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files, Variant *r_metadata) {
+Error ResourceImporterOggVorbis::import(ResourceUID::ID p_source_id, const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files, Variant *r_metadata) {
bool loop = p_options["loop"];
double loop_offset = p_options["loop_offset"];
double bpm = p_options["bpm"];
diff --git a/modules/vorbis/resource_importer_ogg_vorbis.h b/modules/vorbis/resource_importer_ogg_vorbis.h
index f378b80694..a4e4441d82 100644
--- a/modules/vorbis/resource_importer_ogg_vorbis.h
+++ b/modules/vorbis/resource_importer_ogg_vorbis.h
@@ -63,7 +63,7 @@ public:
virtual void get_import_options(const String &p_path, List<ImportOption> *r_options, int p_preset = 0) const override;
virtual bool get_option_visibility(const String &p_path, const String &p_option, const HashMap<StringName, Variant> &p_options) const override;
- virtual Error import(const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = nullptr, Variant *r_metadata = nullptr) override;
+ virtual Error import(ResourceUID::ID p_source_id, const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = nullptr, Variant *r_metadata = nullptr) override;
virtual bool can_import_threaded() const override { return true; }
diff --git a/modules/websocket/doc_classes/WebSocketPeer.xml b/modules/websocket/doc_classes/WebSocketPeer.xml
index 238dd30536..59a2e2ac1a 100644
--- a/modules/websocket/doc_classes/WebSocketPeer.xml
+++ b/modules/websocket/doc_classes/WebSocketPeer.xml
@@ -155,6 +155,10 @@
The extra HTTP headers to be sent during the WebSocket handshake.
[b]Note:[/b] Not supported in Web exports due to browsers' restrictions.
</member>
+ <member name="heartbeat_interval" type="float" setter="set_heartbeat_interval" getter="get_heartbeat_interval" default="0.0">
+ The interval (in seconds) at which the peer will automatically send WebSocket "ping" control frames. When set to [code]0[/code], no "ping" control frames will be sent.
+ [b]Note:[/b] Has no effect in Web exports due to browser restrictions.
+ </member>
<member name="inbound_buffer_size" type="int" setter="set_inbound_buffer_size" getter="get_inbound_buffer_size" default="65535">
The size of the input buffer in bytes (roughly the maximum amount of memory that will be allocated for the inbound packets).
</member>
diff --git a/modules/websocket/websocket_peer.cpp b/modules/websocket/websocket_peer.cpp
index 95a1a238e9..5c24b5d082 100644
--- a/modules/websocket/websocket_peer.cpp
+++ b/modules/websocket/websocket_peer.cpp
@@ -70,6 +70,9 @@ void WebSocketPeer::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_max_queued_packets", "buffer_size"), &WebSocketPeer::set_max_queued_packets);
ClassDB::bind_method(D_METHOD("get_max_queued_packets"), &WebSocketPeer::get_max_queued_packets);
+ ClassDB::bind_method(D_METHOD("set_heartbeat_interval", "interval"), &WebSocketPeer::set_heartbeat_interval);
+ ClassDB::bind_method(D_METHOD("get_heartbeat_interval"), &WebSocketPeer::get_heartbeat_interval);
+
ADD_PROPERTY(PropertyInfo(Variant::PACKED_STRING_ARRAY, "supported_protocols"), "set_supported_protocols", "get_supported_protocols");
ADD_PROPERTY(PropertyInfo(Variant::PACKED_STRING_ARRAY, "handshake_headers"), "set_handshake_headers", "get_handshake_headers");
@@ -78,6 +81,8 @@ void WebSocketPeer::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::INT, "max_queued_packets"), "set_max_queued_packets", "get_max_queued_packets");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "heartbeat_interval"), "set_heartbeat_interval", "get_heartbeat_interval");
+
BIND_ENUM_CONSTANT(WRITE_MODE_TEXT);
BIND_ENUM_CONSTANT(WRITE_MODE_BINARY);
@@ -151,3 +156,12 @@ void WebSocketPeer::set_max_queued_packets(int p_max_queued_packets) {
int WebSocketPeer::get_max_queued_packets() const {
return max_queued_packets;
}
+
+double WebSocketPeer::get_heartbeat_interval() const {
+ return heartbeat_interval_msec / 1000.0;
+}
+
+void WebSocketPeer::set_heartbeat_interval(double p_interval) {
+ ERR_FAIL_COND(p_interval < 0);
+ heartbeat_interval_msec = p_interval * 1000.0;
+}
diff --git a/modules/websocket/websocket_peer.h b/modules/websocket/websocket_peer.h
index ef0197cf6c..3696e787e1 100644
--- a/modules/websocket/websocket_peer.h
+++ b/modules/websocket/websocket_peer.h
@@ -72,6 +72,7 @@ protected:
int outbound_buffer_size = DEFAULT_BUFFER_SIZE;
int inbound_buffer_size = DEFAULT_BUFFER_SIZE;
int max_queued_packets = 2048;
+ uint64_t heartbeat_interval_msec = 0;
public:
static WebSocketPeer *create(bool p_notify_postinitialize = true) {
@@ -117,6 +118,9 @@ public:
void set_max_queued_packets(int p_max_queued_packets);
int get_max_queued_packets() const;
+ double get_heartbeat_interval() const;
+ void set_heartbeat_interval(double p_interval);
+
WebSocketPeer();
~WebSocketPeer();
};
diff --git a/modules/websocket/wsl_peer.cpp b/modules/websocket/wsl_peer.cpp
index 0c0a046805..b624da09e8 100644
--- a/modules/websocket/wsl_peer.cpp
+++ b/modules/websocket/wsl_peer.cpp
@@ -636,7 +636,10 @@ void WSLPeer::_wsl_msg_recv_callback(wslay_event_context_ptr ctx, const struct w
uint8_t is_string = arg->opcode == WSLAY_TEXT_FRAME ? 1 : 0;
peer->in_buffer.write_packet(arg->msg, arg->msg_length, &is_string);
}
- // Ping or pong.
+ if (op == WSLAY_PONG) {
+ peer->heartbeat_waiting = false;
+ }
+ // Pong.
}
wslay_event_callbacks WSLPeer::_wsl_callbacks = {
@@ -680,7 +683,31 @@ void WSLPeer::poll() {
if (ready_state == STATE_OPEN || ready_state == STATE_CLOSING) {
ERR_FAIL_NULL(wsl_ctx);
+ uint64_t ticks = OS::get_singleton()->get_ticks_msec();
int err = 0;
+ if (heartbeat_interval_msec != 0 && ticks - last_heartbeat > heartbeat_interval_msec && ready_state == STATE_OPEN) {
+ if (heartbeat_waiting) {
+ wslay_event_context_free(wsl_ctx);
+ wsl_ctx = nullptr;
+ close(-1);
+ return;
+ }
+ heartbeat_waiting = true;
+ struct wslay_event_msg msg;
+ msg.opcode = WSLAY_PING;
+ msg.msg = nullptr;
+ msg.msg_length = 0;
+ err = wslay_event_queue_msg(wsl_ctx, &msg);
+ if (err == 0) {
+ last_heartbeat = ticks;
+ } else {
+ print_verbose("Websocket (wslay) failed to send ping: " + itos(err));
+ wslay_event_context_free(wsl_ctx);
+ wsl_ctx = nullptr;
+ close(-1);
+ return;
+ }
+ }
if ((err = wslay_event_recv(wsl_ctx)) != 0 || (err = wslay_event_send(wsl_ctx)) != 0) {
// Error close.
print_verbose("Websocket (wslay) poll error: " + itos(err));
@@ -689,12 +716,37 @@ void WSLPeer::poll() {
close(-1);
return;
}
- if (wslay_event_get_close_sent(wsl_ctx) && wslay_event_get_close_received(wsl_ctx)) {
- // Clean close.
- wslay_event_context_free(wsl_ctx);
- wsl_ctx = nullptr;
- close(-1);
- return;
+ if (wslay_event_get_close_sent(wsl_ctx)) {
+ if (wslay_event_get_close_received(wsl_ctx)) {
+ // Clean close.
+ wslay_event_context_free(wsl_ctx);
+ wsl_ctx = nullptr;
+ close(-1);
+ return;
+ } else if (!wslay_event_get_read_enabled(wsl_ctx)) {
+ // Some protocol error caused wslay to stop processing incoming events, we'll never receive a close from the other peer.
+ close_code = wslay_event_get_status_code_sent(wsl_ctx);
+ switch (close_code) {
+ case WSLAY_CODE_MESSAGE_TOO_BIG:
+ close_reason = "Message too big";
+ break;
+ case WSLAY_CODE_PROTOCOL_ERROR:
+ close_reason = "Protocol error";
+ break;
+ case WSLAY_CODE_ABNORMAL_CLOSURE:
+ close_reason = "Abnormal closure";
+ break;
+ case WSLAY_CODE_INVALID_FRAME_PAYLOAD_DATA:
+ close_reason = "Invalid frame payload data";
+ break;
+ default:
+ close_reason = "Unknown";
+ }
+ wslay_event_context_free(wsl_ctx);
+ wsl_ctx = nullptr;
+ close(-1);
+ return;
+ }
}
}
}
@@ -781,6 +833,7 @@ void WSLPeer::close(int p_code, String p_reason) {
}
}
+ heartbeat_waiting = false;
in_buffer.clear();
packet_buffer.resize(0);
}
diff --git a/modules/websocket/wsl_peer.h b/modules/websocket/wsl_peer.h
index c4fe18630c..07bd850607 100644
--- a/modules/websocket/wsl_peer.h
+++ b/modules/websocket/wsl_peer.h
@@ -99,6 +99,8 @@ private:
int close_code = -1;
String close_reason;
uint8_t was_string = 0;
+ uint64_t last_heartbeat = 0;
+ bool heartbeat_waiting = false;
// WebSocket configuration.
bool use_tls = true;