summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.github/workflows/android_builds.yml2
-rw-r--r--.github/workflows/godot_cpp_test.yml2
-rw-r--r--.github/workflows/linux_builds.yml7
-rw-r--r--.github/workflows/static_checks.yml2
-rw-r--r--.github/workflows/web_builds.yml2
-rw-r--r--core/io/http_client_tcp.cpp7
-rw-r--r--core/io/packet_peer_udp.cpp18
-rw-r--r--core/io/stream_peer.cpp4
-rw-r--r--core/io/tcp_server.cpp2
-rw-r--r--core/variant/callable_bind.cpp4
-rw-r--r--core/variant/variant.cpp2
-rw-r--r--core/variant/variant_setget.cpp4
-rw-r--r--doc/classes/Basis.xml1
-rw-r--r--doc/classes/CharFXTransform.xml1
-rw-r--r--doc/classes/Color.xml11
-rw-r--r--doc/classes/Cubemap.xml23
-rw-r--r--doc/classes/EditorExportPlugin.xml11
-rw-r--r--doc/classes/EditorInterface.xml9
-rw-r--r--doc/classes/EditorProperty.xml7
-rw-r--r--doc/classes/EditorSettings.xml2
-rw-r--r--doc/classes/ProjectSettings.xml9
-rw-r--r--doc/classes/Projection.xml3
-rw-r--r--doc/classes/Quaternion.xml1
-rw-r--r--doc/classes/RenderingServer.xml14
-rw-r--r--doc/classes/TileMapLayer.xml15
-rw-r--r--doc/classes/Transform2D.xml1
-rw-r--r--doc/classes/Transform3D.xml1
-rw-r--r--editor/animation_track_editor.cpp64
-rw-r--r--editor/animation_track_editor.h7
-rw-r--r--editor/editor_help_search.cpp4
-rw-r--r--editor/editor_inspector.cpp402
-rw-r--r--editor/editor_inspector.h40
-rw-r--r--editor/editor_interface.cpp26
-rw-r--r--editor/editor_interface.h3
-rw-r--r--editor/editor_node.cpp2
-rw-r--r--editor/editor_settings.cpp48
-rw-r--r--editor/editor_settings.h3
-rw-r--r--editor/export/codesign.cpp8
-rw-r--r--editor/filesystem_dock.cpp28
-rw-r--r--editor/filesystem_dock.h2
-rw-r--r--editor/gui/editor_bottom_panel.cpp42
-rw-r--r--editor/gui/editor_bottom_panel.h7
-rw-r--r--editor/gui/scene_tree_editor.cpp12
-rw-r--r--editor/icons/FPS.svg1
-rw-r--r--editor/icons/Unfavorite.svg1
-rw-r--r--editor/import/3d/editor_import_collada.cpp2
-rw-r--r--editor/import/3d/resource_importer_scene.cpp2
-rw-r--r--editor/import/audio_stream_import_settings.cpp4
-rw-r--r--editor/plugins/animation_blend_tree_editor_plugin.cpp2
-rw-r--r--editor/plugins/animation_player_editor_plugin.cpp4
-rw-r--r--editor/plugins/canvas_item_editor_plugin.cpp2
-rw-r--r--editor/plugins/gizmos/marker_3d_gizmo_plugin.cpp2
-rw-r--r--editor/plugins/light_occluder_2d_editor_plugin.cpp4
-rw-r--r--editor/plugins/navigation_obstacle_3d_editor_plugin.cpp4
-rw-r--r--editor/plugins/navigation_polygon_editor_plugin.cpp4
-rw-r--r--editor/plugins/node_3d_editor_plugin.cpp16
-rw-r--r--editor/plugins/path_3d_editor_plugin.cpp2
-rw-r--r--editor/plugins/polygon_2d_editor_plugin.cpp34
-rw-r--r--editor/plugins/polygon_2d_editor_plugin.h2
-rw-r--r--editor/plugins/polygon_3d_editor_plugin.cpp4
-rw-r--r--editor/plugins/skeleton_3d_editor_plugin.cpp4
-rw-r--r--main/main.cpp2
-rw-r--r--modules/enet/enet_connection.cpp2
-rw-r--r--modules/gltf/gltf_document.cpp6
-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/mono/csharp_script.cpp2
-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.cpp193
-rw-r--r--modules/openxr/extensions/openxr_composition_layer_extension.cpp2
-rw-r--r--modules/theora/video_stream_theora.cpp2
-rw-r--r--platform/ios/export/export_plugin.cpp2
-rw-r--r--scene/2d/tile_map.cpp3
-rw-r--r--scene/2d/tile_map_layer.cpp23
-rw-r--r--scene/2d/tile_map_layer.h2
-rw-r--r--scene/2d/touch_screen_button.cpp2
-rw-r--r--scene/3d/label_3d.cpp2
-rw-r--r--scene/3d/navigation_agent_3d.cpp4
-rw-r--r--scene/3d/navigation_link_3d.cpp4
-rw-r--r--scene/3d/navigation_obstacle_3d.cpp8
-rw-r--r--scene/3d/navigation_region_3d.cpp8
-rw-r--r--scene/3d/path_3d.cpp4
-rw-r--r--scene/3d/physics/ray_cast_3d.cpp2
-rw-r--r--scene/3d/physics/shape_cast_3d.cpp2
-rw-r--r--scene/3d/sprite_3d.cpp2
-rw-r--r--scene/gui/graph_edit.cpp2
-rw-r--r--scene/main/scene_tree.cpp2
-rw-r--r--scene/main/viewport.cpp8
-rw-r--r--scene/resources/2d/navigation_mesh_source_geometry_data_2d.h2
-rw-r--r--scene/resources/3d/shape_3d.cpp2
-rw-r--r--scene/resources/mesh.cpp2
-rw-r--r--scene/resources/navigation_mesh.cpp4
-rw-r--r--scene/resources/packed_scene.cpp4
-rw-r--r--scene/resources/portable_compressed_texture.cpp4
-rw-r--r--servers/rendering/shader_compiler.cpp10
-rw-r--r--servers/rendering_server.cpp13
-rw-r--r--servers/rendering_server.h3
-rw-r--r--tests/scene/test_viewport.h2
102 files changed, 1435 insertions, 516 deletions
diff --git a/.github/workflows/android_builds.yml b/.github/workflows/android_builds.yml
index d516c37d16..950e1e51cc 100644
--- a/.github/workflows/android_builds.yml
+++ b/.github/workflows/android_builds.yml
@@ -14,7 +14,7 @@ concurrency:
jobs:
build-android:
- runs-on: ubuntu-20.04
+ runs-on: ubuntu-24.04
name: ${{ matrix.name }}
strategy:
fail-fast: false
diff --git a/.github/workflows/godot_cpp_test.yml b/.github/workflows/godot_cpp_test.yml
index e26c109d75..dc82a7cb3c 100644
--- a/.github/workflows/godot_cpp_test.yml
+++ b/.github/workflows/godot_cpp_test.yml
@@ -15,7 +15,7 @@ concurrency:
jobs:
godot-cpp-tests:
- runs-on: ubuntu-20.04
+ runs-on: ubuntu-24.04
name: Build and test Godot CPP
steps:
- name: Checkout
diff --git a/.github/workflows/linux_builds.yml b/.github/workflows/linux_builds.yml
index dc3d9f3786..cf653caa3d 100644
--- a/.github/workflows/linux_builds.yml
+++ b/.github/workflows/linux_builds.yml
@@ -17,7 +17,8 @@ concurrency:
jobs:
build-linux:
- runs-on: ubuntu-20.04
+ # If unspecified, stay one LTS before latest to increase portability of Linux artifacts.
+ runs-on: ${{ matrix.os || 'ubuntu-22.04' }}
name: ${{ matrix.name }}
strategy:
fail-fast: false
@@ -60,6 +61,8 @@ jobs:
artifact: false
# Test our oldest supported SCons/Python versions on one arbitrary editor build.
legacy-scons: true
+ # Python 3.6 unavailable on 22.04.
+ os: ubuntu-20.04
- name: Editor with ThreadSanitizer (target=editor, tests=yes, dev_build=yes, use_tsan=yes, use_llvm=yes, linker=lld)
cache-name: linux-editor-thread-sanitizer
@@ -100,7 +103,7 @@ jobs:
run: |
sudo rm -f /etc/apt/sources.list.d/microsoft-prod.list
sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys EB8B81E14DA65431D7504EA8F63F0F2B90935439
- sudo add-apt-repository "deb https://ppa.launchpadcontent.net/kisak/turtle/ubuntu focal main"
+ sudo add-apt-repository "deb https://ppa.launchpadcontent.net/kisak/turtle/ubuntu ${{ matrix.os == 'ubuntu-20.04' && 'focal' || 'jammy' }} main"
sudo apt-get install -qq mesa-vulkan-drivers
# TODO: Figure out somehow how to embed this one.
diff --git a/.github/workflows/static_checks.yml b/.github/workflows/static_checks.yml
index ff102a06cc..9b326cb43e 100644
--- a/.github/workflows/static_checks.yml
+++ b/.github/workflows/static_checks.yml
@@ -9,7 +9,7 @@ concurrency:
jobs:
static-checks:
name: Code style, file formatting, and docs
- runs-on: ubuntu-22.04
+ runs-on: ubuntu-24.04
steps:
- name: Checkout
uses: actions/checkout@v4
diff --git a/.github/workflows/web_builds.yml b/.github/workflows/web_builds.yml
index ec57fa2f0d..9ed8475769 100644
--- a/.github/workflows/web_builds.yml
+++ b/.github/workflows/web_builds.yml
@@ -15,7 +15,7 @@ concurrency:
jobs:
web-template:
- runs-on: ubuntu-22.04
+ runs-on: ubuntu-24.04
name: ${{ matrix.name }}
strategy:
fail-fast: false
diff --git a/core/io/http_client_tcp.cpp b/core/io/http_client_tcp.cpp
index 70fcad543a..237ba30a80 100644
--- a/core/io/http_client_tcp.cpp
+++ b/core/io/http_client_tcp.cpp
@@ -662,15 +662,16 @@ PackedByteArray HTTPClientTCP::read_response_body_chunk() {
chunk_left -= rec;
if (chunk_left == 0) {
- if (chunk[chunk.size() - 2] != '\r' || chunk[chunk.size() - 1] != '\n') {
+ const int chunk_size = chunk.size();
+ if (chunk[chunk_size - 2] != '\r' || chunk[chunk_size - 1] != '\n') {
ERR_PRINT("HTTP Invalid chunk terminator (not \\r\\n)");
status = STATUS_CONNECTION_ERROR;
break;
}
- ret.resize(chunk.size() - 2);
+ ret.resize(chunk_size - 2);
uint8_t *w = ret.ptrw();
- memcpy(w, chunk.ptr(), chunk.size() - 2);
+ memcpy(w, chunk.ptr(), chunk_size - 2);
chunk.clear();
}
diff --git a/core/io/packet_peer_udp.cpp b/core/io/packet_peer_udp.cpp
index fae3de2a98..08e5353174 100644
--- a/core/io/packet_peer_udp.cpp
+++ b/core/io/packet_peer_udp.cpp
@@ -105,6 +105,19 @@ Error PacketPeerUDP::get_packet(const uint8_t **r_buffer, int &r_buffer_size) {
return ERR_UNAVAILABLE;
}
+/* Bogus GCC warning here:
+ * In member function 'int RingBuffer<T>::read(T*, int, bool) [with T = unsigned char]',
+ * inlined from 'virtual Error PacketPeerUDP::get_packet(const uint8_t**, int&)' at core/io/packet_peer_udp.cpp:112:9,
+ * inlined from 'virtual Error PacketPeerUDP::get_packet(const uint8_t**, int&)' at core/io/packet_peer_udp.cpp:99:7:
+ * Error: ./core/ring_buffer.h:68:46: error: writing 1 byte into a region of size 0 [-Werror=stringop-overflow=]
+ * 68 | p_buf[dst++] = read[pos + i];
+ * | ~~~~~~~~~~~~~^~~~~~~
+ */
+#if defined(__GNUC__) && !defined(__clang__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic warning "-Wstringop-overflow=0"
+#endif
+
uint32_t size = 0;
uint8_t ipv6[16] = {};
rb.read(ipv6, 16, true);
@@ -115,6 +128,11 @@ Error PacketPeerUDP::get_packet(const uint8_t **r_buffer, int &r_buffer_size) {
--queue_count;
*r_buffer = packet_buffer;
r_buffer_size = size;
+
+#if defined(__GNUC__) && !defined(__clang__)
+#pragma GCC diagnostic pop
+#endif
+
return OK;
}
diff --git a/core/io/stream_peer.cpp b/core/io/stream_peer.cpp
index c49e15a3a0..3f1c468fb3 100644
--- a/core/io/stream_peer.cpp
+++ b/core/io/stream_peer.cpp
@@ -223,13 +223,13 @@ void StreamPeer::put_var(const Variant &p_variant, bool p_full_objects) {
}
uint8_t StreamPeer::get_u8() {
- uint8_t buf[1];
+ uint8_t buf[1] = {};
get_data(buf, 1);
return buf[0];
}
int8_t StreamPeer::get_8() {
- uint8_t buf[1];
+ uint8_t buf[1] = {};
get_data(buf, 1);
return buf[0];
}
diff --git a/core/io/tcp_server.cpp b/core/io/tcp_server.cpp
index f2b3d5e56a..d69d1f1b29 100644
--- a/core/io/tcp_server.cpp
+++ b/core/io/tcp_server.cpp
@@ -112,7 +112,7 @@ Ref<StreamPeerTCP> TCPServer::take_connection() {
return conn;
}
- conn = Ref<StreamPeerTCP>(memnew(StreamPeerTCP));
+ conn.instantiate();
conn->accept_socket(ns, ip, port);
return conn;
}
diff --git a/core/variant/callable_bind.cpp b/core/variant/callable_bind.cpp
index d82aa3583d..afb889551e 100644
--- a/core/variant/callable_bind.cpp
+++ b/core/variant/callable_bind.cpp
@@ -43,7 +43,7 @@ bool CallableCustomBind::_equal_func(const CallableCustom *p_a, const CallableCu
const CallableCustomBind *a = static_cast<const CallableCustomBind *>(p_a);
const CallableCustomBind *b = static_cast<const CallableCustomBind *>(p_b);
- if (!(a->callable != b->callable)) {
+ if (a->callable != b->callable) {
return false;
}
@@ -185,7 +185,7 @@ bool CallableCustomUnbind::_equal_func(const CallableCustom *p_a, const Callable
const CallableCustomUnbind *a = static_cast<const CallableCustomUnbind *>(p_a);
const CallableCustomUnbind *b = static_cast<const CallableCustomUnbind *>(p_b);
- if (!(a->callable != b->callable)) {
+ if (a->callable != b->callable) {
return false;
}
diff --git a/core/variant/variant.cpp b/core/variant/variant.cpp
index 65bfc29a55..54936eb8a2 100644
--- a/core/variant/variant.cpp
+++ b/core/variant/variant.cpp
@@ -951,7 +951,7 @@ bool Variant::is_zero() const {
return *reinterpret_cast<const ::RID *>(_data._mem) == ::RID();
}
case OBJECT: {
- return _get_obj().obj == nullptr;
+ return get_validated_object() == nullptr;
}
case CALLABLE: {
return reinterpret_cast<const Callable *>(_data._mem)->is_null();
diff --git a/core/variant/variant_setget.cpp b/core/variant/variant_setget.cpp
index 1652f81d99..560067fc08 100644
--- a/core/variant/variant_setget.cpp
+++ b/core/variant/variant_setget.cpp
@@ -141,6 +141,10 @@ void register_named_setters_getters() {
REGISTER_MEMBER(Color, h);
REGISTER_MEMBER(Color, s);
REGISTER_MEMBER(Color, v);
+
+ REGISTER_MEMBER(Color, ok_hsl_h);
+ REGISTER_MEMBER(Color, ok_hsl_s);
+ REGISTER_MEMBER(Color, ok_hsl_l);
}
void unregister_named_setters_getters() {
diff --git a/doc/classes/Basis.xml b/doc/classes/Basis.xml
index 322d2ab9d4..92faa55fe0 100644
--- a/doc/classes/Basis.xml
+++ b/doc/classes/Basis.xml
@@ -25,6 +25,7 @@
<return type="Basis" />
<description>
Constructs a [Basis] identical to the [constant IDENTITY].
+ [b]Note:[/b] In C#, this constructs a [Basis] with all of its components set to [constant Vector3.ZERO].
</description>
</constructor>
<constructor name="Basis">
diff --git a/doc/classes/CharFXTransform.xml b/doc/classes/CharFXTransform.xml
index 403033f85d..28f5d38ee4 100644
--- a/doc/classes/CharFXTransform.xml
+++ b/doc/classes/CharFXTransform.xml
@@ -8,7 +8,6 @@
</description>
<tutorials>
<link title="BBCode in RichTextLabel">$DOCS_URL/tutorials/ui/bbcode_in_richtextlabel.html</link>
- <link title="RichTextEffect test project (third-party)">https://github.com/Eoin-ONeill-Yokai/Godot-Rich-Text-Effect-Test-Project</link>
</tutorials>
<members>
<member name="color" type="Color" setter="set_color" getter="get_color" default="Color(0, 0, 0, 1)" keywords="colour">
diff --git a/doc/classes/Color.xml b/doc/classes/Color.xml
index 9fe42fff90..546d90fa3c 100644
--- a/doc/classes/Color.xml
+++ b/doc/classes/Color.xml
@@ -19,7 +19,7 @@
<return type="Color" />
<description>
Constructs a default [Color] from opaque black. This is the same as [constant BLACK].
- [b]Note:[/b] in C#, constructs an empty color with all of its components set to [code]0.0[/code] (transparent black).
+ [b]Note:[/b] In C#, this constructs a [Color] with all of its components set to [code]0.0[/code] (transparent black).
</description>
</constructor>
<constructor name="Color">
@@ -495,6 +495,15 @@
<member name="h" type="float" setter="" getter="" default="0.0">
The HSV hue of this color, on the range 0 to 1.
</member>
+ <member name="ok_hsl_h" type="float" setter="" getter="" default="0.0">
+ The OKHSL hue of this color, on the range 0 to 1.
+ </member>
+ <member name="ok_hsl_l" type="float" setter="" getter="" default="0.0">
+ The OKHSL lightness of this color, on the range 0 to 1.
+ </member>
+ <member name="ok_hsl_s" type="float" setter="" getter="" default="0.0">
+ The OKHSL saturation of this color, on the range 0 to 1.
+ </member>
<member name="r" type="float" setter="" getter="" default="0.0">
The color's red component, typically on the range of 0 to 1.
</member>
diff --git a/doc/classes/Cubemap.xml b/doc/classes/Cubemap.xml
index b7da3c4ec6..8a850920f7 100644
--- a/doc/classes/Cubemap.xml
+++ b/doc/classes/Cubemap.xml
@@ -6,8 +6,27 @@
<description>
A cubemap is made of 6 textures organized in layers. They are typically used for faking reflections in 3D rendering (see [ReflectionProbe]). It can be used to make an object look as if it's reflecting its surroundings. This usually delivers much better performance than other reflection methods.
This resource is typically used as a uniform in custom shaders. Few core Godot methods make use of [Cubemap] resources.
- To create such a texture file yourself, reimport your image files using the Godot Editor import presets.
- [b]Note:[/b] Godot doesn't support using cubemaps in a [PanoramaSkyMaterial]. You can use [url=https://danilw.github.io/GLSL-howto/cubemap_to_panorama_js/cubemap_to_panorama.html]this tool[/url] to convert a cubemap to an equirectangular sky map.
+ To create such a texture file yourself, reimport your image files using the Godot Editor import presets. The expected image order is X+, X-, Y+, Y-, Z+, Z- (in Godot's coordinate system, so Y+ is "up" and Z- is "forward"). You can use one of the following templates as a base:
+ - [url=https://raw.githubusercontent.com/godotengine/godot-docs/master/img/cubemap_template_2x3.webp]2×3 cubemap template (default layout option)[/url]
+ - [url=https://raw.githubusercontent.com/godotengine/godot-docs/master/img/cubemap_template_3x2.webp]3×2 cubemap template[/url]
+ - [url=https://raw.githubusercontent.com/godotengine/godot-docs/master/img/cubemap_template_1x6.webp]1×6 cubemap template[/url]
+ - [url=https://raw.githubusercontent.com/godotengine/godot-docs/master/img/cubemap_template_6x1.webp]6×1 cubemap template[/url]
+ [b]Note:[/b] Godot doesn't support using cubemaps in a [PanoramaSkyMaterial]. To use a cubemap as a skybox, convert the default [PanoramaSkyMaterial] to a [ShaderMaterial] using the [b]Convert to ShaderMaterial[/b] resource dropdown option, then replace its code with the following:
+ [codeblock lang=text]
+ shader_type sky;
+
+ uniform samplerCube source_panorama : filter_linear, source_color, hint_default_black;
+ uniform float exposure : hint_range(0, 128) = 1.0;
+
+ void sky() {
+ // If importing a cubemap from another engine, you may need to flip one of the `EYEDIR` components below
+ // by replacing it with `-EYEDIR`.
+ vec3 eyedir = vec3(EYEDIR.x, EYEDIR.y, EYEDIR.z);
+ COLOR = texture(source_panorama, eyedir).rgb * exposure;
+ }
+ [/codeblock]
+ After replacing the shader code and saving, specify the imported Cubemap resource in the Shader Parameters section of the ShaderMaterial in the inspector.
+ Alternatively, you can use [url=https://danilw.github.io/GLSL-howto/cubemap_to_panorama_js/cubemap_to_panorama.html]this tool[/url] to convert a cubemap to an equirectangular sky map and use [PanoramaSkyMaterial] as usual.
</description>
<tutorials>
</tutorials>
diff --git a/doc/classes/EditorExportPlugin.xml b/doc/classes/EditorExportPlugin.xml
index dc1cd89642..23e3f4139c 100644
--- a/doc/classes/EditorExportPlugin.xml
+++ b/doc/classes/EditorExportPlugin.xml
@@ -35,9 +35,16 @@
<param index="0" name="resource" type="Resource" />
<param index="1" name="path" type="String" />
<description>
- Customize a resource. If changes are made to it, return the same or a new resource. Otherwise, return [code]null[/code].
- The [i]path[/i] argument is only used when customizing an actual file, otherwise this means that this resource is part of another one and it will be empty.
+ Customize a resource. If changes are made to it, return the same or a new resource. Otherwise, return [code]null[/code]. When a new resource is returned, [param resource] will be replaced by a copy of the new resource.
+ The [param path] argument is only used when customizing an actual file, otherwise this means that this resource is part of another one and it will be empty.
Implementing this method is required if [method _begin_customize_resources] returns [code]true[/code].
+ [b]Note:[/b] When customizing any of the following types and returning another resource, the other resource should not be skipped using [method skip] in [method _export_file]:
+ - [AtlasTexture]
+ - [CompressedCubemap]
+ - [CompressedCubemapArray]
+ - [CompressedTexture2D]
+ - [CompressedTexture2DArray]
+ - [CompressedTexture3D]
</description>
</method>
<method name="_customize_scene" qualifiers="virtual">
diff --git a/doc/classes/EditorInterface.xml b/doc/classes/EditorInterface.xml
index 43059db8b2..0304499a61 100644
--- a/doc/classes/EditorInterface.xml
+++ b/doc/classes/EditorInterface.xml
@@ -301,6 +301,15 @@
See also [method Window.set_unparent_when_invisible].
</description>
</method>
+ <method name="popup_method_selector">
+ <return type="void" />
+ <param index="0" name="object" type="Object" />
+ <param index="1" name="callback" type="Callable" />
+ <param index="2" name="current_value" type="String" default="&quot;&quot;" />
+ <description>
+ Pops up an editor dialog for selecting a method from [param object]. The [param callback] must take a single argument of type [String] which will contain the name of the selected method or be empty if the dialog is canceled. If [param current_value] is provided, the method will be selected automatically in the method list, if it exists.
+ </description>
+ </method>
<method name="popup_node_selector">
<return type="void" />
<param index="0" name="callback" type="Callable" />
diff --git a/doc/classes/EditorProperty.xml b/doc/classes/EditorProperty.xml
index 2b1083393f..4ff541f72d 100644
--- a/doc/classes/EditorProperty.xml
+++ b/doc/classes/EditorProperty.xml
@@ -132,6 +132,13 @@
Emitted when a property was deleted. Used internally.
</description>
</signal>
+ <signal name="property_favorited">
+ <param index="0" name="property" type="StringName" />
+ <param index="1" name="favorited" type="bool" />
+ <description>
+ Emit it if you want to mark a property as favorited, making it appear at the top of the inspector.
+ </description>
+ </signal>
<signal name="property_keyed">
<param index="0" name="property" type="StringName" />
<description>
diff --git a/doc/classes/EditorSettings.xml b/doc/classes/EditorSettings.xml
index ea5207e826..a7a4c6cd5b 100644
--- a/doc/classes/EditorSettings.xml
+++ b/doc/classes/EditorSettings.xml
@@ -1091,9 +1091,11 @@
</member>
<member name="run/bottom_panel/action_on_play" type="int" setter="" getter="">
The action to execute on the bottom panel when running the project.
+ [b]Note:[/b] This option won't do anything if the bottom panel switching is locked using the pin button in the corner of the bottom panel.
</member>
<member name="run/bottom_panel/action_on_stop" type="int" setter="" getter="">
The action to execute on the bottom panel when stopping the project.
+ [b]Note:[/b] This option won't do anything if the bottom panel switching is locked using the pin button in the corner of the bottom panel.
</member>
<member name="run/output/always_clear_output_on_play" type="bool" setter="" getter="">
If [code]true[/code], the editor will clear the Output panel when running the project.
diff --git a/doc/classes/ProjectSettings.xml b/doc/classes/ProjectSettings.xml
index fff6c8d3bb..f5a5045ad5 100644
--- a/doc/classes/ProjectSettings.xml
+++ b/doc/classes/ProjectSettings.xml
@@ -2778,6 +2778,8 @@
[b]Forward Plus[/b]: High-end renderer designed for Desktop devices. Has a higher base overhead, but scales well with complex scenes. Not suitable for older devices or mobile.
[b]Mobile[/b]: Modern renderer designed for mobile devices. Has a lower base overhead than Forward Plus, but does not scale as well to large scenes with many elements.
[b]GL Compatibility[/b]: Low-end renderer designed for older devices. Based on the limitations of the OpenGL 3.3/ OpenGL ES 3.0 / WebGL 2 APIs.
+ This can be overridden using the [code]--rendering-method &lt;method&gt;[/code] command line argument.
+ [b]Note:[/b] The actual rendering method may be automatically changed by the engine as a result of a fallback, or a user-specified command line argument. To get the actual rendering method that is used at runtime, use [method RenderingServer.get_current_rendering_method] instead of reading this project setting's value.
</member>
<member name="rendering/renderer/rendering_method.mobile" type="String" setter="" getter="" default="&quot;mobile&quot;">
Override for [member rendering/renderer/rendering_method] on mobile devices.
@@ -2801,7 +2803,8 @@
Depending on the complexity of scenes, this value may be lowered or may need to be raised.
</member>
<member name="rendering/rendering_device/driver" type="String" setter="" getter="">
- Sets the driver to be used by the renderer when using a RenderingDevice-based renderer like the clustered renderer or the mobile renderer. This property can not be edited directly, instead, set the driver using the platform-specific overrides.
+ Sets the driver to be used by the renderer when using a RenderingDevice-based renderer like the clustered renderer or the mobile renderer. This property can't be edited directly. Instead, set the driver using the platform-specific overrides. This can be overridden using the [code]--rendering-driver &lt;driver&gt;[/code] command line argument.
+ [b]Note:[/b] The actual rendering driver may be automatically changed by the engine as a result of a fallback, or a user-specified command line argument. To get the actual rendering driver that is used at runtime, use [method RenderingServer.get_current_rendering_driver_name] instead of reading this project setting's value.
</member>
<member name="rendering/rendering_device/driver.android" type="String" setter="" getter="">
Android override for [member rendering/rendering_device/driver].
@@ -2890,11 +2893,11 @@
If [code]true[/code], forces vertex shading for all rendering. This can increase performance a lot, but also reduces quality immensely. Can be used to optimize performance on low-end mobile devices.
</member>
<member name="rendering/textures/canvas_textures/default_texture_filter" type="int" setter="" getter="" default="1">
- The default texture filtering mode to use on [CanvasItem]s.
+ The default texture filtering mode to use for [CanvasItem]s built-in texture. In shaders, this texture is accessed as [code]TEXTURE[/code].
[b]Note:[/b] For pixel art aesthetics, see also [member rendering/2d/snap/snap_2d_vertices_to_pixel] and [member rendering/2d/snap/snap_2d_transforms_to_pixel].
</member>
<member name="rendering/textures/canvas_textures/default_texture_repeat" type="int" setter="" getter="" default="0">
- The default texture repeating mode to use on [CanvasItem]s.
+ The default texture repeating mode to use for [CanvasItem]s built-in texture. In shaders, this texture is accessed as [code]TEXTURE[/code].
</member>
<member name="rendering/textures/decals/filter" type="int" setter="" getter="" default="3">
The filtering quality to use for [Decal] nodes. When using one of the anisotropic filtering modes, the anisotropic filtering level is controlled by [member rendering/textures/default_filters/anisotropic_filtering_level].
diff --git a/doc/classes/Projection.xml b/doc/classes/Projection.xml
index 1665660da3..f781083abf 100644
--- a/doc/classes/Projection.xml
+++ b/doc/classes/Projection.xml
@@ -14,7 +14,8 @@
<constructor name="Projection">
<return type="Projection" />
<description>
- Constructs a default-initialized [Projection] set to [constant IDENTITY].
+ Constructs a default-initialized [Projection] identical to [constant IDENTITY].
+ [b]Note:[/b] In C#, this constructs a [Projection] identical to [constant ZERO].
</description>
</constructor>
<constructor name="Projection">
diff --git a/doc/classes/Quaternion.xml b/doc/classes/Quaternion.xml
index 665c6335f2..c74a6453e0 100644
--- a/doc/classes/Quaternion.xml
+++ b/doc/classes/Quaternion.xml
@@ -22,6 +22,7 @@
<return type="Quaternion" />
<description>
Constructs a [Quaternion] identical to the [constant IDENTITY].
+ [b]Note:[/b] In C#, this constructs a [Quaternion] with all of its components set to [code]0.0[/code].
</description>
</constructor>
<constructor name="Quaternion">
diff --git a/doc/classes/RenderingServer.xml b/doc/classes/RenderingServer.xml
index 284dc07fd4..76949fb84e 100644
--- a/doc/classes/RenderingServer.xml
+++ b/doc/classes/RenderingServer.xml
@@ -1548,6 +1548,20 @@
Tries to free an object in the RenderingServer. To avoid memory leaks, this should be called after using an object as memory management does not occur automatically when using RenderingServer directly.
</description>
</method>
+ <method name="get_current_rendering_driver_name" qualifiers="const">
+ <return type="String" />
+ <description>
+ Returns the name of the current rendering driver. This can be [code]vulkan[/code], [code]d3d12[/code], [code]metal[/code], [code]opengl3[/code], [code]opengl3_es[/code], or [code]opengl3_angle[/code]. See also [method get_current_rendering_method].
+ The rendering driver is determined by [member ProjectSettings.rendering/rendering_device/driver], the [code]--rendering-driver[/code] command line argument that overrides this project setting, or an automatic fallback that is applied depending on the hardware.
+ </description>
+ </method>
+ <method name="get_current_rendering_method" qualifiers="const">
+ <return type="String" />
+ <description>
+ Returns the name of the current rendering method. This can be [code]forward_plus[/code], [code]mobile[/code], or [code]gl_compatibility[/code]. See also [method get_current_rendering_driver_name].
+ The rendering method is determined by [member ProjectSettings.rendering/renderer/rendering_method], the [code]--rendering-method[/code] command line argument that overrides this project setting, or an automatic fallback that is applied depending on the hardware.
+ </description>
+ </method>
<method name="get_default_clear_color">
<return type="Color" />
<description>
diff --git a/doc/classes/TileMapLayer.xml b/doc/classes/TileMapLayer.xml
index 6cbec9c2aa..e814d76b73 100644
--- a/doc/classes/TileMapLayer.xml
+++ b/doc/classes/TileMapLayer.xml
@@ -22,6 +22,21 @@
[b]Note:[/b] If the properties of [param tile_data] object should change over time, use [method notify_runtime_tile_data_update] to notify the [TileMapLayer] it needs an update.
</description>
</method>
+ <method name="_update_cells" qualifiers="virtual">
+ <return type="void" />
+ <param index="0" name="coords" type="Vector2i[]" />
+ <param index="1" name="forced_cleanup" type="bool" />
+ <description>
+ Called when this [TileMapLayer]'s cells need an internal update. This update may be caused from individual cells being modified or by a change in the [member tile_set] (causing all cells to be queued for an update). The first call to this function is always for initializing all the [TileMapLayer]'s cells. [param coords] contains the coordinates of all modified cells, roughly in the order they were modified. [param forced_cleanup] is [code]true[/code] when the [TileMapLayer]'s internals should be fully cleaned up. This is the case when:
+ - The layer is disabled;
+ - The layer is not visible;
+ - [member tile_set] is set to [code]null[/code];
+ - The node is removed from the tree;
+ - The node is freed.
+ Note that any internal update happening while one of these conditions is verified is considered to be a "cleanup". See also [method update_internals].
+ [b]Warning:[/b] Implementing this method may degrade the [TileMapLayer]'s performance.
+ </description>
+ </method>
<method name="_use_tile_data_runtime_update" qualifiers="virtual">
<return type="bool" />
<param index="0" name="coords" type="Vector2i" />
diff --git a/doc/classes/Transform2D.xml b/doc/classes/Transform2D.xml
index 756716433e..665e5e9d67 100644
--- a/doc/classes/Transform2D.xml
+++ b/doc/classes/Transform2D.xml
@@ -20,6 +20,7 @@
<return type="Transform2D" />
<description>
Constructs a [Transform2D] identical to [constant IDENTITY].
+ [b]Note:[/b] In C#, this constructs a [Transform2D] with all of its components set to [constant Vector2.ZERO].
</description>
</constructor>
<constructor name="Transform2D">
diff --git a/doc/classes/Transform3D.xml b/doc/classes/Transform3D.xml
index 30c141659a..0f943c7dd1 100644
--- a/doc/classes/Transform3D.xml
+++ b/doc/classes/Transform3D.xml
@@ -21,6 +21,7 @@
<return type="Transform3D" />
<description>
Constructs a [Transform3D] identical to the [constant IDENTITY].
+ [b]Note:[/b] In C#, this constructs a [Transform3D] with its [member origin] and the components of its [member basis] set to [constant Vector3.ZERO].
</description>
</constructor>
<constructor name="Transform3D">
diff --git a/editor/animation_track_editor.cpp b/editor/animation_track_editor.cpp
index bc79b14d4a..076ba6d905 100644
--- a/editor/animation_track_editor.cpp
+++ b/editor/animation_track_editor.cpp
@@ -63,7 +63,6 @@
constexpr double FPS_DECIMAL = 1.0;
constexpr double SECOND_DECIMAL = 0.0001;
-constexpr double FPS_STEP_FRACTION = 0.0625;
void AnimationTrackKeyEdit::_bind_methods() {
ClassDB::bind_method(D_METHOD("_update_obj"), &AnimationTrackKeyEdit::_update_obj);
@@ -3776,6 +3775,7 @@ void AnimationTrackEditor::set_animation(const Ref<Animation> &p_anim, bool p_re
step->set_read_only(false);
snap_keys->set_disabled(false);
snap_timeline->set_disabled(false);
+ fps_compat->set_disabled(false);
snap_mode->set_disabled(false);
auto_fit->set_disabled(false);
auto_fit_bezier->set_disabled(false);
@@ -3798,6 +3798,7 @@ void AnimationTrackEditor::set_animation(const Ref<Animation> &p_anim, bool p_re
step->set_read_only(true);
snap_keys->set_disabled(true);
snap_timeline->set_disabled(true);
+ fps_compat->set_disabled(true);
snap_mode->set_disabled(true);
bezier_edit_icon->set_disabled(true);
auto_fit->set_disabled(true);
@@ -5029,7 +5030,12 @@ void AnimationTrackEditor::_snap_mode_changed(int p_mode) {
}
marker_edit->set_use_fps(use_fps);
// To ensure that the conversion results are consistent between serialization and load, the value is snapped with 0.0625 to be a rational number when FPS mode is used.
- step->set_step(use_fps ? FPS_STEP_FRACTION : SECOND_DECIMAL);
+ step->set_step(use_fps ? FPS_DECIMAL : SECOND_DECIMAL);
+ if (use_fps) {
+ fps_compat->hide();
+ } else {
+ fps_compat->show();
+ }
_update_step_spinbox();
}
@@ -5045,7 +5051,6 @@ void AnimationTrackEditor::_update_step_spinbox() {
} else {
step->set_value(1.0 / animation->get_step());
}
-
} else {
step->set_value(animation->get_step());
}
@@ -5054,6 +5059,20 @@ void AnimationTrackEditor::_update_step_spinbox() {
_update_snap_unit();
}
+void AnimationTrackEditor::_update_fps_compat_mode(bool p_enabled) {
+ _update_snap_unit();
+}
+
+void AnimationTrackEditor::_update_nearest_fps_label() {
+ bool is_fps_invalid = nearest_fps == 0;
+ if (is_fps_invalid) {
+ nearest_fps_label->hide();
+ } else {
+ nearest_fps_label->show();
+ nearest_fps_label->set_text("Nearest FPS: " + itos(nearest_fps));
+ }
+}
+
void AnimationTrackEditor::_animation_update() {
timeline->queue_redraw();
timeline->update_values();
@@ -5115,6 +5134,7 @@ void AnimationTrackEditor::_notification(int p_what) {
bezier_edit_icon->set_button_icon(get_editor_theme_icon(SNAME("EditBezier")));
snap_timeline->set_button_icon(get_editor_theme_icon(SNAME("SnapTimeline")));
snap_keys->set_button_icon(get_editor_theme_icon(SNAME("SnapKeys")));
+ fps_compat->set_button_icon(get_editor_theme_icon(SNAME("FPS")));
view_group->set_button_icon(get_editor_theme_icon(view_group->is_pressed() ? SNAME("AnimationTrackList") : SNAME("AnimationTrackGroup")));
selected_filter->set_button_icon(get_editor_theme_icon(SNAME("AnimationFilter")));
imported_anim_warning->set_button_icon(get_editor_theme_icon(SNAME("NodeWarning")));
@@ -5160,9 +5180,8 @@ void AnimationTrackEditor::_update_step(double p_new_step) {
double step_value = p_new_step;
if (timeline->is_using_fps()) {
if (step_value != 0.0) {
- // step_value must also be less than or equal to 1000 to ensure that no error accumulates due to interactions with retrieving values from inner range.
+ // A step_value should be less than or equal to 1000 to ensure that no error accumulates due to interactions with retrieving values from inner range.
step_value = 1.0 / MIN(1000.0, p_new_step);
- ;
}
timeline->queue_redraw();
}
@@ -7336,19 +7355,30 @@ void AnimationTrackEditor::_selection_changed() {
}
void AnimationTrackEditor::_update_snap_unit() {
+ nearest_fps = 0;
+
if (step->get_value() <= 0) {
snap_unit = 0;
+ _update_nearest_fps_label();
return; // Avoid zero div.
}
if (timeline->is_using_fps()) {
+ _clear_selection(true); // Needs to recreate a spinbox of the KeyEdit.
snap_unit = 1.0 / step->get_value();
} else {
- double integer;
- double fraction = Math::modf(step->get_value(), &integer);
- fraction = 1.0 / Math::round(1.0 / fraction);
- snap_unit = integer + fraction;
+ if (fps_compat->is_pressed()) {
+ snap_unit = CLAMP(step->get_value(), 0.0, 1.0);
+ if (!Math::is_zero_approx(snap_unit)) {
+ real_t fps = Math::round(1.0 / snap_unit);
+ nearest_fps = int(fps);
+ snap_unit = 1.0 / fps;
+ }
+ } else {
+ snap_unit = step->get_value();
+ }
}
+ _update_nearest_fps_label();
}
float AnimationTrackEditor::snap_time(float p_value, bool p_relative) {
@@ -7369,6 +7399,10 @@ float AnimationTrackEditor::snap_time(float p_value, bool p_relative) {
return p_value;
}
+float AnimationTrackEditor::get_snap_unit() {
+ return snap_unit;
+}
+
void AnimationTrackEditor::_show_imported_anim_warning() {
// It looks terrible on a single line but the TTR extractor doesn't support line breaks yet.
EditorNode::get_singleton()->show_warning(
@@ -7622,6 +7656,18 @@ AnimationTrackEditor::AnimationTrackEditor() {
snap_keys->set_pressed(true);
snap_keys->set_tooltip_text(TTR("Apply snapping to selected key(s)."));
+ fps_compat = memnew(Button);
+ fps_compat->set_flat(true);
+ bottom_hb->add_child(fps_compat);
+ fps_compat->set_disabled(true);
+ fps_compat->set_toggle_mode(true);
+ fps_compat->set_pressed(true);
+ fps_compat->set_tooltip_text(TTR("Apply snapping to the nearest integer FPS."));
+ fps_compat->connect(SceneStringName(toggled), callable_mp(this, &AnimationTrackEditor::_update_fps_compat_mode));
+
+ nearest_fps_label = memnew(Label);
+ bottom_hb->add_child(nearest_fps_label);
+
step = memnew(EditorSpinSlider);
step->set_min(0);
step->set_max(1000000);
diff --git a/editor/animation_track_editor.h b/editor/animation_track_editor.h
index 1053468452..e7271f1941 100644
--- a/editor/animation_track_editor.h
+++ b/editor/animation_track_editor.h
@@ -600,6 +600,8 @@ class AnimationTrackEditor : public VBoxContainer {
AnimationMarkerEdit *marker_edit = nullptr;
HSlider *zoom = nullptr;
EditorSpinSlider *step = nullptr;
+ Button *fps_compat = nullptr;
+ Label *nearest_fps_label = nullptr;
TextureRect *zoom_icon = nullptr;
Button *snap_keys = nullptr;
Button *snap_timeline = nullptr;
@@ -637,6 +639,8 @@ class AnimationTrackEditor : public VBoxContainer {
void _track_grab_focus(int p_track);
void _update_scroll(double);
+ void _update_nearest_fps_label();
+ void _update_fps_compat_mode(bool p_enabled);
void _update_step(double p_new_step);
void _update_length(double p_new_len);
void _dropped_track(int p_from_track, int p_to_track);
@@ -853,6 +857,8 @@ class AnimationTrackEditor : public VBoxContainer {
void _pick_track_select_recursive(TreeItem *p_item, const String &p_filter, Vector<Node *> &p_select_candidates);
double snap_unit;
+ bool fps_compatible = true;
+ int nearest_fps = 0;
void _update_snap_unit();
protected:
@@ -935,6 +941,7 @@ public:
bool can_add_reset_key() const;
float get_moving_selection_offset() const;
float snap_time(float p_value, bool p_relative = false);
+ float get_snap_unit();
bool is_grouping_tracks();
PackedStringArray get_selected_section() const;
bool is_marker_selected(const StringName &p_marker) const;
diff --git a/editor/editor_help_search.cpp b/editor/editor_help_search.cpp
index d11bf7720c..b0c06475f8 100644
--- a/editor/editor_help_search.cpp
+++ b/editor/editor_help_search.cpp
@@ -151,7 +151,7 @@ void EditorHelpSearch::_update_results() {
search_flags |= SEARCH_SHOW_HIERARCHY;
}
- search = Ref<Runner>(memnew(Runner(results_tree, results_tree, &tree_cache, term, search_flags)));
+ search.instantiate(results_tree, results_tree, &tree_cache, term, search_flags);
// Clear old search flags to force rebuild on short term.
old_search_flags = 0;
@@ -162,7 +162,7 @@ void EditorHelpSearch::_update_results() {
hierarchy_button->set_disabled(true);
// Always show hierarchy for short searches.
- search = Ref<Runner>(memnew(Runner(results_tree, results_tree, &tree_cache, term, search_flags | SEARCH_SHOW_HIERARCHY)));
+ search.instantiate(results_tree, results_tree, &tree_cache, term, search_flags | SEARCH_SHOW_HIERARCHY);
old_search_flags = search_flags;
set_process(true);
diff --git a/editor/editor_inspector.cpp b/editor/editor_inspector.cpp
index 91dd8e5019..1c23ce8ede 100644
--- a/editor/editor_inspector.cpp
+++ b/editor/editor_inspector.cpp
@@ -931,10 +931,19 @@ float EditorProperty::get_name_split_ratio() const {
return split_ratio;
}
+void EditorProperty::set_favoritable(bool p_favoritable) {
+ can_favorite = p_favoritable;
+}
+
+bool EditorProperty::is_favoritable() const {
+ return can_favorite;
+}
+
void EditorProperty::set_object_and_property(Object *p_object, const StringName &p_property) {
object = p_object;
property = p_property;
- _update_pin_flags();
+
+ _update_flags();
}
static bool _is_value_potential_override(Node *p_node, const String &p_property) {
@@ -953,12 +962,14 @@ static bool _is_value_potential_override(Node *p_node, const String &p_property)
}
}
-void EditorProperty::_update_pin_flags() {
+void EditorProperty::_update_flags() {
can_pin = false;
pin_hidden = true;
+
if (read_only) {
return;
}
+
if (Node *node = Object::cast_to<Node>(object)) {
// Avoid errors down the road by ignoring nodes which are not part of a scene
if (!node->get_owner()) {
@@ -1034,6 +1045,10 @@ void EditorProperty::menu_option(int p_option) {
case MENU_COPY_PROPERTY_PATH: {
DisplayServer::get_singleton()->clipboard_set(property_path);
} break;
+ case MENU_FAVORITE_PROPERTY: {
+ emit_signal(SNAME("property_favorited"), property, !favorited);
+ queue_redraw();
+ } break;
case MENU_PIN_VALUE: {
emit_signal(SNAME("property_pinned"), property, !pinned);
queue_redraw();
@@ -1091,6 +1106,7 @@ void EditorProperty::_bind_methods() {
ADD_SIGNAL(MethodInfo("property_deleted", PropertyInfo(Variant::STRING_NAME, "property")));
ADD_SIGNAL(MethodInfo("property_keyed_with_value", PropertyInfo(Variant::STRING_NAME, "property"), PropertyInfo(Variant::NIL, "value", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NIL_IS_VARIANT)));
ADD_SIGNAL(MethodInfo("property_checked", PropertyInfo(Variant::STRING_NAME, "property"), PropertyInfo(Variant::BOOL, "checked")));
+ ADD_SIGNAL(MethodInfo("property_favorited", PropertyInfo(Variant::STRING_NAME, "property"), PropertyInfo(Variant::BOOL, "favorited")));
ADD_SIGNAL(MethodInfo("property_pinned", PropertyInfo(Variant::STRING_NAME, "property"), PropertyInfo(Variant::BOOL, "pinned")));
ADD_SIGNAL(MethodInfo("property_can_revert_changed", PropertyInfo(Variant::STRING_NAME, "property"), PropertyInfo(Variant::BOOL, "can_revert")));
ADD_SIGNAL(MethodInfo("resource_selected", PropertyInfo(Variant::STRING, "path"), PropertyInfo(Variant::OBJECT, "resource", PROPERTY_HINT_RESOURCE_TYPE, "Resource")));
@@ -1129,8 +1145,21 @@ void EditorProperty::_update_popup() {
menu->set_item_disabled(MENU_PASTE_VALUE, is_read_only());
menu->set_item_disabled(MENU_COPY_PROPERTY_PATH, internal);
- if (!pin_hidden) {
+ if (can_favorite || !pin_hidden) {
menu->add_separator();
+ }
+
+ if (can_favorite) {
+ if (favorited) {
+ menu->add_icon_item(get_editor_theme_icon(SNAME("Unfavorite")), TTR("Unfavorite Property"), MENU_FAVORITE_PROPERTY);
+ menu->set_item_tooltip(menu->get_item_index(MENU_FAVORITE_PROPERTY), TTR("Make this property be put back at its original place."));
+ } else {
+ menu->add_icon_item(get_editor_theme_icon(SNAME("Favorites")), TTR("Favorite Property"), MENU_FAVORITE_PROPERTY);
+ menu->set_item_tooltip(menu->get_item_index(MENU_FAVORITE_PROPERTY), TTR("Make this property be placed at the top for all objects of this class."));
+ }
+ }
+
+ if (!pin_hidden) {
if (can_pin) {
menu->add_icon_check_item(get_editor_theme_icon(SNAME("Pin")), TTR("Pin Value"), MENU_PIN_VALUE);
menu->set_item_checked(menu->get_item_index(MENU_PIN_VALUE), pinned);
@@ -1219,9 +1248,14 @@ void EditorInspectorPlugin::_bind_methods() {
void EditorInspectorCategory::_notification(int p_what) {
switch (p_what) {
- case NOTIFICATION_ENTER_TREE:
case NOTIFICATION_THEME_CHANGED: {
- menu->set_item_icon(menu->get_item_index(MENU_OPEN_DOCS), get_editor_theme_icon(SNAME("Help")));
+ if (menu) {
+ if (is_favorite) {
+ menu->set_item_icon(menu->get_item_index(EditorInspector::MENU_UNFAVORITE_ALL), get_editor_theme_icon(SNAME("Unfavorite")));
+ } else {
+ menu->set_item_icon(menu->get_item_index(MENU_OPEN_DOCS), get_editor_theme_icon(SNAME("Help")));
+ }
+ }
} break;
case NOTIFICATION_DRAW: {
Ref<StyleBox> sb = get_theme_stylebox(SNAME("bg"));
@@ -1278,6 +1312,15 @@ Control *EditorInspectorCategory::make_custom_tooltip(const String &p_text) cons
return memnew(Control); // Make the standard tooltip invisible.
}
+void EditorInspectorCategory::set_as_favorite(EditorInspector *p_for_inspector) {
+ is_favorite = true;
+
+ menu = memnew(PopupMenu);
+ menu->add_item(TTR("Unfavorite All"), EditorInspector::MENU_UNFAVORITE_ALL);
+ add_child(menu);
+ menu->connect(SceneStringName(id_pressed), callable_mp(p_for_inspector, &EditorInspector::_handle_menu_option));
+}
+
Size2 EditorInspectorCategory::get_minimum_size() const {
Ref<Font> font = get_theme_font(SNAME("bold"), EditorStringName(EditorFonts));
int font_size = get_theme_font_size(SNAME("bold_size"), EditorStringName(EditorFonts));
@@ -1306,7 +1349,7 @@ void EditorInspectorCategory::_handle_menu_option(int p_option) {
}
void EditorInspectorCategory::gui_input(const Ref<InputEvent> &p_event) {
- if (doc_class_name.is_empty()) {
+ if (!is_favorite && doc_class_name.is_empty()) {
return;
}
@@ -1315,20 +1358,21 @@ void EditorInspectorCategory::gui_input(const Ref<InputEvent> &p_event) {
return;
}
- menu->set_item_disabled(menu->get_item_index(MENU_OPEN_DOCS), !EditorHelp::get_doc_data()->class_list.has(doc_class_name));
+ if (!is_favorite) {
+ if (!menu) {
+ menu = memnew(PopupMenu);
+ menu->add_icon_item(get_editor_theme_icon(SNAME("Help")), TTR("Open Documentation"), MENU_OPEN_DOCS);
+ add_child(menu);
+ menu->connect(SceneStringName(id_pressed), callable_mp(this, &EditorInspectorCategory::_handle_menu_option));
+ }
+ menu->set_item_disabled(menu->get_item_index(MENU_OPEN_DOCS), !EditorHelp::get_doc_data()->class_list.has(doc_class_name));
+ }
menu->set_position(get_screen_position() + mb_event->get_position());
menu->reset_size();
menu->popup();
}
-EditorInspectorCategory::EditorInspectorCategory() {
- menu = memnew(PopupMenu);
- menu->connect(SceneStringName(id_pressed), callable_mp(this, &EditorInspectorCategory::_handle_menu_option));
- menu->add_item(TTR("Open Documentation"), MENU_OPEN_DOCS);
- add_child(menu);
-}
-
////////////////////////////////////////////////
////////////////////////////////////////////////
@@ -1622,6 +1666,10 @@ void EditorInspectorSection::gui_input(const Ref<InputEvent> &p_event) {
}
}
+String EditorInspectorSection::get_section() const {
+ return section;
+}
+
VBoxContainer *EditorInspectorSection::get_vbox() {
return vbox;
}
@@ -2675,7 +2723,13 @@ String EditorInspector::get_selected_path() const {
void EditorInspector::_parse_added_editors(VBoxContainer *current_vbox, EditorInspectorSection *p_section, Ref<EditorInspectorPlugin> ped) {
for (const EditorInspectorPlugin::AddedEditor &F : ped->added_editors) {
EditorProperty *ep = Object::cast_to<EditorProperty>(F.property_editor);
- current_vbox->add_child(F.property_editor);
+
+ if (ep && current_favorites.has(F.properties[0])) {
+ ep->favorited = true;
+ favorites_vbox->add_child(F.property_editor);
+ } else {
+ current_vbox->add_child(F.property_editor);
+ }
if (ep) {
ep->object = object;
@@ -2684,6 +2738,7 @@ void EditorInspector::_parse_added_editors(VBoxContainer *current_vbox, EditorIn
ep->connect("property_deleted", callable_mp(this, &EditorInspector::_property_deleted), CONNECT_DEFERRED);
ep->connect("property_keyed_with_value", callable_mp(this, &EditorInspector::_property_keyed_with_value));
ep->connect("property_checked", callable_mp(this, &EditorInspector::_property_checked));
+ ep->connect("property_favorited", callable_mp(this, &EditorInspector::_set_property_favorited), CONNECT_DEFERRED);
ep->connect("property_pinned", callable_mp(this, &EditorInspector::_property_pinned));
ep->connect("selected", callable_mp(this, &EditorInspector::_property_selected));
ep->connect("multiple_properties_changed", callable_mp(this, &EditorInspector::_multiple_properties_changed));
@@ -2727,7 +2782,7 @@ void EditorInspector::_parse_added_editors(VBoxContainer *current_vbox, EditorIn
ep->set_read_only(read_only);
ep->update_property();
- ep->_update_pin_flags();
+ ep->_update_flags();
ep->update_editor_property_status();
ep->set_deletable(deletable_properties);
ep->update_cache();
@@ -2837,6 +2892,7 @@ void EditorInspector::update_tree() {
String subgroup;
String subgroup_base;
int section_depth = 0;
+ bool disable_favorite = false;
VBoxContainer *category_vbox = nullptr;
List<PropertyInfo> plist;
@@ -2844,13 +2900,17 @@ void EditorInspector::update_tree() {
HashMap<VBoxContainer *, HashMap<String, VBoxContainer *>> vbox_per_path;
HashMap<String, EditorInspectorArray *> editor_inspector_array_per_prefix;
+ HashMap<String, HashMap<String, LocalVector<EditorProperty *>>> favorites_to_add;
Color sscolor = get_theme_color(SNAME("prop_subsection"), EditorStringName(Editor));
// Get the lists of editors to add the beginning.
for (Ref<EditorInspectorPlugin> &ped : valid_plugins) {
ped->parse_begin(object);
- _parse_added_editors(main_vbox, nullptr, ped);
+ _parse_added_editors(begin_vbox, nullptr, ped);
+ }
+ if (begin_vbox->get_child_count()) {
+ begin_vbox->show();
}
StringName doc_name;
@@ -2897,6 +2957,7 @@ void EditorInspector::update_tree() {
subgroup = "";
subgroup_base = "";
section_depth = 0;
+ disable_favorite = false;
vbox_per_path.clear();
editor_inspector_array_per_prefix.clear();
@@ -2960,6 +3021,11 @@ void EditorInspector::update_tree() {
} else {
category_icon = EditorNode::get_singleton()->get_object_icon(scr.ptr(), "Object");
}
+
+ // Property favorites aren't compatible with built-in scripts.
+ if (scr->is_built_in()) {
+ disable_favorite = true;
+ }
}
}
@@ -3058,6 +3124,11 @@ void EditorInspector::update_tree() {
}
}
+ // Don't allow to favorite array items.
+ if (!disable_favorite) {
+ disable_favorite = !array_prefix.is_empty();
+ }
+
if (!array_prefix.is_empty()) {
path = path.trim_prefix(array_prefix);
int char_index = path.find("/");
@@ -3449,6 +3520,7 @@ void EditorInspector::update_tree() {
ep->set_draw_warning(draw_warning);
ep->set_use_folding(use_folding);
+ ep->set_favoritable(can_favorite && !disable_favorite);
ep->set_checkable(checkable);
ep->set_checked(checked);
ep->set_keying(keying);
@@ -3456,7 +3528,12 @@ void EditorInspector::update_tree() {
ep->set_deletable(deletable_properties || p.name.begins_with("metadata/"));
}
- current_vbox->add_child(editors[i].property_editor);
+ if (ep && ep->is_favoritable() && current_favorites.has(p.name)) {
+ ep->favorited = true;
+ favorites_to_add[group][subgroup].push_back(ep);
+ } else {
+ current_vbox->add_child(editors[i].property_editor);
+ }
if (ep) {
// Eventually, set other properties/signals after the property editor got added to the tree.
@@ -3465,6 +3542,7 @@ void EditorInspector::update_tree() {
ep->connect("property_keyed", callable_mp(this, &EditorInspector::_property_keyed));
ep->connect("property_deleted", callable_mp(this, &EditorInspector::_property_deleted), CONNECT_DEFERRED);
ep->connect("property_keyed_with_value", callable_mp(this, &EditorInspector::_property_keyed_with_value));
+ ep->connect("property_favorited", callable_mp(this, &EditorInspector::_set_property_favorited), CONNECT_DEFERRED);
ep->connect("property_checked", callable_mp(this, &EditorInspector::_property_checked));
ep->connect("property_pinned", callable_mp(this, &EditorInspector::_property_pinned));
ep->connect("selected", callable_mp(this, &EditorInspector::_property_selected));
@@ -3495,7 +3573,7 @@ void EditorInspector::update_tree() {
ep->set_internal(p.usage & PROPERTY_USAGE_INTERNAL);
ep->update_property();
- ep->_update_pin_flags();
+ ep->_update_flags();
ep->update_editor_property_status();
ep->update_cache();
@@ -3506,6 +3584,79 @@ void EditorInspector::update_tree() {
}
}
+ if (!current_favorites.is_empty()) {
+ favorites_section->show();
+
+ // Organize the favorited properties in their sections, to keep context and differentiate from others with the same name.
+ bool is_localized = property_name_style == EditorPropertyNameProcessor::STYLE_LOCALIZED;
+ for (const KeyValue<String, HashMap<String, LocalVector<EditorProperty *>>> &KV : favorites_to_add) {
+ String section_name = KV.key;
+ String label;
+ String tooltip;
+ VBoxContainer *parent_vbox = favorites_vbox;
+ if (!section_name.is_empty()) {
+ if (is_localized) {
+ label = EditorPropertyNameProcessor::get_singleton()->translate_group_name(section_name);
+ tooltip = section_name;
+ } else {
+ label = section_name;
+ tooltip = EditorPropertyNameProcessor::get_singleton()->translate_group_name(section_name);
+ }
+
+ EditorInspectorSection *section = memnew(EditorInspectorSection);
+ favorites_groups_vbox->add_child(section);
+ parent_vbox = section->get_vbox();
+ section->setup("", section_name, object, sscolor, false);
+ section->set_tooltip_text(tooltip);
+ }
+
+ for (const KeyValue<String, LocalVector<EditorProperty *>> &KV2 : KV.value) {
+ section_name = KV2.key;
+ VBoxContainer *vbox = parent_vbox;
+ if (!section_name.is_empty()) {
+ if (is_localized) {
+ label = EditorPropertyNameProcessor::get_singleton()->translate_group_name(section_name);
+ tooltip = section_name;
+ } else {
+ label = section_name;
+ tooltip = EditorPropertyNameProcessor::get_singleton()->translate_group_name(section_name);
+ }
+
+ EditorInspectorSection *section = memnew(EditorInspectorSection);
+ vbox->add_child(section);
+ vbox = section->get_vbox();
+ section->setup("", section_name, object, sscolor, false);
+ section->set_tooltip_text(tooltip);
+ }
+
+ for (EditorProperty *ep : KV2.value) {
+ vbox->add_child(ep);
+ }
+ }
+ }
+
+ // Show a separator if there's no category to clearly divide the properties.
+ favorites_separator->hide();
+ if (main_vbox->get_child_count() > 0) {
+ EditorInspectorCategory *category = Object::cast_to<EditorInspectorCategory>(main_vbox->get_child(0));
+ if (!category) {
+ favorites_separator->show();
+ }
+ }
+
+ // Clean up empty sections.
+ for (List<EditorInspectorSection *>::Element *I = sections.back(); I; I = I->prev()) {
+ EditorInspectorSection *section = I->get();
+ if (section->get_vbox()->get_child_count() == 0) {
+ I = I->prev();
+
+ sections.erase(section);
+ vbox_per_path[main_vbox].erase(section->get_section());
+ memdelete(section);
+ }
+ }
+ }
+
if (!hide_metadata && !object->call("_hide_metadata_from_inspector")) {
// Add 4px of spacing between the "Add Metadata" button and the content above it.
Control *spacer = memnew(Control);
@@ -3548,6 +3699,19 @@ void EditorInspector::update_property(const String &p_prop) {
}
void EditorInspector::_clear(bool p_hide_plugins) {
+ begin_vbox->hide();
+ while (begin_vbox->get_child_count()) {
+ memdelete(begin_vbox->get_child(0));
+ }
+
+ favorites_section->hide();
+ while (favorites_vbox->get_child_count()) {
+ memdelete(favorites_vbox->get_child(0));
+ }
+ while (favorites_groups_vbox->get_child_count()) {
+ memdelete(favorites_groups_vbox->get_child(0));
+ }
+
while (main_vbox->get_child_count()) {
memdelete(main_vbox->get_child(0));
}
@@ -3594,6 +3758,10 @@ void EditorInspector::edit(Object *p_object) {
update_scroll_request = scroll_cache[object->get_instance_id()]; //done this way because wait until full size is accommodated
}
object->connect(CoreStringName(property_list_changed), callable_mp(this, &EditorInspector::_changed_callback));
+
+ can_favorite = Object::cast_to<Node>(object) || Object::cast_to<Resource>(object);
+ _update_current_favorites();
+
update_tree();
}
@@ -4088,10 +4256,164 @@ void EditorInspector::_node_removed(Node *p_node) {
}
}
+void EditorInspector::_update_current_favorites() {
+ current_favorites.clear();
+ if (!can_favorite) {
+ return;
+ }
+
+ HashMap<String, PackedStringArray> favorites = EditorSettings::get_singleton()->get_favorite_properties();
+
+ // Fetch script properties.
+ Ref<Script> scr = object->get_script();
+ if (scr.is_valid()) {
+ List<PropertyInfo> plist;
+ // FIXME: Only properties from a saved script will be available, unsaved ones will be ignored.
+ // Can cause a little wonkiness, while nothing serious, would be nice to find a way to get
+ // unsaved ones without needing to get the entire property list of an object.
+ scr->get_script_property_list(&plist);
+
+ String path;
+ HashMap<String, LocalVector<String>> props;
+
+ for (PropertyInfo &p : plist) {
+ if (p.usage & PROPERTY_USAGE_CATEGORY) {
+ path = favorites.has(p.hint_string) ? p.hint_string : String();
+ } else if (p.usage & PROPERTY_USAGE_SCRIPT_VARIABLE && !path.is_empty()) {
+ props[path].push_back(p.name);
+ }
+ }
+
+ // Add favorited properties while removing invalid ones.
+ bool invalid_props = false;
+ for (const KeyValue<String, LocalVector<String>> &KV : props) {
+ path = KV.key;
+ for (int i = 0; i < favorites[path].size(); i++) {
+ String prop = favorites[path][i];
+ if (KV.value.has(prop)) {
+ current_favorites.append(prop);
+ } else {
+ invalid_props = true;
+ favorites[path].erase(prop);
+ i--;
+ }
+ }
+
+ if (favorites[path].is_empty()) {
+ favorites.erase(path);
+ }
+ }
+
+ if (invalid_props) {
+ EditorSettings::get_singleton()->set_favorite_properties(favorites);
+ }
+ }
+
+ // Fetch built-in properties.
+ StringName class_name = object->get_class_name();
+ for (const KeyValue<String, PackedStringArray> &KV : favorites) {
+ if (ClassDB::is_parent_class(class_name, KV.key)) {
+ current_favorites.append_array(KV.value);
+ }
+ }
+}
+
+void EditorInspector::_set_property_favorited(const String &p_path, bool p_favorited) {
+ if (!object) {
+ return;
+ }
+
+ StringName class_name = object->get_class_name();
+ while (!class_name.is_empty()) {
+ bool has_prop = ClassDB::has_property(class_name, p_path, true);
+ if (has_prop) {
+ break;
+ }
+
+ class_name = ClassDB::get_parent_class_nocheck(class_name);
+ }
+
+ if (class_name.is_empty()) {
+ Ref<Script> scr = object->get_script();
+ if (scr.is_valid()) {
+ List<PropertyInfo> plist;
+ scr->get_script_property_list(&plist);
+
+ String path;
+ for (PropertyInfo &p : plist) {
+ if (p.usage & PROPERTY_USAGE_CATEGORY) {
+ path = p.hint_string;
+ } else if (p.usage & PROPERTY_USAGE_SCRIPT_VARIABLE && p.name == p_path) {
+ class_name = path;
+ break;
+ }
+ }
+ }
+
+ ERR_FAIL_COND_MSG(class_name.is_empty(), "Can't favorite invalid property. If said property was from a script and recently renamed, try saving it first.");
+ }
+
+ HashMap<String, PackedStringArray> favorites = EditorSettings::get_singleton()->get_favorite_properties();
+ if (p_favorited) {
+ current_favorites.append(p_path);
+ favorites[class_name].append(p_path);
+ } else {
+ current_favorites.erase(p_path);
+
+ if (favorites.has(class_name) && favorites[class_name].has(p_path)) {
+ if (favorites[class_name].size() > 1) {
+ favorites[class_name].erase(p_path);
+ } else {
+ favorites.erase(class_name);
+ }
+ }
+ }
+ EditorSettings::get_singleton()->set_favorite_properties(favorites);
+
+ update_tree();
+}
+
+void EditorInspector::_clear_current_favorites() {
+ current_favorites.clear();
+
+ HashMap<String, PackedStringArray> favorites = EditorSettings::get_singleton()->get_favorite_properties();
+
+ Ref<Script> scr = object->get_script();
+ if (scr.is_valid()) {
+ List<PropertyInfo> plist;
+ scr->get_script_property_list(&plist);
+
+ for (PropertyInfo &p : plist) {
+ if (p.usage & PROPERTY_USAGE_CATEGORY && favorites.has(p.hint_string)) {
+ favorites.erase(p.hint_string);
+ }
+ }
+ }
+
+ StringName class_name = object->get_class_name();
+ while (class_name) {
+ if (favorites.has(class_name)) {
+ favorites.erase(class_name);
+ }
+
+ class_name = ClassDB::get_parent_class(class_name);
+ }
+
+ EditorSettings::get_singleton()->set_favorite_properties(favorites);
+ update_tree();
+}
+
void EditorInspector::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_THEME_CHANGED: {
- main_vbox->add_theme_constant_override("separation", get_theme_constant(SNAME("v_separation"), SNAME("EditorInspector")));
+ favorites_category->icon = get_editor_theme_icon(SNAME("Favorites"));
+
+ int separation = get_theme_constant(SNAME("v_separation"), SNAME("EditorInspector"));
+ base_vbox->add_theme_constant_override("separation", separation);
+ begin_vbox->add_theme_constant_override("separation", separation);
+ favorites_section->add_theme_constant_override("separation", separation);
+ favorites_groups_vbox->add_theme_constant_override("separation", separation);
+ main_vbox->add_theme_constant_override("separation", separation);
} break;
case NOTIFICATION_READY: {
@@ -4189,6 +4511,7 @@ void EditorInspector::_notification(int p_what) {
void EditorInspector::_changed_callback() {
//this is called when property change is notified via notify_property_list_changed()
if (object != nullptr) {
+ _update_current_favorites();
_edit_request_change(object, String());
}
}
@@ -4278,6 +4601,14 @@ void EditorInspector::_add_meta_confirm() {
undo_redo->commit_action();
}
+void EditorInspector::_handle_menu_option(int p_option) {
+ switch (p_option) {
+ case MENU_UNFAVORITE_ALL:
+ _clear_current_favorites();
+ break;
+ }
+}
+
void EditorInspector::_bind_methods() {
ClassDB::bind_method("_edit_request_change", &EditorInspector::_edit_request_change);
ClassDB::bind_method("get_selected_path", &EditorInspector::get_selected_path);
@@ -4296,9 +4627,36 @@ void EditorInspector::_bind_methods() {
EditorInspector::EditorInspector() {
object = nullptr;
+
+ base_vbox = memnew(VBoxContainer);
+ base_vbox->set_h_size_flags(SIZE_EXPAND_FILL);
+ add_child(base_vbox);
+
+ begin_vbox = memnew(VBoxContainer);
+ base_vbox->add_child(begin_vbox);
+ begin_vbox->hide();
+
+ favorites_section = memnew(VBoxContainer);
+ base_vbox->add_child(favorites_section);
+ favorites_section->hide();
+
+ favorites_category = memnew(EditorInspectorCategory);
+ favorites_category->set_as_favorite(this);
+ favorites_section->add_child(favorites_category);
+ favorites_category->label = TTR("Favorites");
+
+ favorites_vbox = memnew(VBoxContainer);
+ favorites_section->add_child(favorites_vbox);
+ favorites_groups_vbox = memnew(VBoxContainer);
+ favorites_section->add_child(favorites_groups_vbox);
+
+ favorites_separator = memnew(HSeparator);
+ favorites_section->add_child(favorites_separator);
+ favorites_separator->hide();
+
main_vbox = memnew(VBoxContainer);
- main_vbox->set_h_size_flags(SIZE_EXPAND_FILL);
- add_child(main_vbox);
+ base_vbox->add_child(main_vbox);
+
set_horizontal_scroll_mode(SCROLL_MODE_DISABLED);
set_follow_focus(true);
diff --git a/editor/editor_inspector.h b/editor/editor_inspector.h
index 0309213b76..2e4633ccea 100644
--- a/editor/editor_inspector.h
+++ b/editor/editor_inspector.h
@@ -41,6 +41,7 @@ class Button;
class ConfirmationDialog;
class EditorInspector;
class EditorValidationPanel;
+class HSeparator;
class LineEdit;
class MarginContainer;
class OptionButton;
@@ -64,6 +65,7 @@ public:
MENU_COPY_VALUE,
MENU_PASTE_VALUE,
MENU_COPY_PROPERTY_PATH,
+ MENU_FAVORITE_PROPERTY,
MENU_PIN_VALUE,
MENU_OPEN_DOCUMENTATION,
};
@@ -112,6 +114,9 @@ private:
bool pin_hidden = false;
bool pinned = false;
+ bool can_favorite = false;
+ bool favorited = false;
+
bool use_folding = false;
bool draw_top_bg = true;
@@ -134,7 +139,7 @@ private:
GDVIRTUAL0(_update_property)
GDVIRTUAL1(_set_read_only, bool)
- void _update_pin_flags();
+ void _update_flags();
protected:
bool has_borders = false;
@@ -218,6 +223,9 @@ public:
void set_name_split_ratio(float p_ratio);
float get_name_split_ratio() const;
+ void set_favoritable(bool p_favoritable);
+ bool is_favoritable() const;
+
void set_object_and_property(Object *p_object, const StringName &p_property);
virtual Control *make_custom_tooltip(const String &p_text) const override;
@@ -285,6 +293,7 @@ class EditorInspectorCategory : public Control {
String label;
String doc_class_name;
PopupMenu *menu = nullptr;
+ bool is_favorite = false;
void _handle_menu_option(int p_option);
@@ -293,10 +302,10 @@ protected:
virtual void gui_input(const Ref<InputEvent> &p_event) override;
public:
+ void set_as_favorite(EditorInspector *p_for_inspector);
+
virtual Size2 get_minimum_size() const override;
virtual Control *make_custom_tooltip(const String &p_text) const override;
-
- EditorInspectorCategory();
};
class EditorInspectorSection : public Container {
@@ -331,6 +340,7 @@ public:
virtual Size2 get_minimum_size() const override;
void setup(const String &p_section, const String &p_label, Object *p_object, const Color &p_bg_color, bool p_foldable, int p_indent_depth = 0, int p_level = 1);
+ String get_section() const;
VBoxContainer *get_vbox();
void unfold();
void fold();
@@ -480,13 +490,31 @@ public:
class EditorInspector : public ScrollContainer {
GDCLASS(EditorInspector, ScrollContainer);
+ friend class EditorInspectorCategory;
+
enum {
MAX_PLUGINS = 1024
};
static Ref<EditorInspectorPlugin> inspector_plugins[MAX_PLUGINS];
static int inspector_plugin_count;
+ // Right-click context menu options.
+ enum ClassMenuOption {
+ MENU_UNFAVORITE_ALL,
+ };
+
+ bool can_favorite = false;
+ PackedStringArray current_favorites;
+ VBoxContainer *favorites_section = nullptr;
+ EditorInspectorCategory *favorites_category = nullptr;
+ VBoxContainer *favorites_vbox = nullptr;
+ VBoxContainer *favorites_groups_vbox = nullptr;
+ HSeparator *favorites_separator = nullptr;
+
EditorInspector *root_inspector = nullptr;
+
+ VBoxContainer *base_vbox = nullptr;
+ VBoxContainer *begin_vbox = nullptr;
VBoxContainer *main_vbox = nullptr;
// Map used to cache the instantiated editors.
@@ -557,6 +585,10 @@ class EditorInspector : public ScrollContainer {
void _property_selected(const String &p_path, int p_focusable);
void _object_id_selected(const String &p_path, ObjectID p_id);
+ void _update_current_favorites();
+ void _set_property_favorited(const String &p_path, bool p_favorited);
+ void _clear_current_favorites();
+
void _node_removed(Node *p_node);
HashMap<StringName, int> per_array_page;
@@ -584,6 +616,8 @@ class EditorInspector : public ScrollContainer {
void _add_meta_confirm();
void _show_add_meta_dialog();
+ void _handle_menu_option(int p_option);
+
protected:
static void _bind_methods();
void _notification(int p_what);
diff --git a/editor/editor_interface.cpp b/editor/editor_interface.cpp
index 264c80dcbf..304b9d4b8c 100644
--- a/editor/editor_interface.cpp
+++ b/editor/editor_interface.cpp
@@ -337,6 +337,19 @@ void EditorInterface::popup_property_selector(Object *p_object, const Callable &
property_selector->connect(SNAME("canceled"), canceled_callback, CONNECT_DEFERRED);
}
+void EditorInterface::popup_method_selector(Object *p_object, const Callable &p_callback, const String &p_current_value) {
+ if (!method_selector) {
+ method_selector = memnew(PropertySelector);
+ get_base_control()->add_child(method_selector);
+ }
+
+ method_selector->select_method_from_instance(p_object, p_current_value);
+
+ const Callable callback = callable_mp(this, &EditorInterface::_method_selected);
+ method_selector->connect(SNAME("selected"), callback.bind(p_callback), CONNECT_DEFERRED);
+ method_selector->connect(SNAME("canceled"), callback.bind(String(), p_callback), CONNECT_DEFERRED);
+}
+
void EditorInterface::popup_quick_open(const Callable &p_callback, const TypedArray<StringName> &p_base_types) {
StringName required_type = SNAME("Resource");
Vector<StringName> base_types;
@@ -372,6 +385,18 @@ void EditorInterface::_property_selection_canceled(const Callable &p_callback) {
_call_dialog_callback(p_callback, NodePath(), "property selection canceled");
}
+void EditorInterface::_method_selected(const String &p_method_name, const Callable &p_callback) {
+ const Callable callback = callable_mp(this, &EditorInterface::_method_selected);
+ method_selector->disconnect(SNAME("selected"), callback);
+ method_selector->disconnect(SNAME("canceled"), callback);
+
+ if (p_method_name.is_empty()) {
+ _call_dialog_callback(p_callback, p_method_name, "method selection canceled");
+ } else {
+ _call_dialog_callback(p_callback, p_method_name, "method selected");
+ }
+}
+
void EditorInterface::_quick_open(const String &p_file_path, const Callable &p_callback) {
EditorQuickOpenDialog *quick_open = EditorNode::get_singleton()->get_quick_open_dialog();
quick_open->disconnect(SNAME("canceled"), callable_mp(this, &EditorInterface::_quick_open));
@@ -593,6 +618,7 @@ void EditorInterface::_bind_methods() {
ClassDB::bind_method(D_METHOD("popup_node_selector", "callback", "valid_types", "current_value"), &EditorInterface::popup_node_selector, DEFVAL(TypedArray<StringName>()), DEFVAL(Variant()));
ClassDB::bind_method(D_METHOD("popup_property_selector", "object", "callback", "type_filter", "current_value"), &EditorInterface::popup_property_selector, DEFVAL(PackedInt32Array()), DEFVAL(String()));
+ ClassDB::bind_method(D_METHOD("popup_method_selector", "object", "callback", "current_value"), &EditorInterface::popup_method_selector, DEFVAL(String()));
ClassDB::bind_method(D_METHOD("popup_quick_open", "callback", "base_types"), &EditorInterface::popup_quick_open, DEFVAL(TypedArray<StringName>()));
// Editor docks.
diff --git a/editor/editor_interface.h b/editor/editor_interface.h
index 4877444dac..c1032bf9b6 100644
--- a/editor/editor_interface.h
+++ b/editor/editor_interface.h
@@ -66,12 +66,14 @@ class EditorInterface : public Object {
// Editor dialogs.
PropertySelector *property_selector = nullptr;
+ PropertySelector *method_selector = nullptr;
SceneTreeDialog *node_selector = nullptr;
void _node_selected(const NodePath &p_node_paths, const Callable &p_callback);
void _node_selection_canceled(const Callable &p_callback);
void _property_selected(const String &p_property_name, const Callable &p_callback);
void _property_selection_canceled(const Callable &p_callback);
+ void _method_selected(const String &p_property_name, const Callable &p_callback);
void _quick_open(const String &p_file_path, const Callable &p_callback);
void _call_dialog_callback(const Callable &p_callback, const Variant &p_selected, const String &p_context);
@@ -139,6 +141,7 @@ public:
void popup_node_selector(const Callable &p_callback, const TypedArray<StringName> &p_valid_types = TypedArray<StringName>(), Node *p_current_value = nullptr);
// Must use Vector<int> because exposing Vector<Variant::Type> is not supported.
void popup_property_selector(Object *p_object, const Callable &p_callback, const PackedInt32Array &p_type_filter = PackedInt32Array(), const String &p_current_value = String());
+ void popup_method_selector(Object *p_object, const Callable &p_callback, const String &p_current_value = String());
void popup_quick_open(const Callable &p_callback, const TypedArray<StringName> &p_base_types = TypedArray<StringName>());
// Editor docks.
diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp
index 36b43b7e9b..f8e23ecc9d 100644
--- a/editor/editor_node.cpp
+++ b/editor/editor_node.cpp
@@ -2153,7 +2153,7 @@ void EditorNode::_dialog_action(String p_file) {
}
if (ml.is_null()) {
- ml = Ref<MeshLibrary>(memnew(MeshLibrary));
+ ml.instantiate();
}
MeshLibraryEditor::update_library_file(editor_data.get_edited_scene_root(), ml, merge_with_existing_library, apply_mesh_instance_transforms);
diff --git a/editor/editor_settings.cpp b/editor/editor_settings.cpp
index eeb8ceb1ed..44690f81f0 100644
--- a/editor/editor_settings.cpp
+++ b/editor/editor_settings.cpp
@@ -1229,7 +1229,7 @@ fail:
extra_config->set_value("init_projects", "list", list);
}
- singleton = Ref<EditorSettings>(memnew(EditorSettings));
+ singleton.instantiate();
singleton->set_path(config_file_path, true);
singleton->save_changed_setting = true;
singleton->_load_defaults(extra_config);
@@ -1496,10 +1496,26 @@ void EditorSettings::set_favorites(const Vector<String> &p_favorites) {
}
}
+void EditorSettings::set_favorite_properties(const HashMap<String, PackedStringArray> &p_favorite_properties) {
+ favorite_properties = p_favorite_properties;
+ String favorite_properties_file = EditorPaths::get_singleton()->get_project_settings_dir().path_join("favorite_properties");
+
+ Ref<ConfigFile> cf;
+ cf.instantiate();
+ for (const KeyValue<String, PackedStringArray> &kv : p_favorite_properties) {
+ cf->set_value(kv.key, "properties", kv.value);
+ }
+ cf->save(favorite_properties_file);
+}
+
Vector<String> EditorSettings::get_favorites() const {
return favorites;
}
+HashMap<String, PackedStringArray> EditorSettings::get_favorite_properties() const {
+ return favorite_properties;
+}
+
void EditorSettings::set_recent_dirs(const Vector<String> &p_recent_dirs) {
recent_dirs = p_recent_dirs;
String recent_dirs_file;
@@ -1522,23 +1538,51 @@ Vector<String> EditorSettings::get_recent_dirs() const {
void EditorSettings::load_favorites_and_recent_dirs() {
String favorites_file;
+ String favorite_properties_file;
String recent_dirs_file;
if (Engine::get_singleton()->is_project_manager_hint()) {
favorites_file = EditorPaths::get_singleton()->get_config_dir().path_join("favorite_dirs");
+ favorite_properties_file = EditorPaths::get_singleton()->get_config_dir().path_join("favorite_properties");
recent_dirs_file = EditorPaths::get_singleton()->get_config_dir().path_join("recent_dirs");
} else {
favorites_file = EditorPaths::get_singleton()->get_project_settings_dir().path_join("favorites");
+ favorite_properties_file = EditorPaths::get_singleton()->get_project_settings_dir().path_join("favorite_properties");
recent_dirs_file = EditorPaths::get_singleton()->get_project_settings_dir().path_join("recent_dirs");
}
+
+ /// File Favorites
+
Ref<FileAccess> f = FileAccess::open(favorites_file, FileAccess::READ);
if (f.is_valid()) {
String line = f->get_line().strip_edges();
while (!line.is_empty()) {
- favorites.push_back(line);
+ favorites.append(line);
line = f->get_line().strip_edges();
}
}
+ /// Inspector Favorites
+
+ Ref<ConfigFile> cf;
+ cf.instantiate();
+ if (cf->load(favorite_properties_file) == OK) {
+ List<String> secs;
+ cf->get_sections(&secs);
+
+ for (String &E : secs) {
+ PackedStringArray properties = PackedStringArray(cf->get_value(E, "properties"));
+ if (EditorNode::get_editor_data().is_type_recognized(E) || ResourceLoader::exists(E, "Script")) {
+ for (const String &property : properties) {
+ if (!favorite_properties[E].has(property)) {
+ favorite_properties[E].push_back(property);
+ }
+ }
+ }
+ }
+ }
+
+ /// Recent Directories
+
f = FileAccess::open(recent_dirs_file, FileAccess::READ);
if (f.is_valid()) {
String line = f->get_line().strip_edges();
diff --git a/editor/editor_settings.h b/editor/editor_settings.h
index d1ccedfe6c..3c8a4de866 100644
--- a/editor/editor_settings.h
+++ b/editor/editor_settings.h
@@ -102,6 +102,7 @@ private:
HashMap<String, List<Ref<InputEvent>>> builtin_action_overrides;
Vector<String> favorites;
+ HashMap<String, PackedStringArray> favorite_properties;
Vector<String> recent_dirs;
bool save_changed_setting = true;
@@ -176,6 +177,8 @@ public:
void set_favorites(const Vector<String> &p_favorites);
Vector<String> get_favorites() const;
+ void set_favorite_properties(const HashMap<String, PackedStringArray> &p_favorite_properties);
+ HashMap<String, PackedStringArray> get_favorite_properties() const;
void set_recent_dirs(const Vector<String> &p_recent_dirs);
Vector<String> get_recent_dirs() const;
void load_favorites_and_recent_dirs();
diff --git a/editor/export/codesign.cpp b/editor/export/codesign.cpp
index 72d496b04d..cc53068d48 100644
--- a/editor/export/codesign.cpp
+++ b/editor/export/codesign.cpp
@@ -1381,14 +1381,14 @@ Error CodeSign::_codesign_file(bool p_use_hardened_runtime, bool p_force, const
r_error_msg = TTR("Invalid entitlements file.");
ERR_FAIL_V_MSG(FAILED, "CodeSign: Invalid entitlements file.");
}
- cet = Ref<CodeSignEntitlementsText>(memnew(CodeSignEntitlementsText(entitlements)));
- ceb = Ref<CodeSignEntitlementsBinary>(memnew(CodeSignEntitlementsBinary(entitlements)));
+ cet.instantiate(entitlements);
+ ceb.instantiate(entitlements);
}
print_verbose("CodeSign: Generating requirements...");
Ref<CodeSignRequirements> rq;
String team_id = "";
- rq = Ref<CodeSignRequirements>(memnew(CodeSignRequirements()));
+ rq.instantiate();
// Sign executables.
for (int i = 0; i < files_to_sign.size(); i++) {
@@ -1487,7 +1487,7 @@ Error CodeSign::_codesign_file(bool p_use_hardened_runtime, bool p_force, const
print_verbose("CodeSign: Generating signature...");
Ref<CodeSignSignature> cs;
- cs = Ref<CodeSignSignature>(memnew(CodeSignSignature()));
+ cs.instantiate();
print_verbose("CodeSign: Writing signature superblob...");
// Write signature data to the executable.
diff --git a/editor/filesystem_dock.cpp b/editor/filesystem_dock.cpp
index 210249ebc9..fcd5a572b4 100644
--- a/editor/filesystem_dock.cpp
+++ b/editor/filesystem_dock.cpp
@@ -1648,21 +1648,27 @@ String FileSystemDock::_get_unique_name(const FileOrFolder &p_entry, const Strin
return new_path;
}
-void FileSystemDock::_update_favorites_list_after_move(const HashMap<String, String> &p_files_renames, const HashMap<String, String> &p_folders_renames) const {
- Vector<String> favorites_list = EditorSettings::get_singleton()->get_favorites();
- Vector<String> new_favorites;
-
- for (const String &old_path : favorites_list) {
+void FileSystemDock::_update_favorites_after_move(const HashMap<String, String> &p_files_renames, const HashMap<String, String> &p_folders_renames) const {
+ Vector<String> favorite_files = EditorSettings::get_singleton()->get_favorites();
+ Vector<String> new_favorite_files;
+ for (const String &old_path : favorite_files) {
if (p_folders_renames.has(old_path)) {
- new_favorites.push_back(p_folders_renames[old_path]);
+ new_favorite_files.push_back(p_folders_renames[old_path]);
} else if (p_files_renames.has(old_path)) {
- new_favorites.push_back(p_files_renames[old_path]);
+ new_favorite_files.push_back(p_files_renames[old_path]);
} else {
- new_favorites.push_back(old_path);
+ new_favorite_files.push_back(old_path);
}
}
+ EditorSettings::get_singleton()->set_favorites(new_favorite_files);
- EditorSettings::get_singleton()->set_favorites(new_favorites);
+ HashMap<String, PackedStringArray> favorite_properties = EditorSettings::get_singleton()->get_favorite_properties();
+ for (const KeyValue<String, String> &KV : p_files_renames) {
+ if (favorite_properties.has(KV.key)) {
+ favorite_properties.replace_key(KV.key, KV.value);
+ }
+ }
+ EditorSettings::get_singleton()->set_favorite_properties(favorite_properties);
}
void FileSystemDock::_make_scene_confirm() {
@@ -1805,7 +1811,7 @@ void FileSystemDock::_rename_operation_confirm() {
_update_resource_paths_after_move(file_renames, uids);
_update_dependencies_after_move(file_renames, file_owners);
_update_project_settings_after_move(file_renames, folder_renames);
- _update_favorites_list_after_move(file_renames, folder_renames);
+ _update_favorites_after_move(file_renames, folder_renames);
EditorSceneTabs::get_singleton()->set_current_tab(current_tab);
@@ -1966,7 +1972,7 @@ void FileSystemDock::_move_operation_confirm(const String &p_to_path, bool p_cop
_update_resource_paths_after_move(file_renames, uids);
_update_dependencies_after_move(file_renames, file_owners);
_update_project_settings_after_move(file_renames, folder_renames);
- _update_favorites_list_after_move(file_renames, folder_renames);
+ _update_favorites_after_move(file_renames, folder_renames);
EditorSceneTabs::get_singleton()->set_current_tab(current_tab);
diff --git a/editor/filesystem_dock.h b/editor/filesystem_dock.h
index 72d5ac3a98..fe83129c07 100644
--- a/editor/filesystem_dock.h
+++ b/editor/filesystem_dock.h
@@ -275,7 +275,7 @@ private:
void _before_move(HashMap<String, ResourceUID::ID> &r_uids, HashSet<String> &r_file_owners) const;
void _update_dependencies_after_move(const HashMap<String, String> &p_renames, const HashSet<String> &p_file_owners) const;
void _update_resource_paths_after_move(const HashMap<String, String> &p_renames, const HashMap<String, ResourceUID::ID> &p_uids) const;
- void _update_favorites_list_after_move(const HashMap<String, String> &p_files_renames, const HashMap<String, String> &p_folders_renames) const;
+ void _update_favorites_after_move(const HashMap<String, String> &p_files_renames, const HashMap<String, String> &p_folders_renames) const;
void _update_project_settings_after_move(const HashMap<String, String> &p_renames, const HashMap<String, String> &p_folders_renames);
String _get_unique_name(const FileOrFolder &p_entry, const String &p_at_path);
diff --git a/editor/gui/editor_bottom_panel.cpp b/editor/gui/editor_bottom_panel.cpp
index 2eb899f085..3cc1e37be0 100644
--- a/editor/gui/editor_bottom_panel.cpp
+++ b/editor/gui/editor_bottom_panel.cpp
@@ -31,35 +31,35 @@
#include "editor_bottom_panel.h"
#include "editor/debugger/editor_debugger_node.h"
-#include "editor/editor_about.h"
#include "editor/editor_command_palette.h"
#include "editor/editor_node.h"
#include "editor/editor_string_names.h"
-#include "editor/engine_update_label.h"
#include "editor/gui/editor_toaster.h"
#include "editor/gui/editor_version_button.h"
#include "editor/themes/editor_scale.h"
#include "scene/gui/box_container.h"
#include "scene/gui/button.h"
+#include "scene/gui/split_container.h"
void EditorBottomPanel::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_THEME_CHANGED: {
+ pin_button->set_button_icon(get_editor_theme_icon(SNAME("Pin")));
expand_button->set_button_icon(get_editor_theme_icon(SNAME("ExpandBottomDock")));
} break;
}
}
-void EditorBottomPanel::_switch_by_control(bool p_visible, Control *p_control) {
+void EditorBottomPanel::_switch_by_control(bool p_visible, Control *p_control, bool p_ignore_lock) {
for (int i = 0; i < items.size(); i++) {
if (items[i].control == p_control) {
- _switch_to_item(p_visible, i);
+ _switch_to_item(p_visible, i, p_ignore_lock);
return;
}
}
}
-void EditorBottomPanel::_switch_to_item(bool p_visible, int p_idx) {
+void EditorBottomPanel::_switch_to_item(bool p_visible, int p_idx, bool p_ignore_lock) {
ERR_FAIL_INDEX(p_idx, items.size());
if (items[p_idx].control->is_visible() == p_visible) {
@@ -70,6 +70,10 @@ void EditorBottomPanel::_switch_to_item(bool p_visible, int p_idx) {
ERR_FAIL_NULL(center_split);
if (p_visible) {
+ if (!p_ignore_lock && lock_panel_switching && pin_button->is_visible()) {
+ return;
+ }
+
for (int i = 0; i < items.size(); i++) {
items[i].button->set_pressed_no_signal(i == p_idx);
items[i].control->set_visible(i == p_idx);
@@ -80,18 +84,23 @@ void EditorBottomPanel::_switch_to_item(bool p_visible, int p_idx) {
} else {
add_theme_style_override(SceneStringName(panel), get_theme_stylebox(SNAME("BottomPanel"), EditorStringName(EditorStyles)));
}
+
center_split->set_dragger_visibility(SplitContainer::DRAGGER_VISIBLE);
center_split->set_collapsed(false);
+ pin_button->show();
+
+ expand_button->show();
if (expand_button->is_pressed()) {
EditorNode::get_top_split()->hide();
}
- expand_button->show();
} else {
add_theme_style_override(SceneStringName(panel), get_theme_stylebox(SNAME("BottomPanel"), EditorStringName(EditorStyles)));
items[p_idx].button->set_pressed_no_signal(false);
items[p_idx].control->set_visible(false);
center_split->set_dragger_visibility(SplitContainer::DRAGGER_HIDDEN);
center_split->set_collapsed(true);
+ pin_button->hide();
+
expand_button->hide();
if (expand_button->is_pressed()) {
EditorNode::get_top_split()->show();
@@ -101,13 +110,17 @@ void EditorBottomPanel::_switch_to_item(bool p_visible, int p_idx) {
last_opened_control = items[p_idx].control;
}
+void EditorBottomPanel::_pin_button_toggled(bool p_pressed) {
+ lock_panel_switching = p_pressed;
+}
+
void EditorBottomPanel::_expand_button_toggled(bool p_pressed) {
EditorNode::get_top_split()->set_visible(!p_pressed);
}
bool EditorBottomPanel::_button_drag_hover(const Vector2 &, const Variant &, Button *p_button, Control *p_control) {
if (!p_button->is_pressed()) {
- _switch_by_control(true, p_control);
+ _switch_by_control(true, p_control, true);
}
return false;
}
@@ -149,7 +162,7 @@ void EditorBottomPanel::load_layout_from_config(Ref<ConfigFile> p_config_file, c
Button *EditorBottomPanel::add_item(String p_text, Control *p_item, const Ref<Shortcut> &p_shortcut, bool p_at_front) {
Button *tb = memnew(Button);
tb->set_theme_type_variation("BottomPanelButton");
- tb->connect(SceneStringName(toggled), callable_mp(this, &EditorBottomPanel::_switch_by_control).bind(p_item));
+ tb->connect(SceneStringName(toggled), callable_mp(this, &EditorBottomPanel::_switch_by_control).bind(p_item, true));
tb->set_drag_forwarding(Callable(), callable_mp(this, &EditorBottomPanel::_button_drag_hover).bind(tb, p_item), Callable());
tb->set_text(p_text);
tb->set_shortcut(p_shortcut);
@@ -231,10 +244,10 @@ void EditorBottomPanel::toggle_last_opened_bottom_panel() {
// Select by control instead of index, so that the last bottom panel is opened correctly
// if it's been reordered since.
if (last_opened_control) {
- _switch_by_control(!last_opened_control->is_visible(), last_opened_control);
+ _switch_by_control(!last_opened_control->is_visible(), last_opened_control, true);
} else {
// Open the first panel in the list if no panel was opened this session.
- _switch_to_item(true, 0);
+ _switch_to_item(true, 0, true);
}
}
@@ -263,10 +276,17 @@ EditorBottomPanel::EditorBottomPanel() {
Control *h_spacer = memnew(Control);
bottom_hbox->add_child(h_spacer);
+ pin_button = memnew(Button);
+ bottom_hbox->add_child(pin_button);
+ pin_button->hide();
+ pin_button->set_theme_type_variation("FlatMenuButton");
+ pin_button->set_toggle_mode(true);
+ pin_button->set_tooltip_text(TTR("Pin Bottom Panel Switching"));
+ pin_button->connect(SceneStringName(toggled), callable_mp(this, &EditorBottomPanel::_pin_button_toggled));
+
expand_button = memnew(Button);
bottom_hbox->add_child(expand_button);
expand_button->hide();
- expand_button->set_flat(false);
expand_button->set_theme_type_variation("FlatMenuButton");
expand_button->set_toggle_mode(true);
expand_button->set_shortcut(ED_SHORTCUT_AND_COMMAND("editor/bottom_panel_expand", TTR("Expand Bottom Panel"), KeyModifierMask::SHIFT | Key::F12));
diff --git a/editor/gui/editor_bottom_panel.h b/editor/gui/editor_bottom_panel.h
index 3d44b3750a..950f0e2570 100644
--- a/editor/gui/editor_bottom_panel.h
+++ b/editor/gui/editor_bottom_panel.h
@@ -49,16 +49,19 @@ class EditorBottomPanel : public PanelContainer {
};
Vector<BottomPanelItem> items;
+ bool lock_panel_switching = false;
VBoxContainer *item_vbox = nullptr;
HBoxContainer *bottom_hbox = nullptr;
HBoxContainer *button_hbox = nullptr;
EditorToaster *editor_toaster = nullptr;
+ Button *pin_button = nullptr;
Button *expand_button = nullptr;
Control *last_opened_control = nullptr;
- void _switch_by_control(bool p_visible, Control *p_control);
- void _switch_to_item(bool p_visible, int p_idx);
+ void _switch_by_control(bool p_visible, Control *p_control, bool p_ignore_lock = false);
+ void _switch_to_item(bool p_visible, int p_idx, bool p_ignore_lock = false);
+ void _pin_button_toggled(bool p_pressed);
void _expand_button_toggled(bool p_pressed);
bool _button_drag_hover(const Vector2 &, const Variant &, Button *p_button, Control *p_control);
diff --git a/editor/gui/scene_tree_editor.cpp b/editor/gui/scene_tree_editor.cpp
index 0ada9aa8b2..c11da5dfdb 100644
--- a/editor/gui/scene_tree_editor.cpp
+++ b/editor/gui/scene_tree_editor.cpp
@@ -369,16 +369,14 @@ void SceneTreeEditor::_add_nodes(Node *p_node, TreeItem *p_parent) {
msg_temp += String::utf8("• ") + String(E.name) + "\n";
}
}
- }
- if (num_connections >= 1 || num_groups >= 1) {
- if (num_groups < 1) {
- msg_temp += "\n";
- }
- msg_temp += TTR("Click to show signals dock.");
+ } else {
+ msg_temp += "\n";
}
Ref<Texture2D> icon_temp;
SceneTreeEditorButton signal_temp = BUTTON_SIGNALS;
+ String msg_temp_end = TTR("Click to show signals dock.");
+
if (num_connections >= 1 && num_groups >= 1) {
icon_temp = get_editor_theme_icon(SNAME("SignalsAndGroups"));
} else if (num_connections >= 1) {
@@ -386,9 +384,11 @@ void SceneTreeEditor::_add_nodes(Node *p_node, TreeItem *p_parent) {
} else if (num_groups >= 1) {
icon_temp = get_editor_theme_icon(SNAME("Groups"));
signal_temp = BUTTON_GROUPS;
+ msg_temp_end = TTR("Click to show groups dock.");
}
if (num_connections >= 1 || num_groups >= 1) {
+ msg_temp += msg_temp_end;
item->add_button(0, icon_temp, signal_temp, false, msg_temp);
}
}
diff --git a/editor/icons/FPS.svg b/editor/icons/FPS.svg
new file mode 100644
index 0000000000..5ee818c308
--- /dev/null
+++ b/editor/icons/FPS.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><path fill="#e0e0e0" d="M7.25 4h-2v8h2v-2c1.656 0 3-1.344 3-3 0-1.657-1.344-3-3-3zm0 4v-2c.553 0 1 .448 1 1s-.447 1-1 1zM.25 7v5h2v-2h2v-2h-2v-1c0-.553.447-1 1-1h1v-2h-1c-1.656 0-3 1.344-3 3zM13.25 7c-.276 0-.5-.224-.5-.5s.224-.5.5-.5h2v-2h-2c-1.381 0-2.5 1.119-2.5 2.5s1.119 2.5 2.5 2.5c.276 0 .5.224.5.5s-.224.5-.5.5h-2v2h2c1.381 0 2.5-1.119 2.5-2.5s-1.119-2.5-2.5-2.5z"/></svg> \ No newline at end of file
diff --git a/editor/icons/Unfavorite.svg b/editor/icons/Unfavorite.svg
new file mode 100644
index 0000000000..78f1b90fd0
--- /dev/null
+++ b/editor/icons/Unfavorite.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><path fill="#e0e0e0" d="M 8 1.6992188 L 5.6269531 5.796875 L 1 6.8945312 L 4.2363281 10.302734 L 3.8769531 14.976562 L 8.0175781 12.998047 L 12.173828 14.941406 L 11.777344 10.287109 L 15 6.8945312 L 10.373047 5.796875 L 8 1.6992188 z M 8 4.2773438 L 9.4882812 6.8457031 L 12.388672 7.5332031 L 10.369141 9.6601562 L 10.617188 12.576172 L 8.0097656 11.359375 L 5.4160156 12.599609 L 5.640625 9.6699219 L 3.6113281 7.5332031 L 6.5117188 6.8457031 L 8 4.2773438 z"/></svg> \ No newline at end of file
diff --git a/editor/import/3d/editor_import_collada.cpp b/editor/import/3d/editor_import_collada.cpp
index 04a3f23154..c04278fc55 100644
--- a/editor/import/3d/editor_import_collada.cpp
+++ b/editor/import/3d/editor_import_collada.cpp
@@ -1263,7 +1263,7 @@ Error ColladaImport::_create_resources(Collada::Node *p_node, bool p_use_compres
//bleh, must ignore invalid
ERR_FAIL_COND_V(!collada.state.mesh_data_map.has(meshid), ERR_INVALID_DATA);
- mesh = Ref<ImporterMesh>(memnew(ImporterMesh));
+ mesh.instantiate();
const Collada::MeshData &meshdata = collada.state.mesh_data_map[meshid];
String name = meshdata.name;
if (name.is_empty()) {
diff --git a/editor/import/3d/resource_importer_scene.cpp b/editor/import/3d/resource_importer_scene.cpp
index edf7aa66f0..50082e45bf 100644
--- a/editor/import/3d/resource_importer_scene.cpp
+++ b/editor/import/3d/resource_importer_scene.cpp
@@ -3092,7 +3092,7 @@ Error ResourceImporterScene::import(const String &p_source_file, const String &p
if (!scr.is_valid()) {
EditorNode::add_io_error(TTR("Couldn't load post-import script:") + " " + post_import_script_path);
} else {
- post_import_script = Ref<EditorScenePostImport>(memnew(EditorScenePostImport));
+ post_import_script.instantiate();
post_import_script->set_script(scr);
if (!post_import_script->get_script_instance()) {
EditorNode::add_io_error(TTR("Invalid/broken script for post-import (check console):") + " " + post_import_script_path);
diff --git a/editor/import/audio_stream_import_settings.cpp b/editor/import/audio_stream_import_settings.cpp
index 0367e5e3f3..dd45806385 100644
--- a/editor/import/audio_stream_import_settings.cpp
+++ b/editor/import/audio_stream_import_settings.cpp
@@ -580,12 +580,10 @@ AudioStreamImportSettingsDialog::AudioStreamImportSettingsDialog() {
bar_beats_edit->set_max(32);
bar_beats_edit->connect(SceneStringName(value_changed), callable_mp(this, &AudioStreamImportSettingsDialog::_settings_changed).unbind(1));
interactive_hb->add_child(bar_beats_edit);
- interactive_hb->add_spacer();
main_vbox->add_margin_child(TTR("Music Playback:"), interactive_hb);
color_rect = memnew(ColorRect);
- main_vbox->add_margin_child(TTR("Preview:"), color_rect);
-
+ main_vbox->add_margin_child(TTR("Preview:"), color_rect, true);
color_rect->set_custom_minimum_size(Size2(600, 200) * EDSCALE);
color_rect->set_v_size_flags(Control::SIZE_EXPAND_FILL);
diff --git a/editor/plugins/animation_blend_tree_editor_plugin.cpp b/editor/plugins/animation_blend_tree_editor_plugin.cpp
index 34c0040cb7..eef890d013 100644
--- a/editor/plugins/animation_blend_tree_editor_plugin.cpp
+++ b/editor/plugins/animation_blend_tree_editor_plugin.cpp
@@ -1265,7 +1265,7 @@ AnimationNodeBlendTreeEditor::AnimationNodeBlendTreeEditor() {
open_file->set_file_mode(EditorFileDialog::FILE_MODE_OPEN_FILE);
open_file->connect("file_selected", callable_mp(this, &AnimationNodeBlendTreeEditor::_file_opened));
- animation_node_inspector_plugin = Ref<EditorInspectorPluginAnimationNodeAnimation>(memnew(EditorInspectorPluginAnimationNodeAnimation));
+ animation_node_inspector_plugin.instantiate();
EditorInspector::add_inspector_plugin(animation_node_inspector_plugin);
}
diff --git a/editor/plugins/animation_player_editor_plugin.cpp b/editor/plugins/animation_player_editor_plugin.cpp
index d089aedc63..4edd021b4d 100644
--- a/editor/plugins/animation_player_editor_plugin.cpp
+++ b/editor/plugins/animation_player_editor_plugin.cpp
@@ -580,8 +580,10 @@ float AnimationPlayerEditor::_get_editor_step() const {
const Ref<Animation> anim = player->get_animation(current);
ERR_FAIL_COND_V(anim.is_null(), 0.0);
+ float step = track_editor->get_snap_unit();
+
// Use more precise snapping when holding Shift
- return Input::get_singleton()->is_key_pressed(Key::SHIFT) ? anim->get_step() * 0.25 : anim->get_step();
+ return Input::get_singleton()->is_key_pressed(Key::SHIFT) ? step * 0.25 : step;
}
void AnimationPlayerEditor::_animation_name_edited() {
diff --git a/editor/plugins/canvas_item_editor_plugin.cpp b/editor/plugins/canvas_item_editor_plugin.cpp
index e3cf3dbbf2..62793fbcb5 100644
--- a/editor/plugins/canvas_item_editor_plugin.cpp
+++ b/editor/plugins/canvas_item_editor_plugin.cpp
@@ -5620,7 +5620,7 @@ CanvasItemEditor::CanvasItemEditor() {
snap_dialog->connect(SceneStringName(confirmed), callable_mp(this, &CanvasItemEditor::_snap_changed));
add_child(snap_dialog);
- select_sb = Ref<StyleBoxTexture>(memnew(StyleBoxTexture));
+ select_sb.instantiate();
selection_menu = memnew(PopupMenu);
add_child(selection_menu);
diff --git a/editor/plugins/gizmos/marker_3d_gizmo_plugin.cpp b/editor/plugins/gizmos/marker_3d_gizmo_plugin.cpp
index 39ae020d53..5a6527f876 100644
--- a/editor/plugins/gizmos/marker_3d_gizmo_plugin.cpp
+++ b/editor/plugins/gizmos/marker_3d_gizmo_plugin.cpp
@@ -36,7 +36,7 @@
#include "scene/3d/marker_3d.h"
Marker3DGizmoPlugin::Marker3DGizmoPlugin() {
- pos3d_mesh = Ref<ArrayMesh>(memnew(ArrayMesh));
+ pos3d_mesh.instantiate();
Vector<Vector3> cursor_points;
Vector<Color> cursor_colors;
diff --git a/editor/plugins/light_occluder_2d_editor_plugin.cpp b/editor/plugins/light_occluder_2d_editor_plugin.cpp
index 429add4540..e3b59f9bfb 100644
--- a/editor/plugins/light_occluder_2d_editor_plugin.cpp
+++ b/editor/plugins/light_occluder_2d_editor_plugin.cpp
@@ -35,8 +35,8 @@
Ref<OccluderPolygon2D> LightOccluder2DEditor::_ensure_occluder() const {
Ref<OccluderPolygon2D> occluder = node->get_occluder_polygon();
- if (!occluder.is_valid()) {
- occluder = Ref<OccluderPolygon2D>(memnew(OccluderPolygon2D));
+ if (occluder.is_null()) {
+ occluder.instantiate();
node->set_occluder_polygon(occluder);
}
return occluder;
diff --git a/editor/plugins/navigation_obstacle_3d_editor_plugin.cpp b/editor/plugins/navigation_obstacle_3d_editor_plugin.cpp
index 2eaab0fcbd..8b938d6f60 100644
--- a/editor/plugins/navigation_obstacle_3d_editor_plugin.cpp
+++ b/editor/plugins/navigation_obstacle_3d_editor_plugin.cpp
@@ -542,7 +542,7 @@ NavigationObstacle3DEditor::NavigationObstacle3DEditor() {
point_lines_meshinstance->set_mesh(point_lines_mesh);
point_lines_meshinstance->set_transform(Transform3D(Basis(), Vector3(0, 0, 0.00001)));
- line_material = Ref<StandardMaterial3D>(memnew(StandardMaterial3D));
+ line_material.instantiate();
line_material->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED);
line_material->set_transparency(StandardMaterial3D::TRANSPARENCY_ALPHA);
line_material->set_flag(StandardMaterial3D::FLAG_ALBEDO_FROM_VERTEX_COLOR, true);
@@ -550,7 +550,7 @@ NavigationObstacle3DEditor::NavigationObstacle3DEditor() {
line_material->set_flag(StandardMaterial3D::FLAG_DISABLE_FOG, true);
line_material->set_albedo(Color(1, 1, 1));
- handle_material = Ref<StandardMaterial3D>(memnew(StandardMaterial3D));
+ handle_material.instantiate();
handle_material->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED);
handle_material->set_flag(StandardMaterial3D::FLAG_USE_POINT_SIZE, true);
handle_material->set_transparency(StandardMaterial3D::TRANSPARENCY_ALPHA);
diff --git a/editor/plugins/navigation_polygon_editor_plugin.cpp b/editor/plugins/navigation_polygon_editor_plugin.cpp
index 3c71040408..c11a7cf20e 100644
--- a/editor/plugins/navigation_polygon_editor_plugin.cpp
+++ b/editor/plugins/navigation_polygon_editor_plugin.cpp
@@ -38,8 +38,8 @@
Ref<NavigationPolygon> NavigationPolygonEditor::_ensure_navpoly() const {
Ref<NavigationPolygon> navpoly = node->get_navigation_polygon();
- if (!navpoly.is_valid()) {
- navpoly = Ref<NavigationPolygon>(memnew(NavigationPolygon));
+ if (navpoly.is_null()) {
+ navpoly.instantiate();
node->set_navigation_polygon(navpoly);
}
return navpoly;
diff --git a/editor/plugins/node_3d_editor_plugin.cpp b/editor/plugins/node_3d_editor_plugin.cpp
index 5afe01025d..810d1674ca 100644
--- a/editor/plugins/node_3d_editor_plugin.cpp
+++ b/editor/plugins/node_3d_editor_plugin.cpp
@@ -7050,12 +7050,12 @@ void fragment() {
col.a = EDITOR_GET("editors/3d/manipulator_gizmo_opacity");
- move_gizmo[i] = Ref<ArrayMesh>(memnew(ArrayMesh));
- move_plane_gizmo[i] = Ref<ArrayMesh>(memnew(ArrayMesh));
- rotate_gizmo[i] = Ref<ArrayMesh>(memnew(ArrayMesh));
- scale_gizmo[i] = Ref<ArrayMesh>(memnew(ArrayMesh));
- scale_plane_gizmo[i] = Ref<ArrayMesh>(memnew(ArrayMesh));
- axis_gizmo[i] = Ref<ArrayMesh>(memnew(ArrayMesh));
+ move_gizmo[i].instantiate();
+ move_plane_gizmo[i].instantiate();
+ rotate_gizmo[i].instantiate();
+ scale_gizmo[i].instantiate();
+ scale_plane_gizmo[i].instantiate();
+ axis_gizmo[i].instantiate();
Ref<StandardMaterial3D> mat = memnew(StandardMaterial3D);
mat->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED);
@@ -7286,7 +7286,7 @@ void fragment() {
border_mat->set_shader(border_shader);
border_mat->set_shader_parameter("albedo", Color(0.75, 0.75, 0.75, col.a / 3.0));
- rotate_gizmo[3] = Ref<ArrayMesh>(memnew(ArrayMesh));
+ rotate_gizmo[3].instantiate();
rotate_gizmo[3]->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, arrays);
rotate_gizmo[3]->surface_set_material(0, border_mat);
}
@@ -8639,7 +8639,7 @@ Node3DEditor::Node3DEditor() {
gizmo.visible = true;
gizmo.scale = 1.0;
- viewport_environment = Ref<Environment>(memnew(Environment));
+ viewport_environment.instantiate();
VBoxContainer *vbc = this;
custom_camera = nullptr;
diff --git a/editor/plugins/path_3d_editor_plugin.cpp b/editor/plugins/path_3d_editor_plugin.cpp
index 91cff9f8e2..3a4b3a0ea2 100644
--- a/editor/plugins/path_3d_editor_plugin.cpp
+++ b/editor/plugins/path_3d_editor_plugin.cpp
@@ -922,7 +922,7 @@ Ref<EditorNode3DGizmo> Path3DGizmoPlugin::create_gizmo(Node3D *p_spatial) {
Path3D *path = Object::cast_to<Path3D>(p_spatial);
if (path) {
- ref = Ref<Path3DGizmo>(memnew(Path3DGizmo(path, disk_size)));
+ ref.instantiate(path, disk_size);
}
return ref;
diff --git a/editor/plugins/polygon_2d_editor_plugin.cpp b/editor/plugins/polygon_2d_editor_plugin.cpp
index 6cd04174a5..8ab08ff28f 100644
--- a/editor/plugins/polygon_2d_editor_plugin.cpp
+++ b/editor/plugins/polygon_2d_editor_plugin.cpp
@@ -275,7 +275,11 @@ void Polygon2DEditor::_uv_edit_mode_select(int p_mode) {
uv_button[UV_MODE_REMOVE_POLYGON]->hide();
uv_button[UV_MODE_PAINT_WEIGHT]->hide();
uv_button[UV_MODE_CLEAR_WEIGHT]->hide();
- _uv_mode(UV_MODE_EDIT_POINT);
+ if (node->get_polygon().is_empty()) {
+ _uv_mode(UV_MODE_CREATE);
+ } else {
+ _uv_mode(UV_MODE_EDIT_POINT);
+ }
bone_scroll_main_vb->hide();
bone_paint_strength->hide();
@@ -317,9 +321,16 @@ void Polygon2DEditor::_uv_edit_mode_select(int p_mode) {
uv_edit_draw->queue_redraw();
}
+void Polygon2DEditor::_uv_edit_popup_show() {
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
+ undo_redo->connect("version_changed", callable_mp(this, &Polygon2DEditor::_update_available_modes));
+}
+
void Polygon2DEditor::_uv_edit_popup_hide() {
EditorSettings::get_singleton()->set_project_metadata("dialog_bounds", "uv_editor", Rect2(uv_edit->get_position(), uv_edit->get_size()));
_cancel_editing();
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
+ undo_redo->disconnect("version_changed", callable_mp(this, &Polygon2DEditor::_update_available_modes));
}
void Polygon2DEditor::_menu_option(int p_option) {
@@ -346,6 +357,7 @@ void Polygon2DEditor::_menu_option(int p_option) {
uv_edit->popup_centered_ratio(0.85);
}
_update_bone_list();
+ _update_available_modes();
get_tree()->connect("process_frame", callable_mp(this, &Polygon2DEditor::_center_view), CONNECT_ONE_SHOT);
} break;
case UVEDIT_POLYGON_TO_UV: {
@@ -408,6 +420,7 @@ void Polygon2DEditor::_cancel_editing() {
node->set_polygons(polygons_prev);
_update_polygon_editing_state();
+ _update_available_modes();
} else if (uv_drag) {
uv_drag = false;
if (uv_edit_mode[0]->is_pressed()) { // Edit UV.
@@ -566,6 +579,7 @@ void Polygon2DEditor::_uv_input(const Ref<InputEvent> &p_input) {
uv_drag = false;
uv_create = false;
+ _update_available_modes();
_uv_mode(UV_MODE_EDIT_POINT);
_menu_option(MODE_EDIT);
} else {
@@ -973,6 +987,23 @@ void Polygon2DEditor::_uv_input(const Ref<InputEvent> &p_input) {
}
}
+void Polygon2DEditor::_update_available_modes() {
+ // Force point editing mode if there's no polygon yet.
+ if (node->get_polygon().is_empty()) {
+ if (!uv_edit_mode[1]->is_pressed()) {
+ uv_edit_mode[1]->set_pressed(true);
+ _uv_edit_mode_select(1);
+ }
+ uv_edit_mode[0]->set_disabled(true);
+ uv_edit_mode[2]->set_disabled(true);
+ uv_edit_mode[3]->set_disabled(true);
+ } else {
+ uv_edit_mode[0]->set_disabled(false);
+ uv_edit_mode[2]->set_disabled(false);
+ uv_edit_mode[3]->set_disabled(false);
+ }
+}
+
void Polygon2DEditor::_center_view() {
Size2 texture_size;
if (node->get_texture().is_valid()) {
@@ -1324,6 +1355,7 @@ Polygon2DEditor::Polygon2DEditor() {
add_child(uv_edit);
uv_edit->connect(SceneStringName(confirmed), callable_mp(this, &Polygon2DEditor::_uv_edit_popup_hide));
uv_edit->connect("canceled", callable_mp(this, &Polygon2DEditor::_uv_edit_popup_hide));
+ uv_edit->connect("about_to_popup", callable_mp(this, &Polygon2DEditor::_uv_edit_popup_show));
VBoxContainer *uv_main_vb = memnew(VBoxContainer);
uv_edit->add_child(uv_main_vb);
diff --git a/editor/plugins/polygon_2d_editor_plugin.h b/editor/plugins/polygon_2d_editor_plugin.h
index cb082ec513..4e1cd7172e 100644
--- a/editor/plugins/polygon_2d_editor_plugin.h
+++ b/editor/plugins/polygon_2d_editor_plugin.h
@@ -142,6 +142,7 @@ class Polygon2DEditor : public AbstractPolygon2DEditor {
void _cancel_editing();
void _update_polygon_editing_state();
+ void _update_available_modes();
void _center_view();
void _update_zoom_and_pan(bool p_zoom_at_center);
@@ -157,6 +158,7 @@ class Polygon2DEditor : public AbstractPolygon2DEditor {
void _set_snap_step_y(real_t p_val);
void _uv_edit_mode_select(int p_mode);
+ void _uv_edit_popup_show();
void _uv_edit_popup_hide();
void _bone_paint_selected(int p_index);
diff --git a/editor/plugins/polygon_3d_editor_plugin.cpp b/editor/plugins/polygon_3d_editor_plugin.cpp
index 9bb2d81549..017504f0d6 100644
--- a/editor/plugins/polygon_3d_editor_plugin.cpp
+++ b/editor/plugins/polygon_3d_editor_plugin.cpp
@@ -554,7 +554,7 @@ Polygon3DEditor::Polygon3DEditor() {
imgeom->set_mesh(imesh);
imgeom->set_transform(Transform3D(Basis(), Vector3(0, 0, 0.00001)));
- line_material = Ref<StandardMaterial3D>(memnew(StandardMaterial3D));
+ line_material.instantiate();
line_material->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED);
line_material->set_transparency(StandardMaterial3D::TRANSPARENCY_ALPHA);
line_material->set_flag(StandardMaterial3D::FLAG_ALBEDO_FROM_VERTEX_COLOR, true);
@@ -562,7 +562,7 @@ Polygon3DEditor::Polygon3DEditor() {
line_material->set_flag(StandardMaterial3D::FLAG_DISABLE_FOG, true);
line_material->set_albedo(Color(1, 1, 1));
- handle_material = Ref<StandardMaterial3D>(memnew(StandardMaterial3D));
+ handle_material.instantiate();
handle_material->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED);
handle_material->set_flag(StandardMaterial3D::FLAG_USE_POINT_SIZE, true);
handle_material->set_transparency(StandardMaterial3D::TRANSPARENCY_ALPHA);
diff --git a/editor/plugins/skeleton_3d_editor_plugin.cpp b/editor/plugins/skeleton_3d_editor_plugin.cpp
index 19fbd2dd3b..369a1fc864 100644
--- a/editor/plugins/skeleton_3d_editor_plugin.cpp
+++ b/editor/plugins/skeleton_3d_editor_plugin.cpp
@@ -1186,8 +1186,8 @@ Skeleton3DEditor::Skeleton3DEditor(EditorInspectorPluginSkeleton *e_plugin, Skel
singleton = this;
// Handle.
- handle_material = Ref<ShaderMaterial>(memnew(ShaderMaterial));
- handle_shader = Ref<Shader>(memnew(Shader));
+ handle_material.instantiate();
+ handle_shader.instantiate();
handle_shader->set_code(R"(
// Skeleton 3D gizmo handle shader.
diff --git a/main/main.cpp b/main/main.cpp
index fb21ee502f..bdadd24d35 100644
--- a/main/main.cpp
+++ b/main/main.cpp
@@ -653,7 +653,7 @@ void Main::print_help(const char *p_binary) {
print_help_option("--delta-smoothing <enable>", "Enable or disable frame delta smoothing [\"enable\", \"disable\"].\n");
print_help_option("--print-fps", "Print the frames per second to the stdout.\n");
#ifdef TOOLS_ENABLED
- print_help_option("--editor-pseudolocalization", "Enable pseudolocalization for the editor and the project manager.\n");
+ print_help_option("--editor-pseudolocalization", "Enable pseudolocalization for the editor and the project manager.\n", CLI_OPTION_AVAILABILITY_EDITOR);
#endif
print_help_title("Standalone tools");
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/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/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/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/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..2339936ec4 100644
--- a/modules/navigation/2d/nav_mesh_generator_2d.cpp
+++ b/modules/navigation/2d/nav_mesh_generator_2d.cpp
@@ -760,16 +760,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 +840,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 +924,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 +963,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 +975,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/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/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/platform/ios/export/export_plugin.cpp b/platform/ios/export/export_plugin.cpp
index d6cd2e0f3c..3c5a930bab 100644
--- a/platform/ios/export/export_plugin.cpp
+++ b/platform/ios/export/export_plugin.cpp
@@ -1082,7 +1082,7 @@ Error EditorExportPlatformIOS::_export_loading_screen_file(const Ref<EditorExpor
}
if (splash.is_null()) {
- splash = Ref<Image>(memnew(Image(boot_splash_png)));
+ splash.instantiate(boot_splash_png);
}
// Using same image for both @2x and @3x
diff --git a/scene/2d/tile_map.cpp b/scene/2d/tile_map.cpp
index ddb0635f6b..20cbbc091a 100644
--- a/scene/2d/tile_map.cpp
+++ b/scene/2d/tile_map.cpp
@@ -75,7 +75,8 @@ void TileMap::_set_tile_map_data_using_compatibility_format(int p_layer, TileMap
for (int i = 0; i < c; i += offset) {
const uint8_t *ptr = (const uint8_t *)&r[i];
uint8_t local[12];
- for (int j = 0; j < ((p_format >= TileMapDataFormat::TILE_MAP_DATA_FORMAT_2) ? 12 : 8); j++) {
+ const int buffer_size = (format == TILE_MAP_DATA_FORMAT_2) ? 12 : 8;
+ for (int j = 0; j < buffer_size; j++) {
local[j] = ptr[j];
}
diff --git a/scene/2d/tile_map_layer.cpp b/scene/2d/tile_map_layer.cpp
index 30bd59c1a2..c4a2f35d31 100644
--- a/scene/2d/tile_map_layer.cpp
+++ b/scene/2d/tile_map_layer.cpp
@@ -1460,6 +1460,24 @@ void TileMapLayer::_clear_runtime_update_tile_data_for_cell(CellData &r_cell_dat
}
}
+void TileMapLayer::_update_cells_callback(bool p_force_cleanup) {
+ if (!GDVIRTUAL_IS_OVERRIDDEN(_update_cells)) {
+ return;
+ }
+
+ // Check if we should cleanup everything.
+ bool forced_cleanup = p_force_cleanup || !enabled || tile_set.is_null() || !is_visible_in_tree();
+
+ // List all the dirty cell's positions to notify script of cell updates.
+ TypedArray<Vector2i> dirty_cell_positions;
+ for (SelfList<CellData> *cell_data_list_element = dirty.cell_list.first(); cell_data_list_element; cell_data_list_element = cell_data_list_element->next()) {
+ CellData &cell_data = *cell_data_list_element->self();
+ dirty_cell_positions.push_back(cell_data.coords);
+ }
+
+ GDVIRTUAL_CALL(_update_cells, dirty_cell_positions, forced_cleanup);
+}
+
TileSet::TerrainsPattern TileMapLayer::_get_best_terrain_pattern_for_constraints(int p_terrain_set, const Vector2i &p_position, const RBSet<TerrainConstraint> &p_constraints, TileSet::TerrainsPattern p_current_pattern) const {
if (tile_set.is_null()) {
return TileSet::TerrainsPattern();
@@ -1673,6 +1691,10 @@ void TileMapLayer::_internal_update(bool p_force_cleanup) {
// This may add cells to the dirty list if a runtime modification has been notified.
_build_runtime_update_tile_data(p_force_cleanup);
+ // Callback for implementing custom subsystems.
+ // This may add to the dirty list if some cells are changed inside _update_cells.
+ _update_cells_callback(p_force_cleanup);
+
// Update all subsystems.
_rendering_update(p_force_cleanup);
_physics_update(p_force_cleanup);
@@ -1861,6 +1883,7 @@ void TileMapLayer::_bind_methods() {
GDVIRTUAL_BIND(_use_tile_data_runtime_update, "coords");
GDVIRTUAL_BIND(_tile_data_runtime_update, "coords", "tile_data");
+ GDVIRTUAL_BIND(_update_cells, "coords", "forced_cleanup");
ADD_PROPERTY(PropertyInfo(Variant::PACKED_BYTE_ARRAY, "tile_map_data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_tile_map_data_from_array", "get_tile_map_data_as_array");
diff --git a/scene/2d/tile_map_layer.h b/scene/2d/tile_map_layer.h
index 6cb481849c..912fa2be15 100644
--- a/scene/2d/tile_map_layer.h
+++ b/scene/2d/tile_map_layer.h
@@ -321,6 +321,7 @@ private:
bool _runtime_update_needs_all_cells_cleaned_up = false;
void _clear_runtime_update_tile_data();
void _clear_runtime_update_tile_data_for_cell(CellData &r_cell_data);
+ void _update_cells_callback(bool p_force_cleanup);
// Per-system methods.
#ifdef DEBUG_ENABLED
@@ -462,6 +463,7 @@ public:
void notify_runtime_tile_data_update();
GDVIRTUAL1R(bool, _use_tile_data_runtime_update, Vector2i);
GDVIRTUAL2(_tile_data_runtime_update, Vector2i, TileData *);
+ GDVIRTUAL2(_update_cells, TypedArray<Vector2i>, bool);
// --- Shortcuts to methods defined in TileSet ---
Vector2i map_pattern(const Vector2i &p_position_in_tilemap, const Vector2i &p_coords_in_pattern, Ref<TileMapPattern> p_pattern);
diff --git a/scene/2d/touch_screen_button.cpp b/scene/2d/touch_screen_button.cpp
index b968f39ccb..220608a250 100644
--- a/scene/2d/touch_screen_button.cpp
+++ b/scene/2d/touch_screen_button.cpp
@@ -430,6 +430,6 @@ void TouchScreenButton::_bind_methods() {
}
TouchScreenButton::TouchScreenButton() {
- unit_rect = Ref<RectangleShape2D>(memnew(RectangleShape2D));
+ unit_rect.instantiate();
unit_rect->set_size(Vector2(1, 1));
}
diff --git a/scene/3d/label_3d.cpp b/scene/3d/label_3d.cpp
index 6b3510a72a..01f15137be 100644
--- a/scene/3d/label_3d.cpp
+++ b/scene/3d/label_3d.cpp
@@ -318,7 +318,7 @@ Ref<TriangleMesh> Label3D::generate_triangle_mesh() const {
facesw[j] = vtx;
}
- triangle_mesh = Ref<TriangleMesh>(memnew(TriangleMesh));
+ triangle_mesh.instantiate();
triangle_mesh->create(faces);
return triangle_mesh;
diff --git a/scene/3d/navigation_agent_3d.cpp b/scene/3d/navigation_agent_3d.cpp
index 9242c2a2ea..faf138896a 100644
--- a/scene/3d/navigation_agent_3d.cpp
+++ b/scene/3d/navigation_agent_3d.cpp
@@ -1081,8 +1081,8 @@ void NavigationAgent3D::_update_debug_path() {
debug_path_instance = RenderingServer::get_singleton()->instance_create();
}
- if (!debug_path_mesh.is_valid()) {
- debug_path_mesh = Ref<ArrayMesh>(memnew(ArrayMesh));
+ if (debug_path_mesh.is_null()) {
+ debug_path_mesh.instantiate();
}
debug_path_mesh->clear_surfaces();
diff --git a/scene/3d/navigation_link_3d.cpp b/scene/3d/navigation_link_3d.cpp
index 0cce21b9d0..9e29384fc9 100644
--- a/scene/3d/navigation_link_3d.cpp
+++ b/scene/3d/navigation_link_3d.cpp
@@ -56,8 +56,8 @@ void NavigationLink3D::_update_debug_mesh() {
debug_instance = RenderingServer::get_singleton()->instance_create();
}
- if (!debug_mesh.is_valid()) {
- debug_mesh = Ref<ArrayMesh>(memnew(ArrayMesh));
+ if (debug_mesh.is_null()) {
+ debug_mesh.instantiate();
}
RID nav_map = get_world_3d()->get_navigation_map();
diff --git a/scene/3d/navigation_obstacle_3d.cpp b/scene/3d/navigation_obstacle_3d.cpp
index 2eb04a0054..72a7253a10 100644
--- a/scene/3d/navigation_obstacle_3d.cpp
+++ b/scene/3d/navigation_obstacle_3d.cpp
@@ -389,8 +389,8 @@ void NavigationObstacle3D::_update_fake_agent_radius_debug() {
if (!fake_agent_radius_debug_instance.is_valid()) {
fake_agent_radius_debug_instance = RenderingServer::get_singleton()->instance_create();
}
- if (!fake_agent_radius_debug_mesh.is_valid()) {
- fake_agent_radius_debug_mesh = Ref<ArrayMesh>(memnew(ArrayMesh));
+ if (fake_agent_radius_debug_mesh.is_null()) {
+ fake_agent_radius_debug_mesh.instantiate();
}
fake_agent_radius_debug_mesh->clear_surfaces();
@@ -487,8 +487,8 @@ void NavigationObstacle3D::_update_static_obstacle_debug() {
if (!static_obstacle_debug_instance.is_valid()) {
static_obstacle_debug_instance = RenderingServer::get_singleton()->instance_create();
}
- if (!static_obstacle_debug_mesh.is_valid()) {
- static_obstacle_debug_mesh = Ref<ArrayMesh>(memnew(ArrayMesh));
+ if (static_obstacle_debug_mesh.is_null()) {
+ static_obstacle_debug_mesh.instantiate();
}
static_obstacle_debug_mesh->clear_surfaces();
diff --git a/scene/3d/navigation_region_3d.cpp b/scene/3d/navigation_region_3d.cpp
index c0c254e7ed..2d67e4334e 100644
--- a/scene/3d/navigation_region_3d.cpp
+++ b/scene/3d/navigation_region_3d.cpp
@@ -492,8 +492,8 @@ void NavigationRegion3D::_update_debug_mesh() {
debug_instance = RenderingServer::get_singleton()->instance_create();
}
- if (!debug_mesh.is_valid()) {
- debug_mesh = Ref<ArrayMesh>(memnew(ArrayMesh));
+ if (debug_mesh.is_null()) {
+ debug_mesh.instantiate();
}
debug_mesh->clear_surfaces();
@@ -669,8 +669,8 @@ void NavigationRegion3D::_update_debug_edge_connections_mesh() {
debug_edge_connections_instance = RenderingServer::get_singleton()->instance_create();
}
- if (!debug_edge_connections_mesh.is_valid()) {
- debug_edge_connections_mesh = Ref<ArrayMesh>(memnew(ArrayMesh));
+ if (debug_edge_connections_mesh.is_null()) {
+ debug_edge_connections_mesh.instantiate();
}
debug_edge_connections_mesh->clear_surfaces();
diff --git a/scene/3d/path_3d.cpp b/scene/3d/path_3d.cpp
index 64259a24b0..00c83101b9 100644
--- a/scene/3d/path_3d.cpp
+++ b/scene/3d/path_3d.cpp
@@ -88,8 +88,8 @@ void Path3D::_update_debug_mesh() {
return;
}
- if (!debug_mesh.is_valid()) {
- debug_mesh = Ref<ArrayMesh>(memnew(ArrayMesh));
+ if (debug_mesh.is_null()) {
+ debug_mesh.instantiate();
}
if (!(curve.is_valid())) {
diff --git a/scene/3d/physics/ray_cast_3d.cpp b/scene/3d/physics/ray_cast_3d.cpp
index a9272388c1..b9159f072b 100644
--- a/scene/3d/physics/ray_cast_3d.cpp
+++ b/scene/3d/physics/ray_cast_3d.cpp
@@ -464,7 +464,7 @@ void RayCast3D::_create_debug_shape() {
}
if (debug_mesh.is_null()) {
- debug_mesh = Ref<ArrayMesh>(memnew(ArrayMesh));
+ debug_mesh.instantiate();
}
}
diff --git a/scene/3d/physics/shape_cast_3d.cpp b/scene/3d/physics/shape_cast_3d.cpp
index 8ad651fdf5..19c74bc925 100644
--- a/scene/3d/physics/shape_cast_3d.cpp
+++ b/scene/3d/physics/shape_cast_3d.cpp
@@ -546,7 +546,7 @@ void ShapeCast3D::_create_debug_shape() {
}
if (debug_mesh.is_null()) {
- debug_mesh = Ref<ArrayMesh>(memnew(ArrayMesh));
+ debug_mesh.instantiate();
}
}
diff --git a/scene/3d/sprite_3d.cpp b/scene/3d/sprite_3d.cpp
index 42460eec4c..6e3ada83ad 100644
--- a/scene/3d/sprite_3d.cpp
+++ b/scene/3d/sprite_3d.cpp
@@ -470,7 +470,7 @@ Ref<TriangleMesh> SpriteBase3D::generate_triangle_mesh() const {
facesw[j] = vtx;
}
- triangle_mesh = Ref<TriangleMesh>(memnew(TriangleMesh));
+ triangle_mesh.instantiate();
triangle_mesh->create(faces);
return triangle_mesh;
diff --git a/scene/gui/graph_edit.cpp b/scene/gui/graph_edit.cpp
index b1c47d858d..7e07f4a202 100644
--- a/scene/gui/graph_edit.cpp
+++ b/scene/gui/graph_edit.cpp
@@ -3001,5 +3001,5 @@ GraphEdit::GraphEdit() {
set_clip_contents(true);
- arranger = Ref<GraphEditArranger>(memnew(GraphEditArranger(this)));
+ arranger.instantiate(this);
}
diff --git a/scene/main/scene_tree.cpp b/scene/main/scene_tree.cpp
index 60cecfcfe7..16b41c8f9b 100644
--- a/scene/main/scene_tree.cpp
+++ b/scene/main/scene_tree.cpp
@@ -899,7 +899,7 @@ Ref<ArrayMesh> SceneTree::get_debug_contact_mesh() {
return debug_contact_mesh;
}
- debug_contact_mesh = Ref<ArrayMesh>(memnew(ArrayMesh));
+ debug_contact_mesh.instantiate();
Ref<StandardMaterial3D> mat = Ref<StandardMaterial3D>(memnew(StandardMaterial3D));
mat->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED);
diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp
index 5a90eb8f3e..1ee99099ec 100644
--- a/scene/main/viewport.cpp
+++ b/scene/main/viewport.cpp
@@ -1213,7 +1213,7 @@ void Viewport::set_world_2d(const Ref<World2D> &p_world_2d) {
}
} else {
WARN_PRINT("Invalid world_2d");
- world_2d = Ref<World2D>(memnew(World2D));
+ world_2d.instantiate();
}
world_2d->register_viewport(this);
@@ -4386,7 +4386,7 @@ void Viewport::set_world_3d(const Ref<World3D> &p_world_3d) {
own_world_3d = world_3d->duplicate();
world_3d->connect_changed(callable_mp(this, &Viewport::_own_world_3d_changed));
} else {
- own_world_3d = Ref<World3D>(memnew(World3D));
+ own_world_3d.instantiate();
}
}
@@ -4437,7 +4437,7 @@ void Viewport::set_use_own_world_3d(bool p_use_own_world_3d) {
own_world_3d = world_3d->duplicate();
world_3d->connect_changed(callable_mp(this, &Viewport::_own_world_3d_changed));
} else {
- own_world_3d = Ref<World3D>(memnew(World3D));
+ own_world_3d.instantiate();
}
} else {
own_world_3d = Ref<World3D>();
@@ -4963,7 +4963,7 @@ void Viewport::_validate_property(PropertyInfo &p_property) const {
}
Viewport::Viewport() {
- world_2d = Ref<World2D>(memnew(World2D));
+ world_2d.instantiate();
world_2d->register_viewport(this);
viewport = RenderingServer::get_singleton()->viewport_create();
diff --git a/scene/resources/2d/navigation_mesh_source_geometry_data_2d.h b/scene/resources/2d/navigation_mesh_source_geometry_data_2d.h
index b29c106fb5..2812925770 100644
--- a/scene/resources/2d/navigation_mesh_source_geometry_data_2d.h
+++ b/scene/resources/2d/navigation_mesh_source_geometry_data_2d.h
@@ -36,6 +36,8 @@
#include "scene/resources/2d/navigation_polygon.h"
class NavigationMeshSourceGeometryData2D : public Resource {
+ friend class NavMeshGenerator2D;
+
GDCLASS(NavigationMeshSourceGeometryData2D, Resource);
RWLock geometry_rwlock;
diff --git a/scene/resources/3d/shape_3d.cpp b/scene/resources/3d/shape_3d.cpp
index 5a79392ba5..259d82b7a0 100644
--- a/scene/resources/3d/shape_3d.cpp
+++ b/scene/resources/3d/shape_3d.cpp
@@ -73,7 +73,7 @@ Ref<ArrayMesh> Shape3D::get_debug_mesh() {
Vector<Vector3> lines = get_debug_mesh_lines();
- debug_mesh_cache = Ref<ArrayMesh>(memnew(ArrayMesh));
+ debug_mesh_cache.instantiate();
if (!lines.is_empty()) {
//make mesh
diff --git a/scene/resources/mesh.cpp b/scene/resources/mesh.cpp
index 848ae2713d..b0353b4f2c 100644
--- a/scene/resources/mesh.cpp
+++ b/scene/resources/mesh.cpp
@@ -385,7 +385,7 @@ Ref<TriangleMesh> Mesh::generate_triangle_mesh() const {
}
}
- triangle_mesh = Ref<TriangleMesh>(memnew(TriangleMesh));
+ triangle_mesh.instantiate();
triangle_mesh->create(faces);
return triangle_mesh;
diff --git a/scene/resources/navigation_mesh.cpp b/scene/resources/navigation_mesh.cpp
index 67ed65df0d..034d4d6996 100644
--- a/scene/resources/navigation_mesh.cpp
+++ b/scene/resources/navigation_mesh.cpp
@@ -392,8 +392,8 @@ Ref<ArrayMesh> NavigationMesh::get_debug_mesh() {
return debug_mesh;
}
- if (!debug_mesh.is_valid()) {
- debug_mesh = Ref<ArrayMesh>(memnew(ArrayMesh));
+ if (debug_mesh.is_null()) {
+ debug_mesh.instantiate();
} else {
debug_mesh->clear_surfaces();
}
diff --git a/scene/resources/packed_scene.cpp b/scene/resources/packed_scene.cpp
index bb3aad0f28..809a77a487 100644
--- a/scene/resources/packed_scene.cpp
+++ b/scene/resources/packed_scene.cpp
@@ -2195,7 +2195,7 @@ void PackedScene::replace_state(Ref<SceneState> p_by) {
}
void PackedScene::recreate_state() {
- state = Ref<SceneState>(memnew(SceneState));
+ state.instantiate();
state->set_path(get_path());
#ifdef TOOLS_ENABLED
state->set_last_modified_time(get_last_modified_time());
@@ -2286,5 +2286,5 @@ void PackedScene::_bind_methods() {
}
PackedScene::PackedScene() {
- state = Ref<SceneState>(memnew(SceneState));
+ state.instantiate();
}
diff --git a/scene/resources/portable_compressed_texture.cpp b/scene/resources/portable_compressed_texture.cpp
index 06b5ec6d5a..55bbed7c47 100644
--- a/scene/resources/portable_compressed_texture.cpp
+++ b/scene/resources/portable_compressed_texture.cpp
@@ -89,7 +89,7 @@ void PortableCompressedTexture2D::_set_data(const Vector<uint8_t> &p_data) {
data_size -= mipsize;
}
- image = Ref<Image>(memnew(Image(size.width, size.height, mipmaps, format, image_data)));
+ image.instantiate(size.width, size.height, mipmaps, format, image_data);
} break;
case COMPRESSION_MODE_BASIS_UNIVERSAL: {
@@ -100,7 +100,7 @@ void PortableCompressedTexture2D::_set_data(const Vector<uint8_t> &p_data) {
case COMPRESSION_MODE_S3TC:
case COMPRESSION_MODE_ETC2:
case COMPRESSION_MODE_BPTC: {
- image = Ref<Image>(memnew(Image(size.width, size.height, mipmaps, format, p_data.slice(20))));
+ image.instantiate(size.width, size.height, mipmaps, format, p_data.slice(20));
} break;
}
ERR_FAIL_COND(image.is_null());
diff --git a/servers/rendering/shader_compiler.cpp b/servers/rendering/shader_compiler.cpp
index 119ac677eb..f52963f98f 100644
--- a/servers/rendering/shader_compiler.cpp
+++ b/servers/rendering/shader_compiler.cpp
@@ -489,7 +489,7 @@ String ShaderCompiler::_dump_node_code(const SL::Node *p_node, int p_level, Gene
struct_code += _typestr(m->datatype);
}
struct_code += " ";
- struct_code += m->name;
+ struct_code += _mkid(m->name);
if (m->array_size > 0) {
struct_code += "[";
struct_code += itos(m->array_size);
@@ -1448,7 +1448,13 @@ String ShaderCompiler::_dump_node_code(const SL::Node *p_node, int p_level, Gene
} break;
case SL::Node::NODE_TYPE_MEMBER: {
SL::MemberNode *mnode = (SL::MemberNode *)p_node;
- code = _dump_node_code(mnode->owner, p_level, r_gen_code, p_actions, p_default_actions, p_assigning) + "." + mnode->name;
+ String name;
+ if (mnode->basetype == SL::TYPE_STRUCT) {
+ name = _mkid(mnode->name);
+ } else {
+ name = mnode->name;
+ }
+ code = _dump_node_code(mnode->owner, p_level, r_gen_code, p_actions, p_default_actions, p_assigning) + "." + name;
if (mnode->index_expression != nullptr) {
code += "[";
code += _dump_node_code(mnode->index_expression, p_level, r_gen_code, p_actions, p_default_actions, p_assigning);
diff --git a/servers/rendering_server.cpp b/servers/rendering_server.cpp
index 272908aa49..c0c6f1e904 100644
--- a/servers/rendering_server.cpp
+++ b/servers/rendering_server.cpp
@@ -2067,6 +2067,16 @@ void RenderingServer::_particles_set_trail_bind_poses(RID p_particles, const Typ
particles_set_trail_bind_poses(p_particles, tbposes);
}
+String RenderingServer::get_current_rendering_driver_name() const {
+ // Needs to remain in OS, since it's actually OS that interacts with it, but it's better exposed here.
+ return ::OS::get_singleton()->get_current_rendering_driver_name();
+}
+
+String RenderingServer::get_current_rendering_method() const {
+ // Needs to remain in OS, since it's actually OS that interacts with it, but it's better exposed here.
+ return ::OS::get_singleton()->get_current_rendering_method();
+}
+
Vector<uint8_t> _convert_surface_version_1_to_surface_version_2(uint64_t p_format, Vector<uint8_t> p_vertex_data, uint32_t p_vertex_count, uint32_t p_old_stride, uint32_t p_vertex_size, uint32_t p_normal_size, uint32_t p_position_stride, uint32_t p_normal_tangent_stride) {
Vector<uint8_t> new_vertex_data;
new_vertex_data.resize(p_vertex_data.size());
@@ -3423,6 +3433,9 @@ void RenderingServer::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_video_adapter_type"), &RenderingServer::get_video_adapter_type);
ClassDB::bind_method(D_METHOD("get_video_adapter_api_version"), &RenderingServer::get_video_adapter_api_version);
+ ClassDB::bind_method(D_METHOD("get_current_rendering_driver_name"), &RenderingServer::get_current_rendering_driver_name);
+ ClassDB::bind_method(D_METHOD("get_current_rendering_method"), &RenderingServer::get_current_rendering_method);
+
ClassDB::bind_method(D_METHOD("make_sphere_mesh", "latitudes", "longitudes", "radius"), &RenderingServer::make_sphere_mesh);
ClassDB::bind_method(D_METHOD("get_test_cube"), &RenderingServer::get_test_cube);
diff --git a/servers/rendering_server.h b/servers/rendering_server.h
index 90365a19eb..6de934aaeb 100644
--- a/servers/rendering_server.h
+++ b/servers/rendering_server.h
@@ -1787,6 +1787,9 @@ public:
virtual bool is_on_render_thread() = 0;
virtual void call_on_render_thread(const Callable &p_callable) = 0;
+ String get_current_rendering_driver_name() const;
+ String get_current_rendering_method() const;
+
#ifdef TOOLS_ENABLED
virtual void get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const override;
#endif
diff --git a/tests/scene/test_viewport.h b/tests/scene/test_viewport.h
index dde37944ec..06a38f234b 100644
--- a/tests/scene/test_viewport.h
+++ b/tests/scene/test_viewport.h
@@ -1577,7 +1577,7 @@ TEST_CASE("[SceneTree][Viewport] Physics Picking 2D") {
PickingCollider pc;
pc.a = memnew(TestArea2D);
pc.c = memnew(CollisionShape2D);
- pc.r = Ref<RectangleShape2D>(memnew(RectangleShape2D));
+ pc.r.instantiate();
pc.r->set_size(Size2(150, 150));
pc.c->set_shape(pc.r);
pc.a->add_child(pc.c);