summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--COPYRIGHT.txt6
-rw-r--r--SConstruct36
-rw-r--r--core/SCsub1
-rw-r--r--core/config/engine.cpp8
-rw-r--r--core/config/engine.h5
-rw-r--r--core/core_bind.cpp16
-rw-r--r--core/core_bind.h4
-rw-r--r--core/io/file_access_compressed.cpp2
-rw-r--r--core/io/file_access_compressed.h2
-rw-r--r--core/io/file_access_encrypted.cpp22
-rw-r--r--core/io/file_access_encrypted.h2
-rw-r--r--core/io/file_access_memory.cpp2
-rw-r--r--core/io/file_access_memory.h2
-rw-r--r--core/io/file_access_network.cpp2
-rw-r--r--core/io/file_access_network.h2
-rw-r--r--core/io/file_access_pack.cpp2
-rw-r--r--core/io/file_access_pack.h2
-rw-r--r--core/io/file_access_zip.cpp6
-rw-r--r--core/io/file_access_zip.h2
-rw-r--r--core/io/pck_packer.cpp2
-rw-r--r--core/io/xml_parser.cpp2
-rw-r--r--core/io/zip_io.cpp2
-rw-r--r--core/math/bvh_logic.inc14
-rw-r--r--core/math/bvh_public.inc46
-rw-r--r--core/math/bvh_refit.inc4
-rw-r--r--core/math/bvh_structs.inc1
-rw-r--r--core/math/bvh_tree.h15
-rw-r--r--core/math/dynamic_bvh.cpp5
-rw-r--r--core/os/file_access.cpp4
-rw-r--r--core/os/file_access.h2
-rw-r--r--core/variant/variant_call.cpp3
-rw-r--r--doc/classes/Directory.xml8
-rw-r--r--doc/classes/EditorInterface.xml6
-rw-r--r--doc/classes/EditorPaths.xml49
-rw-r--r--doc/classes/EditorResourcePreview.xml6
-rw-r--r--doc/classes/EditorSettings.xml9
-rw-r--r--doc/classes/EditorSpinSlider.xml2
-rw-r--r--doc/classes/File.xml8
-rw-r--r--doc/classes/ProjectSettings.xml10
-rw-r--r--doc/classes/Resource.xml1
-rw-r--r--doc/classes/ScrollContainer.xml9
-rw-r--r--doc/classes/TileMap.xml10
-rw-r--r--doc/classes/Tree.xml20
-rw-r--r--doc/classes/VisualShaderNodeBillboard.xml38
-rw-r--r--drivers/png/image_loader_png.cpp2
-rw-r--r--drivers/unix/dir_access_unix.cpp2
-rw-r--r--drivers/unix/file_access_unix.cpp2
-rw-r--r--drivers/unix/file_access_unix.h2
-rw-r--r--drivers/windows/file_access_windows.cpp2
-rw-r--r--drivers/windows/file_access_windows.h2
-rw-r--r--editor/code_editor.cpp2
-rw-r--r--editor/editor_audio_buses.cpp41
-rw-r--r--editor/editor_audio_buses.h8
-rw-r--r--editor/editor_export.cpp10
-rw-r--r--editor/editor_feature_profile.cpp167
-rw-r--r--editor/editor_feature_profile.h10
-rw-r--r--editor/editor_inspector.cpp2
-rw-r--r--editor/editor_log.cpp8
-rw-r--r--editor/editor_node.cpp29
-rw-r--r--editor/editor_node.h3
-rw-r--r--editor/editor_paths.cpp156
-rw-r--r--editor/editor_paths.h72
-rw-r--r--editor/editor_plugin.cpp5
-rw-r--r--editor/editor_plugin.h2
-rw-r--r--editor/editor_resource_picker.cpp26
-rw-r--r--editor/editor_resource_picker.h2
-rw-r--r--editor/editor_resource_preview.cpp2
-rw-r--r--editor/editor_resource_preview.h3
-rw-r--r--editor/editor_settings.cpp131
-rw-r--r--editor/editor_settings.h10
-rw-r--r--editor/editor_themes.cpp41
-rw-r--r--editor/export_template_manager.cpp2
-rw-r--r--editor/fileserver/editor_file_server.cpp2
-rw-r--r--editor/icons/Grid.svg2
-rw-r--r--editor/icons/RibbonTrailMesh.svg1
-rw-r--r--editor/icons/TubeTrailMesh.svg1
-rw-r--r--editor/import/resource_importer_image.cpp2
-rw-r--r--editor/plugin_config_dialog.cpp1
-rw-r--r--editor/plugins/asset_library_editor_plugin.cpp8
-rw-r--r--editor/plugins/canvas_item_editor_plugin.cpp4
-rw-r--r--editor/plugins/editor_preview_plugins.cpp2
-rw-r--r--editor/plugins/script_editor_plugin.cpp6
-rw-r--r--editor/plugins/script_text_editor.cpp10
-rw-r--r--editor/plugins/texture_region_editor_plugin.cpp1
-rw-r--r--editor/plugins/tiles/tile_atlas_view.cpp4
-rw-r--r--editor/plugins/tiles/tile_map_editor.cpp105
-rw-r--r--editor/plugins/tiles/tile_map_editor.h3
-rw-r--r--editor/plugins/tiles/tile_set_atlas_source_editor.cpp13
-rw-r--r--editor/plugins/tiles/tiles_editor_plugin.cpp1
-rw-r--r--editor/plugins/visual_shader_editor_plugin.cpp203
-rw-r--r--editor/plugins/visual_shader_editor_plugin.h4
-rw-r--r--editor/project_manager.cpp11
-rw-r--r--main/main.cpp6
-rw-r--r--methods.py13
-rw-r--r--misc/dist/ios_xcode/godot_ios.xcodeproj/project.pbxproj8
-rw-r--r--misc/dist/ios_xcode/godot_ios.xcodeproj/xcshareddata/xcschemes/godot_ios.xcscheme10
-rw-r--r--modules/bmp/image_loader_bmp.cpp2
-rw-r--r--modules/csg/csg_shape.cpp52
-rw-r--r--modules/fbx/editor_scene_importer_fbx.cpp2
-rw-r--r--modules/gdnative/pluginscript/pluginscript_script.cpp4
-rw-r--r--modules/gdnative/videodecoder/video_stream_gdnative.cpp2
-rw-r--r--modules/gdscript/editor/gdscript_highlighter.cpp4
-rw-r--r--modules/gdscript/gdscript.cpp2
-rw-r--r--modules/gdscript/gdscript_analyzer.cpp11
-rw-r--r--modules/gdscript/gdscript_byte_codegen.cpp94
-rw-r--r--modules/gdscript/gdscript_byte_codegen.h1
-rw-r--r--modules/gdscript/gdscript_cache.cpp2
-rw-r--r--modules/gdscript/gdscript_codegen.h1
-rw-r--r--modules/gdscript/gdscript_compiler.cpp20
-rw-r--r--modules/gdscript/gdscript_parser.h2
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/call_self_get_name.gd9
-rw-r--r--modules/gdscript/tests/scripts/analyzer/features/call_self_get_name.out3
-rw-r--r--modules/gdscript/tests/test_gdscript.cpp4
-rw-r--r--modules/glslang/register_types.cpp7
-rw-r--r--modules/gltf/doc_classes/GLTFNode.xml2
-rw-r--r--modules/gltf/gltf_document.cpp238
-rw-r--r--modules/gltf/gltf_document.h6
-rw-r--r--modules/gltf/gltf_node.cpp11
-rw-r--r--modules/gltf/gltf_node.h4
-rw-r--r--modules/jpg/image_loader_jpegd.cpp2
-rw-r--r--modules/mbedtls/crypto_mbedtls.cpp4
-rw-r--r--modules/minimp3/resource_importer_mp3.cpp2
-rw-r--r--modules/mono/godotsharp_dirs.cpp4
-rw-r--r--modules/mono/utils/string_utils.cpp2
-rw-r--r--modules/stb_vorbis/resource_importer_ogg_vorbis.cpp2
-rw-r--r--modules/svg/image_loader_svg.cpp2
-rw-r--r--modules/text_server_adv/dynamic_font_adv.cpp2
-rw-r--r--modules/text_server_adv/text_server_adv.cpp2
-rw-r--r--modules/text_server_fb/dynamic_font_fb.cpp2
-rw-r--r--modules/tga/image_loader_tga.cpp2
-rw-r--r--modules/tinyexr/image_loader_tinyexr.cpp2
-rw-r--r--modules/visual_script/visual_script_builtin_funcs.cpp1
-rw-r--r--modules/webm/video_stream_webm.cpp2
-rw-r--r--modules/webp/image_loader_webp.cpp2
-rw-r--r--platform/android/display_server_android.cpp15
-rw-r--r--platform/android/display_server_android.h2
-rw-r--r--platform/android/export/export.cpp12
-rw-r--r--platform/android/export/gradle_export_util.h7
-rw-r--r--platform/android/file_access_android.cpp2
-rw-r--r--platform/android/file_access_android.h2
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/vulkan/VkThread.kt6
-rw-r--r--platform/android/java_godot_lib_jni.cpp1
-rw-r--r--platform/iphone/export/export.cpp18
-rw-r--r--platform/javascript/api/javascript_tools_editor_plugin.cpp4
-rw-r--r--platform/javascript/export/export.cpp16
-rw-r--r--platform/osx/display_server_osx.mm2
-rw-r--r--platform/osx/export/export.cpp14
-rw-r--r--platform/uwp/export/export.cpp12
-rw-r--r--scene/2d/camera_2d.cpp9
-rw-r--r--scene/2d/light_2d.cpp4
-rw-r--r--scene/2d/tile_map.cpp61
-rw-r--r--scene/2d/tile_map.h48
-rw-r--r--scene/3d/audio_stream_player_3d.cpp2
-rw-r--r--scene/3d/reflection_probe.cpp4
-rw-r--r--scene/3d/skeleton_3d.cpp2
-rw-r--r--scene/3d/skeleton_ik_3d.cpp15
-rw-r--r--scene/3d/skeleton_ik_3d.h4
-rw-r--r--scene/animation/animation_node_state_machine.cpp2
-rw-r--r--scene/gui/aspect_ratio_container.cpp2
-rw-r--r--scene/gui/button.cpp2
-rw-r--r--scene/gui/color_picker.cpp2
-rw-r--r--scene/gui/control.cpp4
-rw-r--r--scene/gui/graph_node.cpp2
-rw-r--r--scene/gui/label.cpp2
-rw-r--r--scene/gui/line_edit.cpp2
-rw-r--r--scene/gui/link_button.cpp2
-rw-r--r--scene/gui/rich_text_label.cpp43
-rw-r--r--scene/gui/rich_text_label.h2
-rw-r--r--scene/gui/scroll_container.cpp42
-rw-r--r--scene/gui/scroll_container.h3
-rw-r--r--scene/gui/split_container.cpp2
-rw-r--r--scene/gui/text_edit.cpp5
-rw-r--r--scene/gui/tree.cpp101
-rw-r--r--scene/gui/tree.h8
-rw-r--r--scene/main/canvas_item.cpp4
-rw-r--r--scene/main/http_request.cpp20
-rw-r--r--scene/main/node.cpp2
-rw-r--r--scene/main/viewport.cpp4
-rw-r--r--scene/main/window.cpp6
-rw-r--r--scene/register_scene_types.cpp1
-rw-r--r--scene/resources/default_theme/default_theme.cpp6
-rw-r--r--scene/resources/material.cpp11
-rw-r--r--scene/resources/particles_material.cpp2
-rw-r--r--scene/resources/sky_material.cpp2
-rw-r--r--scene/resources/text_file.cpp2
-rw-r--r--scene/resources/text_line.cpp2
-rw-r--r--scene/resources/text_paragraph.cpp2
-rw-r--r--scene/resources/texture.cpp18
-rw-r--r--scene/resources/texture.h2
-rw-r--r--scene/resources/tile_set.cpp40
-rw-r--r--scene/resources/visual_shader.cpp174
-rw-r--r--scene/resources/visual_shader.h13
-rw-r--r--scene/resources/visual_shader_nodes.cpp168
-rw-r--r--scene/resources/visual_shader_nodes.h51
-rw-r--r--servers/audio/effects/audio_effect_distortion.cpp2
-rw-r--r--servers/audio_server.cpp3
-rw-r--r--servers/physics_3d/body_3d_sw.cpp23
-rw-r--r--servers/rendering/renderer_canvas_render.h1
-rw-r--r--servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.cpp4
-rw-r--r--servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.cpp4
-rw-r--r--servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp4
-rw-r--r--servers/rendering/renderer_rd/renderer_compositor_rd.cpp42
-rw-r--r--servers/rendering/renderer_rd/renderer_compositor_rd.h2
-rw-r--r--servers/rendering/renderer_rd/renderer_scene_sky_rd.cpp4
-rw-r--r--servers/rendering/renderer_rd/renderer_storage_rd.cpp4
-rw-r--r--servers/rendering/renderer_rd/shader_compiler_rd.cpp160
-rw-r--r--servers/rendering/renderer_rd/shader_rd.cpp290
-rw-r--r--servers/rendering/renderer_rd/shader_rd.h23
-rw-r--r--servers/rendering/rendering_device.cpp12
-rw-r--r--servers/rendering/rendering_device.h4
-rw-r--r--servers/rendering/shader_language.cpp1187
-rw-r--r--servers/rendering/shader_language.h61
-rw-r--r--servers/rendering/shader_types.cpp21
-rw-r--r--servers/rendering_server.cpp6
-rw-r--r--tests/test_image.h20
-rw-r--r--tests/test_main.cpp33
-rw-r--r--tests/test_math.cpp4
-rw-r--r--tests/test_pck_packer.h8
-rw-r--r--thirdparty/README.md7
-rw-r--r--thirdparty/meshoptimizer/simplifier.cpp16
-rw-r--r--thirdparty/misc/smolv.cpp2108
-rw-r--r--thirdparty/misc/smolv.h169
222 files changed, 5926 insertions, 1395 deletions
diff --git a/COPYRIGHT.txt b/COPYRIGHT.txt
index c007a7e2b3..b2a930a4bb 100644
--- a/COPYRIGHT.txt
+++ b/COPYRIGHT.txt
@@ -360,6 +360,12 @@ Comment: SMAZ
Copyright: 2006-2009, Salvatore Sanfilippo
License: BSD-3-clause
+Files: ./thirdparty/misc/smolv.cpp
+ ./thirdparty/misc/smolv.h
+Comment: SMOL-V
+Copyright: 2016-2020, Aras Pranckevicius
+License: public-domain or Unlicense or Expat
+
Files: ./thirdparty/misc/stb_rect_pack.h
./thirdparty/misc/stb_vorbis.c
Comment: stb libraries
diff --git a/SConstruct b/SConstruct
index 6516b42e59..2efa71767e 100644
--- a/SConstruct
+++ b/SConstruct
@@ -409,14 +409,27 @@ if selected_platform in platform_list:
env.Prepend(CCFLAGS=["/std:c++17"])
# Enforce our minimal compiler version requirements
- cc_version = methods.get_compiler_version(env) or [-1, -1]
- cc_version_major = cc_version[0]
- cc_version_minor = cc_version[1]
+ cc_version = methods.get_compiler_version(env) or {
+ "major": None,
+ "minor": None,
+ "patch": None,
+ "metadata1": None,
+ "metadata2": None,
+ "date": None,
+ }
+ cc_version_major = int(cc_version["major"] or -1)
+ cc_version_minor = int(cc_version["minor"] or -1)
+ cc_version_metadata1 = cc_version["metadata1"] or ""
if methods.using_gcc(env):
+ if cc_version_major == -1:
+ print(
+ "Couldn't detect compiler version, skipping version checks. "
+ "Build may fail if the compiler doesn't support C++17 fully."
+ )
# GCC 8 before 8.4 has a regression in the support of guaranteed copy elision
# which causes a build failure: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=86521
- if cc_version_major == 8 and cc_version_minor < 4:
+ elif cc_version_major == 8 and cc_version_minor < 4:
print(
"Detected GCC 8 version < 8.4, which is not supported due to a "
"regression in its C++17 guaranteed copy elision support. Use a "
@@ -432,10 +445,23 @@ if selected_platform in platform_list:
"SCons command line."
)
Exit(255)
+ elif cc_version_metadata1 == "win32":
+ print(
+ "Detected mingw version is not using posix threads. Only posix "
+ "version of mingw is supported. "
+ 'Use "update-alternatives --config <platform>-w64-mingw32-[gcc|g++]" '
+ "to switch to posix threads."
+ )
+ Exit(255)
elif methods.using_clang(env):
+ if cc_version_major == -1:
+ print(
+ "Couldn't detect compiler version, skipping version checks. "
+ "Build may fail if the compiler doesn't support C++17 fully."
+ )
# Apple LLVM versions differ from upstream LLVM version \o/, compare
# in https://en.wikipedia.org/wiki/Xcode#Toolchain_versions
- if env["platform"] == "osx" or env["platform"] == "iphone":
+ elif env["platform"] == "osx" or env["platform"] == "iphone":
vanilla = methods.is_vanilla_clang(env)
if vanilla and cc_version_major < 6:
print(
diff --git a/core/SCsub b/core/SCsub
index bdf8544840..e3ba46be02 100644
--- a/core/SCsub
+++ b/core/SCsub
@@ -59,6 +59,7 @@ thirdparty_misc_sources = [
"pcg.cpp",
"polypartition.cpp",
"clipper.cpp",
+ "smolv.cpp",
]
thirdparty_misc_sources = [thirdparty_misc_dir + file for file in thirdparty_misc_sources]
env_thirdparty.add_source_files(thirdparty_obj, thirdparty_misc_sources)
diff --git a/core/config/engine.cpp b/core/config/engine.cpp
index 2360d66438..c43e32868c 100644
--- a/core/config/engine.cpp
+++ b/core/config/engine.cpp
@@ -31,6 +31,7 @@
#include "engine.h"
#include "core/authors.gen.h"
+#include "core/config/project_settings.h"
#include "core/donors.gen.h"
#include "core/license.gen.h"
#include "core/version.h"
@@ -210,6 +211,13 @@ void Engine::get_singletons(List<Singleton> *p_singletons) {
}
}
+void Engine::set_shader_cache_path(const String &p_path) {
+ shader_cache_path = p_path;
+}
+String Engine::get_shader_cache_path() const {
+ return shader_cache_path;
+}
+
Engine *Engine::singleton = nullptr;
Engine *Engine::get_singleton() {
diff --git a/core/config/engine.h b/core/config/engine.h
index a9080e3dfd..276da1c7ea 100644
--- a/core/config/engine.h
+++ b/core/config/engine.h
@@ -72,6 +72,8 @@ private:
static Engine *singleton;
+ String shader_cache_path;
+
public:
static Engine *get_singleton();
@@ -121,6 +123,9 @@ public:
Dictionary get_license_info() const;
String get_license_text() const;
+ void set_shader_cache_path(const String &p_path);
+ String get_shader_cache_path() const;
+
bool is_abort_on_gpu_errors_enabled() const;
bool is_validation_layers_enabled() const;
diff --git a/core/core_bind.cpp b/core/core_bind.cpp
index 63238e3029..05265c41ad 100644
--- a/core/core_bind.cpp
+++ b/core/core_bind.cpp
@@ -1272,9 +1272,9 @@ uint64_t _File::get_position() const {
return f->get_position();
}
-uint64_t _File::get_len() const {
+uint64_t _File::get_length() const {
ERR_FAIL_COND_V_MSG(!f, 0, "File must be opened before use.");
- return f->get_len();
+ return f->get_length();
}
bool _File::eof_reached() const {
@@ -1536,7 +1536,7 @@ void _File::_bind_methods() {
ClassDB::bind_method(D_METHOD("seek", "position"), &_File::seek);
ClassDB::bind_method(D_METHOD("seek_end", "position"), &_File::seek_end, DEFVAL(0));
ClassDB::bind_method(D_METHOD("get_position"), &_File::get_position);
- ClassDB::bind_method(D_METHOD("get_len"), &_File::get_len);
+ ClassDB::bind_method(D_METHOD("get_length"), &_File::get_length);
ClassDB::bind_method(D_METHOD("eof_reached"), &_File::eof_reached);
ClassDB::bind_method(D_METHOD("get_8"), &_File::get_8);
ClassDB::bind_method(D_METHOD("get_16"), &_File::get_16);
@@ -1545,7 +1545,7 @@ void _File::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_float"), &_File::get_float);
ClassDB::bind_method(D_METHOD("get_double"), &_File::get_double);
ClassDB::bind_method(D_METHOD("get_real"), &_File::get_real);
- ClassDB::bind_method(D_METHOD("get_buffer", "len"), &_File::get_buffer);
+ ClassDB::bind_method(D_METHOD("get_buffer", "length"), &_File::get_buffer);
ClassDB::bind_method(D_METHOD("get_line"), &_File::get_line);
ClassDB::bind_method(D_METHOD("get_csv_line", "delim"), &_File::get_csv_line, DEFVAL(","));
ClassDB::bind_method(D_METHOD("get_as_text"), &_File::get_as_text);
@@ -1616,11 +1616,11 @@ bool _Directory::is_open() const {
return d && dir_open;
}
-Error _Directory::list_dir_begin(bool p_skip_navigational, bool p_skip_hidden) {
+Error _Directory::list_dir_begin(bool p_show_navigational, bool p_show_hidden) {
ERR_FAIL_COND_V_MSG(!is_open(), ERR_UNCONFIGURED, "Directory must be opened before use.");
- _list_skip_navigational = p_skip_navigational;
- _list_skip_hidden = p_skip_hidden;
+ _list_skip_navigational = !p_show_navigational;
+ _list_skip_hidden = !p_show_hidden;
return d->list_dir_begin();
}
@@ -1758,7 +1758,7 @@ Error _Directory::remove(String p_name) {
void _Directory::_bind_methods() {
ClassDB::bind_method(D_METHOD("open", "path"), &_Directory::open);
- ClassDB::bind_method(D_METHOD("list_dir_begin", "skip_navigational", "skip_hidden"), &_Directory::list_dir_begin, DEFVAL(false), DEFVAL(false));
+ ClassDB::bind_method(D_METHOD("list_dir_begin", "show_navigational", "show_hidden"), &_Directory::list_dir_begin, DEFVAL(false), DEFVAL(false));
ClassDB::bind_method(D_METHOD("get_next"), &_Directory::get_next);
ClassDB::bind_method(D_METHOD("current_is_dir"), &_Directory::current_is_dir);
ClassDB::bind_method(D_METHOD("list_dir_end"), &_Directory::list_dir_end);
diff --git a/core/core_bind.h b/core/core_bind.h
index 8bd96d8268..8253040a12 100644
--- a/core/core_bind.h
+++ b/core/core_bind.h
@@ -391,7 +391,7 @@ public:
void seek(int64_t p_position); // Seek to a given position.
void seek_end(int64_t p_position = 0); // Seek from the end of file.
uint64_t get_position() const; // Get position in the file.
- uint64_t get_len() const; // Get size of the file.
+ uint64_t get_length() const; // Get size of the file.
bool eof_reached() const; // Reading passed EOF.
@@ -467,7 +467,7 @@ public:
bool is_open() const;
- Error list_dir_begin(bool p_skip_navigational = false, bool p_skip_hidden = false); // This starts dir listing.
+ Error list_dir_begin(bool p_show_navigational = false, bool p_show_hidden = false); // This starts dir listing.
String get_next();
bool current_is_dir() const;
diff --git a/core/io/file_access_compressed.cpp b/core/io/file_access_compressed.cpp
index efcaa80fc5..e54c947340 100644
--- a/core/io/file_access_compressed.cpp
+++ b/core/io/file_access_compressed.cpp
@@ -237,7 +237,7 @@ uint64_t FileAccessCompressed::get_position() const {
}
}
-uint64_t FileAccessCompressed::get_len() const {
+uint64_t FileAccessCompressed::get_length() const {
ERR_FAIL_COND_V_MSG(!f, 0, "File must be opened before use.");
if (writing) {
return write_max;
diff --git a/core/io/file_access_compressed.h b/core/io/file_access_compressed.h
index d8a81c2417..19e4f241dd 100644
--- a/core/io/file_access_compressed.h
+++ b/core/io/file_access_compressed.h
@@ -75,7 +75,7 @@ public:
virtual void seek(uint64_t p_position); ///< seek to a given position
virtual void seek_end(int64_t p_position = 0); ///< seek from the end of file
virtual uint64_t get_position() const; ///< get position in the file
- virtual uint64_t get_len() const; ///< get size of the file
+ virtual uint64_t get_length() const; ///< get size of the file
virtual bool eof_reached() const; ///< reading passed EOF
diff --git a/core/io/file_access_encrypted.cpp b/core/io/file_access_encrypted.cpp
index 9a6bee7348..b9514c8c8b 100644
--- a/core/io/file_access_encrypted.cpp
+++ b/core/io/file_access_encrypted.cpp
@@ -69,7 +69,7 @@ Error FileAccessEncrypted::open_and_parse(FileAccess *p_base, const Vector<uint8
}
base = p_base->get_position();
- ERR_FAIL_COND_V(p_base->get_len() < base + length, ERR_FILE_CORRUPT);
+ ERR_FAIL_COND_V(p_base->get_length() < base + length, ERR_FILE_CORRUPT);
uint64_t ds = length;
if (ds % 16) {
ds += 16 - (ds % 16);
@@ -199,8 +199,8 @@ String FileAccessEncrypted::get_path_absolute() const {
}
void FileAccessEncrypted::seek(uint64_t p_position) {
- if (p_position > get_len()) {
- p_position = get_len();
+ if (p_position > get_length()) {
+ p_position = get_length();
}
pos = p_position;
@@ -208,14 +208,14 @@ void FileAccessEncrypted::seek(uint64_t p_position) {
}
void FileAccessEncrypted::seek_end(int64_t p_position) {
- seek(get_len() + p_position);
+ seek(get_length() + p_position);
}
uint64_t FileAccessEncrypted::get_position() const {
return pos;
}
-uint64_t FileAccessEncrypted::get_len() const {
+uint64_t FileAccessEncrypted::get_length() const {
return data.size();
}
@@ -225,7 +225,7 @@ bool FileAccessEncrypted::eof_reached() const {
uint8_t FileAccessEncrypted::get_8() const {
ERR_FAIL_COND_V_MSG(writing, 0, "File has not been opened in read mode.");
- if (pos >= get_len()) {
+ if (pos >= get_length()) {
eofed = true;
return 0;
}
@@ -239,7 +239,7 @@ uint64_t FileAccessEncrypted::get_buffer(uint8_t *p_dst, uint64_t p_length) cons
ERR_FAIL_COND_V(!p_dst && p_length > 0, -1);
ERR_FAIL_COND_V_MSG(writing, -1, "File has not been opened in read mode.");
- uint64_t to_copy = MIN(p_length, get_len() - pos);
+ uint64_t to_copy = MIN(p_length, get_length() - pos);
for (uint64_t i = 0; i < to_copy; i++) {
p_dst[i] = data[pos++];
}
@@ -258,11 +258,11 @@ Error FileAccessEncrypted::get_error() const {
void FileAccessEncrypted::store_buffer(const uint8_t *p_src, uint64_t p_length) {
ERR_FAIL_COND_MSG(!writing, "File has not been opened in write mode.");
- if (pos < get_len()) {
+ if (pos < get_length()) {
for (uint64_t i = 0; i < p_length; i++) {
store_8(p_src[i]);
}
- } else if (pos == get_len()) {
+ } else if (pos == get_length()) {
data.resize(pos + p_length);
for (uint64_t i = 0; i < p_length; i++) {
data.write[pos + i] = p_src[i];
@@ -280,10 +280,10 @@ void FileAccessEncrypted::flush() {
void FileAccessEncrypted::store_8(uint8_t p_dest) {
ERR_FAIL_COND_MSG(!writing, "File has not been opened in write mode.");
- if (pos < get_len()) {
+ if (pos < get_length()) {
data.write[pos] = p_dest;
pos++;
- } else if (pos == get_len()) {
+ } else if (pos == get_length()) {
data.push_back(p_dest);
pos++;
}
diff --git a/core/io/file_access_encrypted.h b/core/io/file_access_encrypted.h
index 8bea8c2585..00f14099f9 100644
--- a/core/io/file_access_encrypted.h
+++ b/core/io/file_access_encrypted.h
@@ -71,7 +71,7 @@ public:
virtual void seek(uint64_t p_position); ///< seek to a given position
virtual void seek_end(int64_t p_position = 0); ///< seek from the end of file
virtual uint64_t get_position() const; ///< get position in the file
- virtual uint64_t get_len() const; ///< get size of the file
+ virtual uint64_t get_length() const; ///< get size of the file
virtual bool eof_reached() const; ///< reading passed EOF
diff --git a/core/io/file_access_memory.cpp b/core/io/file_access_memory.cpp
index 14e24d6668..0114ab1765 100644
--- a/core/io/file_access_memory.cpp
+++ b/core/io/file_access_memory.cpp
@@ -117,7 +117,7 @@ uint64_t FileAccessMemory::get_position() const {
return pos;
}
-uint64_t FileAccessMemory::get_len() const {
+uint64_t FileAccessMemory::get_length() const {
ERR_FAIL_COND_V(!data, 0);
return length;
}
diff --git a/core/io/file_access_memory.h b/core/io/file_access_memory.h
index cc589dc259..4157531d01 100644
--- a/core/io/file_access_memory.h
+++ b/core/io/file_access_memory.h
@@ -52,7 +52,7 @@ public:
virtual void seek(uint64_t p_position); ///< seek to a given position
virtual void seek_end(int64_t p_position); ///< seek from the end of file
virtual uint64_t get_position() const; ///< get position in the file
- virtual uint64_t get_len() const; ///< get size of the file
+ virtual uint64_t get_length() const; ///< get size of the file
virtual bool eof_reached() const; ///< reading passed EOF
diff --git a/core/io/file_access_network.cpp b/core/io/file_access_network.cpp
index dedd5523ed..63a8f9c5b6 100644
--- a/core/io/file_access_network.cpp
+++ b/core/io/file_access_network.cpp
@@ -328,7 +328,7 @@ uint64_t FileAccessNetwork::get_position() const {
return pos;
}
-uint64_t FileAccessNetwork::get_len() const {
+uint64_t FileAccessNetwork::get_length() const {
ERR_FAIL_COND_V_MSG(!opened, 0, "File must be opened before use.");
return total_size;
}
diff --git a/core/io/file_access_network.h b/core/io/file_access_network.h
index 4810cca195..94b66c2480 100644
--- a/core/io/file_access_network.h
+++ b/core/io/file_access_network.h
@@ -137,7 +137,7 @@ public:
virtual void seek(uint64_t p_position); ///< seek to a given position
virtual void seek_end(int64_t p_position = 0); ///< seek from the end of file
virtual uint64_t get_position() const; ///< get position in the file
- virtual uint64_t get_len() const; ///< get size of the file
+ virtual uint64_t get_length() const; ///< get size of the file
virtual bool eof_reached() const; ///< reading passed EOF
diff --git a/core/io/file_access_pack.cpp b/core/io/file_access_pack.cpp
index 3e1c51b733..e9983ece47 100644
--- a/core/io/file_access_pack.cpp
+++ b/core/io/file_access_pack.cpp
@@ -279,7 +279,7 @@ uint64_t FileAccessPack::get_position() const {
return pos;
}
-uint64_t FileAccessPack::get_len() const {
+uint64_t FileAccessPack::get_length() const {
return pf.size;
}
diff --git a/core/io/file_access_pack.h b/core/io/file_access_pack.h
index 763c0fb327..9747e865c8 100644
--- a/core/io/file_access_pack.h
+++ b/core/io/file_access_pack.h
@@ -163,7 +163,7 @@ public:
virtual void seek(uint64_t p_position);
virtual void seek_end(int64_t p_position = 0);
virtual uint64_t get_position() const;
- virtual uint64_t get_len() const;
+ virtual uint64_t get_length() const;
virtual bool eof_reached() const;
diff --git a/core/io/file_access_zip.cpp b/core/io/file_access_zip.cpp
index d7ff1b7e00..b8383fd865 100644
--- a/core/io/file_access_zip.cpp
+++ b/core/io/file_access_zip.cpp
@@ -73,7 +73,7 @@ static long godot_seek(voidpf opaque, voidpf stream, uLong offset, int origin) {
pos = f->get_position() + offset;
break;
case ZLIB_FILEFUNC_SEEK_END:
- pos = f->get_len() + offset;
+ pos = f->get_length() + offset;
break;
default:
break;
@@ -270,7 +270,7 @@ void FileAccessZip::seek(uint64_t p_position) {
void FileAccessZip::seek_end(int64_t p_position) {
ERR_FAIL_COND(!zfile);
- unzSeekCurrentFile(zfile, get_len() + p_position);
+ unzSeekCurrentFile(zfile, get_length() + p_position);
}
uint64_t FileAccessZip::get_position() const {
@@ -278,7 +278,7 @@ uint64_t FileAccessZip::get_position() const {
return unztell(zfile);
}
-uint64_t FileAccessZip::get_len() const {
+uint64_t FileAccessZip::get_length() const {
ERR_FAIL_COND_V(!zfile, 0);
return file_info.uncompressed_size;
}
diff --git a/core/io/file_access_zip.h b/core/io/file_access_zip.h
index 91bdaafb68..cca14ded62 100644
--- a/core/io/file_access_zip.h
+++ b/core/io/file_access_zip.h
@@ -90,7 +90,7 @@ public:
virtual void seek(uint64_t p_position); ///< seek to a given position
virtual void seek_end(int64_t p_position = 0); ///< seek from the end of file
virtual uint64_t get_position() const; ///< get position in the file
- virtual uint64_t get_len() const; ///< get size of the file
+ virtual uint64_t get_length() const; ///< get size of the file
virtual bool eof_reached() const; ///< reading passed EOF
diff --git a/core/io/pck_packer.cpp b/core/io/pck_packer.cpp
index 4fe22e57d8..cadb02b5dd 100644
--- a/core/io/pck_packer.cpp
+++ b/core/io/pck_packer.cpp
@@ -120,7 +120,7 @@ Error PCKPacker::add_file(const String &p_file, const String &p_src, bool p_encr
pf.path = p_file;
pf.src_path = p_src;
pf.ofs = ofs;
- pf.size = f->get_len();
+ pf.size = f->get_length();
Vector<uint8_t> data = FileAccess::get_file_as_array(p_src);
{
diff --git a/core/io/xml_parser.cpp b/core/io/xml_parser.cpp
index f3ce2319e3..938d93a01b 100644
--- a/core/io/xml_parser.cpp
+++ b/core/io/xml_parser.cpp
@@ -476,7 +476,7 @@ Error XMLParser::open(const String &p_path) {
ERR_FAIL_COND_V_MSG(err != OK, err, "Cannot open file '" + p_path + "'.");
- length = file->get_len();
+ length = file->get_length();
ERR_FAIL_COND_V(length < 1, ERR_FILE_CORRUPT);
if (data) {
diff --git a/core/io/zip_io.cpp b/core/io/zip_io.cpp
index e0e491dc85..fb4c76aa7a 100644
--- a/core/io/zip_io.cpp
+++ b/core/io/zip_io.cpp
@@ -74,7 +74,7 @@ long zipio_seek(voidpf opaque, voidpf stream, uLong offset, int origin) {
pos = f->get_position() + offset;
break;
case ZLIB_FILEFUNC_SEEK_END:
- pos = f->get_len() + offset;
+ pos = f->get_length() + offset;
break;
default:
break;
diff --git a/core/math/bvh_logic.inc b/core/math/bvh_logic.inc
index d84c3f7830..afab08f151 100644
--- a/core/math/bvh_logic.inc
+++ b/core/math/bvh_logic.inc
@@ -20,17 +20,17 @@ void _logic_item_remove_and_reinsert(uint32_t p_ref_id) {
// some overlay elaborate way to find out which tree the node is in!
BVHHandle temp_handle;
temp_handle.set_id(p_ref_id);
- _current_tree = _handle_get_tree_id(temp_handle);
+ uint32_t tree_id = _handle_get_tree_id(temp_handle);
// remove and reinsert
BVHABB_CLASS abb;
- node_remove_item(p_ref_id, &abb);
+ node_remove_item(p_ref_id, tree_id, &abb);
// we must choose where to add to tree
- ref.tnode_id = _logic_choose_item_add_node(_root_node_id[_current_tree], abb);
+ ref.tnode_id = _logic_choose_item_add_node(_root_node_id[tree_id], abb);
_node_add_item(ref.tnode_id, p_ref_id, abb);
- refit_upward_and_balance(ref.tnode_id);
+ refit_upward_and_balance(ref.tnode_id, tree_id);
}
// from randy gaul balance function
@@ -66,7 +66,7 @@ BVHABB_CLASS _logic_abb_merge(const BVHABB_CLASS &a, const BVHABB_CLASS &b) {
// https://github.com/RandyGaul/qu3e
// It is MODIFIED from qu3e version.
// This is the only function used (and _logic_abb_merge helper function).
-int32_t _logic_balance(int32_t iA) {
+int32_t _logic_balance(int32_t iA, uint32_t p_tree_id) {
// return iA; // uncomment this to bypass balance
TNode *A = &_nodes[iA];
@@ -107,7 +107,7 @@ int32_t _logic_balance(int32_t iA) {
}
} else {
// check this .. seems dodgy
- change_root_node(iC);
+ change_root_node(iC, p_tree_id);
}
// Swap A and C
@@ -159,7 +159,7 @@ int32_t _logic_balance(int32_t iA) {
else {
// check this .. seems dodgy
- change_root_node(iB);
+ change_root_node(iB, p_tree_id);
}
// Swap A and B
diff --git a/core/math/bvh_public.inc b/core/math/bvh_public.inc
index f1b6d6b1bf..2c1e406712 100644
--- a/core/math/bvh_public.inc
+++ b/core/math/bvh_public.inc
@@ -53,16 +53,16 @@ BVHHandle item_add(T *p_userdata, bool p_active, const Bounds &p_aabb, int32_t p
// assign to handle to return
handle.set_id(ref_id);
- _current_tree = 0;
+ uint32_t tree_id = 0;
if (p_pairable) {
- _current_tree = 1;
+ tree_id = 1;
}
- create_root_node(_current_tree);
+ create_root_node(tree_id);
// we must choose where to add to tree
if (p_active) {
- ref->tnode_id = _logic_choose_item_add_node(_root_node_id[_current_tree], abb);
+ ref->tnode_id = _logic_choose_item_add_node(_root_node_id[tree_id], abb);
bool refit = _node_add_item(ref->tnode_id, ref_id, abb);
@@ -70,7 +70,7 @@ BVHHandle item_add(T *p_userdata, bool p_active, const Bounds &p_aabb, int32_t p
// only need to refit from the parent
const TNode &add_node = _nodes[ref->tnode_id];
if (add_node.parent_id != BVHCommon::INVALID) {
- refit_upward_and_balance(add_node.parent_id);
+ refit_upward_and_balance(add_node.parent_id, tree_id);
}
}
} else {
@@ -139,13 +139,13 @@ bool item_move(BVHHandle p_handle, const Bounds &p_aabb) {
return true;
}
- _current_tree = _handle_get_tree_id(p_handle);
+ uint32_t tree_id = _handle_get_tree_id(p_handle);
// remove and reinsert
- node_remove_item(ref_id);
+ node_remove_item(ref_id, tree_id);
// we must choose where to add to tree
- ref.tnode_id = _logic_choose_item_add_node(_root_node_id[_current_tree], abb);
+ ref.tnode_id = _logic_choose_item_add_node(_root_node_id[tree_id], abb);
// add to the tree
bool needs_refit = _node_add_item(ref.tnode_id, ref_id, abb);
@@ -167,7 +167,7 @@ bool item_move(BVHHandle p_handle, const Bounds &p_aabb) {
void item_remove(BVHHandle p_handle) {
uint32_t ref_id = p_handle.id();
- _current_tree = _handle_get_tree_id(p_handle);
+ uint32_t tree_id = _handle_get_tree_id(p_handle);
VERBOSE_PRINT("item_remove [" + itos(ref_id) + "] ");
@@ -187,7 +187,7 @@ void item_remove(BVHHandle p_handle) {
// remove the item from the node (only if active)
if (_refs[ref_id].is_active()) {
- node_remove_item(ref_id);
+ node_remove_item(ref_id, tree_id);
}
// remove the item reference
@@ -198,10 +198,10 @@ void item_remove(BVHHandle p_handle) {
}
// don't think refit_all is necessary?
- //refit_all(_current_tree);
+ //refit_all(_tree_id);
#ifdef BVH_VERBOSE_TREE
- _debug_recursive_print_tree(_current_tree);
+ _debug_recursive_print_tree(tree_id);
#endif
}
@@ -218,13 +218,13 @@ bool item_activate(BVHHandle p_handle, const Bounds &p_aabb) {
BVHABB_CLASS abb;
abb.from(p_aabb);
- _current_tree = _handle_get_tree_id(p_handle);
+ uint32_t tree_id = _handle_get_tree_id(p_handle);
// we must choose where to add to tree
- ref.tnode_id = _logic_choose_item_add_node(_root_node_id[_current_tree], abb);
+ ref.tnode_id = _logic_choose_item_add_node(_root_node_id[tree_id], abb);
_node_add_item(ref.tnode_id, ref_id, abb);
- refit_upward_and_balance(ref.tnode_id);
+ refit_upward_and_balance(ref.tnode_id, tree_id);
return true;
}
@@ -238,9 +238,11 @@ bool item_deactivate(BVHHandle p_handle) {
return false;
}
+ uint32_t tree_id = _handle_get_tree_id(p_handle);
+
// remove from tree
BVHABB_CLASS abb;
- node_remove_item(ref_id, &abb);
+ node_remove_item(ref_id, tree_id, &abb);
// mark as inactive
ref.set_inactive();
@@ -304,21 +306,21 @@ bool item_set_pairable(const BVHHandle &p_handle, bool p_pairable, uint32_t p_pa
BVHABB_CLASS abb = leaf.get_aabb(ref.item_id);
// make sure current tree is correct prior to changing
- _current_tree = _handle_get_tree_id(p_handle);
+ uint32_t tree_id = _handle_get_tree_id(p_handle);
// remove from old tree
- node_remove_item(ref_id);
+ node_remove_item(ref_id, tree_id);
// we must set the pairable AFTER getting the current tree
// because the pairable status determines which tree
ex.pairable = p_pairable;
// add to new tree
- _current_tree = _handle_get_tree_id(p_handle);
- create_root_node(_current_tree);
+ tree_id = _handle_get_tree_id(p_handle);
+ create_root_node(tree_id);
// we must choose where to add to tree
- ref.tnode_id = _logic_choose_item_add_node(_root_node_id[_current_tree], abb);
+ ref.tnode_id = _logic_choose_item_add_node(_root_node_id[tree_id], abb);
bool needs_refit = _node_add_item(ref.tnode_id, ref_id, abb);
// only need to refit from the PARENT
@@ -326,7 +328,7 @@ bool item_set_pairable(const BVHHandle &p_handle, bool p_pairable, uint32_t p_pa
// only need to refit from the parent
const TNode &add_node = _nodes[ref.tnode_id];
if (add_node.parent_id != BVHCommon::INVALID) {
- refit_upward_and_balance(add_node.parent_id);
+ refit_upward_and_balance(add_node.parent_id, tree_id);
}
}
} else {
diff --git a/core/math/bvh_refit.inc b/core/math/bvh_refit.inc
index 514c853ac5..717a3438c7 100644
--- a/core/math/bvh_refit.inc
+++ b/core/math/bvh_refit.inc
@@ -66,10 +66,10 @@ void refit_upward(uint32_t p_node_id) {
}
}
-void refit_upward_and_balance(uint32_t p_node_id) {
+void refit_upward_and_balance(uint32_t p_node_id, uint32_t p_tree_id) {
while (p_node_id != BVHCommon::INVALID) {
uint32_t before = p_node_id;
- p_node_id = _logic_balance(p_node_id);
+ p_node_id = _logic_balance(p_node_id, p_tree_id);
if (before != p_node_id) {
VERBOSE_PRINT("REBALANCED!");
diff --git a/core/math/bvh_structs.inc b/core/math/bvh_structs.inc
index 4133ba6c10..1d1e0e6468 100644
--- a/core/math/bvh_structs.inc
+++ b/core/math/bvh_structs.inc
@@ -162,7 +162,6 @@ enum { NUM_TREES = 2,
// Tree 1 - Pairable
// This is more efficient because in physics we only need check non pairable against the pairable tree.
uint32_t _root_node_id[NUM_TREES];
-int _current_tree = 0;
// these values may need tweaking according to the project
// the bound of the world, and the average velocities of the objects
diff --git a/core/math/bvh_tree.h b/core/math/bvh_tree.h
index 64c5f6e254..3169d31ec7 100644
--- a/core/math/bvh_tree.h
+++ b/core/math/bvh_tree.h
@@ -196,7 +196,7 @@ private:
new_child.parent_id = p_parent_id;
}
- void node_remove_child(uint32_t p_parent_id, uint32_t p_child_id, bool p_prevent_sibling = false) {
+ void node_remove_child(uint32_t p_parent_id, uint32_t p_child_id, uint32_t p_tree_id, bool p_prevent_sibling = false) {
TNode &parent = _nodes[p_parent_id];
BVH_ASSERT(!parent.is_leaf());
@@ -231,7 +231,7 @@ private:
if (grandparent_id == BVHCommon::INVALID) {
if (sibling_present) {
// change the root node
- change_root_node(sibling_id);
+ change_root_node(sibling_id, p_tree_id);
// delete the old root node as no longer needed
_nodes.free(p_parent_id);
@@ -243,16 +243,15 @@ private:
if (sibling_present) {
node_replace_child(grandparent_id, p_parent_id, sibling_id);
} else {
- node_remove_child(grandparent_id, p_parent_id, true);
+ node_remove_child(grandparent_id, p_parent_id, p_tree_id, true);
}
// put the node on the free list to recycle
_nodes.free(p_parent_id);
}
- // this relies on _current_tree being accurate
- void change_root_node(uint32_t p_new_root_id) {
- _root_node_id[_current_tree] = p_new_root_id;
+ void change_root_node(uint32_t p_new_root_id, uint32_t p_tree_id) {
+ _root_node_id[p_tree_id] = p_new_root_id;
TNode &root = _nodes[p_new_root_id];
// mark no parent
@@ -272,7 +271,7 @@ private:
node.neg_leaf_id = -(int)child_leaf_id;
}
- void node_remove_item(uint32_t p_ref_id, BVHABB_CLASS *r_old_aabb = nullptr) {
+ void node_remove_item(uint32_t p_ref_id, uint32_t p_tree_id, BVHABB_CLASS *r_old_aabb = nullptr) {
// get the reference
ItemRef &ref = _refs[p_ref_id];
uint32_t owner_node_id = ref.tnode_id;
@@ -336,7 +335,7 @@ private:
uint32_t parent_id = tnode.parent_id;
- node_remove_child(parent_id, owner_node_id);
+ node_remove_child(parent_id, owner_node_id, p_tree_id);
refit_upward(parent_id);
// put the node on the free list to recycle
diff --git a/core/math/dynamic_bvh.cpp b/core/math/dynamic_bvh.cpp
index 200095d8cb..8e596f0f9d 100644
--- a/core/math/dynamic_bvh.cpp
+++ b/core/math/dynamic_bvh.cpp
@@ -312,8 +312,11 @@ void DynamicBVH::optimize_incremental(int passes) {
if (passes < 0) {
passes = total_leaves;
}
- if (bvh_root && (passes > 0)) {
+ if (passes > 0) {
do {
+ if (!bvh_root) {
+ break;
+ }
Node *node = bvh_root;
unsigned bit = 0;
while (node->is_internal()) {
diff --git a/core/os/file_access.cpp b/core/os/file_access.cpp
index d00d0ac5bb..d1b940190a 100644
--- a/core/os/file_access.cpp
+++ b/core/os/file_access.cpp
@@ -380,7 +380,7 @@ uint64_t FileAccess::get_buffer(uint8_t *p_dst, uint64_t p_length) const {
String FileAccess::get_as_utf8_string() const {
Vector<uint8_t> sourcef;
- uint64_t len = get_len();
+ uint64_t len = get_length();
sourcef.resize(len + 1);
uint8_t *w = sourcef.ptrw();
@@ -565,7 +565,7 @@ Vector<uint8_t> FileAccess::get_file_as_array(const String &p_path, Error *r_err
ERR_FAIL_V_MSG(Vector<uint8_t>(), "Can't open file from path '" + String(p_path) + "'.");
}
Vector<uint8_t> data;
- data.resize(f->get_len());
+ data.resize(f->get_length());
f->get_buffer(data.ptrw(), data.size());
memdelete(f);
return data;
diff --git a/core/os/file_access.h b/core/os/file_access.h
index f9749c0fd1..1b9fb2f422 100644
--- a/core/os/file_access.h
+++ b/core/os/file_access.h
@@ -96,7 +96,7 @@ public:
virtual void seek(uint64_t p_position) = 0; ///< seek to a given position
virtual void seek_end(int64_t p_position = 0) = 0; ///< seek from the end of file with negative offset
virtual uint64_t get_position() const = 0; ///< get position in the file
- virtual uint64_t get_len() const = 0; ///< get size of the file
+ virtual uint64_t get_length() const = 0; ///< get size of the file
virtual bool eof_reached() const = 0; ///< reading passed EOF
diff --git a/core/variant/variant_call.cpp b/core/variant/variant_call.cpp
index efaaa8cd19..063611d415 100644
--- a/core/variant/variant_call.cpp
+++ b/core/variant/variant_call.cpp
@@ -611,6 +611,9 @@ struct _VariantCall {
if (buffer_size <= 0) {
ERR_FAIL_V_MSG(decompressed, "Decompression buffer size must be greater than zero.");
}
+ if (p_instance->size() == 0) {
+ ERR_FAIL_V_MSG(decompressed, "Compressed buffer size must be greater than zero.");
+ }
decompressed.resize(buffer_size);
int result = Compression::decompress(decompressed.ptrw(), buffer_size, p_instance->ptr(), p_instance->size(), mode);
diff --git a/doc/classes/Directory.xml b/doc/classes/Directory.xml
index a9d7960501..2c61d723cd 100644
--- a/doc/classes/Directory.xml
+++ b/doc/classes/Directory.xml
@@ -154,14 +154,14 @@
<method name="list_dir_begin">
<return type="int" enum="Error">
</return>
- <argument index="0" name="skip_navigational" type="bool" default="false">
+ <argument index="0" name="show_navigational" type="bool" default="false">
</argument>
- <argument index="1" name="skip_hidden" type="bool" default="false">
+ <argument index="1" name="show_hidden" type="bool" default="false">
</argument>
<description>
Initializes the stream used to list all files and directories using the [method get_next] function, closing the current opened stream if needed. Once the stream has been processed, it should typically be closed with [method list_dir_end].
- If [code]skip_navigational[/code] is [code]true[/code], [code].[/code] and [code]..[/code] are filtered out.
- If [code]skip_hidden[/code] is [code]true[/code], hidden files are filtered out.
+ If [code]show_navigational[/code] is [code]true[/code], [code].[/code] and [code]..[/code] are included too.
+ If [code]show_hidden[/code] is [code]true[/code], hidden files are included too.
</description>
</method>
<method name="list_dir_end">
diff --git a/doc/classes/EditorInterface.xml b/doc/classes/EditorInterface.xml
index a5328ce382..3f324bf1a0 100644
--- a/doc/classes/EditorInterface.xml
+++ b/doc/classes/EditorInterface.xml
@@ -57,6 +57,12 @@
[b]Note:[/b] This returns the main editor control containing the whole editor, not the 2D or 3D viewports specifically.
</description>
</method>
+ <method name="get_editor_paths">
+ <return type="EditorPaths">
+ </return>
+ <description>
+ </description>
+ </method>
<method name="get_editor_scale" qualifiers="const">
<return type="float">
</return>
diff --git a/doc/classes/EditorPaths.xml b/doc/classes/EditorPaths.xml
new file mode 100644
index 0000000000..b92927fd53
--- /dev/null
+++ b/doc/classes/EditorPaths.xml
@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="EditorPaths" inherits="Object" version="4.0">
+ <brief_description>
+ </brief_description>
+ <description>
+ </description>
+ <tutorials>
+ </tutorials>
+ <methods>
+ <method name="get_cache_dir" qualifiers="const">
+ <return type="String">
+ </return>
+ <description>
+ </description>
+ </method>
+ <method name="get_config_dir" qualifiers="const">
+ <return type="String">
+ </return>
+ <description>
+ </description>
+ </method>
+ <method name="get_data_dir" qualifiers="const">
+ <return type="String">
+ </return>
+ <description>
+ </description>
+ </method>
+ <method name="get_self_contained_file" qualifiers="const">
+ <return type="String">
+ </return>
+ <description>
+ </description>
+ </method>
+ <method name="get_settings_dir" qualifiers="const">
+ <return type="String">
+ </return>
+ <description>
+ </description>
+ </method>
+ <method name="is_self_contained" qualifiers="const">
+ <return type="bool">
+ </return>
+ <description>
+ </description>
+ </method>
+ </methods>
+ <constants>
+ </constants>
+</class>
diff --git a/doc/classes/EditorResourcePreview.xml b/doc/classes/EditorResourcePreview.xml
index 0c1d969518..3239a75ada 100644
--- a/doc/classes/EditorResourcePreview.xml
+++ b/doc/classes/EditorResourcePreview.xml
@@ -40,7 +40,8 @@
<argument index="3" name="userdata" type="Variant">
</argument>
<description>
- Queue a resource being edited for preview (using an instance). Once the preview is ready, your receiver.receiver_func will be called either containing the preview texture or an empty texture (if no preview was possible). Callback must have the format: (path,texture,userdata). Userdata can be anything.
+ Queue the [code]resource[/code] being edited for preview. Once the preview is ready, the [code]receiver[/code]'s [code]receiver_func[/code] will be called. The [code]receiver_func[/code] must take the following four arguments: [String] path, [Texture2D] preview, [Texture2D] thumbnail_preview, [Variant] userdata. [code]userdata[/code] can be anything, and will be returned when [code]receiver_func[/code] is called.
+ [b]Note[/b]: If it was not possible to create the preview the [code]receiver_func[/code] will still be called, but the preview will be null.
</description>
</method>
<method name="queue_resource_preview">
@@ -55,7 +56,8 @@
<argument index="3" name="userdata" type="Variant">
</argument>
<description>
- Queue a resource file for preview (using a path). Once the preview is ready, your receiver.receiver_func will be called either containing the preview texture or an empty texture (if no preview was possible). Callback must have the format: (path,texture,userdata). Userdata can be anything.
+ Queue a resource file located at [code]path[/code] for preview. Once the preview is ready, the [code]receiver[/code]'s [code]receiver_func[/code] will be called. The [code]receiver_func[/code] must take the following four arguments: [String] path, [Texture2D] preview, [Texture2D] thumbnail_preview, [Variant] userdata. [code]userdata[/code] can be anything, and will be returned when [code]receiver_func[/code] is called.
+ [b]Note[/b]: If it was not possible to create the preview the [code]receiver_func[/code] will still be called, but the preview will be null.
</description>
</method>
<method name="remove_preview_generator">
diff --git a/doc/classes/EditorSettings.xml b/doc/classes/EditorSettings.xml
index 016d0128eb..e732223516 100644
--- a/doc/classes/EditorSettings.xml
+++ b/doc/classes/EditorSettings.xml
@@ -124,15 +124,6 @@
Returns the value of the setting specified by [code]name[/code]. This is equivalent to using [method Object.get] on the EditorSettings instance.
</description>
</method>
- <method name="get_settings_dir" qualifiers="const">
- <return type="String">
- </return>
- <description>
- Gets the global settings path for the engine. Inside this path, you can find some standard paths such as:
- [code]settings/tmp[/code] - Used for temporary storage of files
- [code]settings/templates[/code] - Where export templates are located
- </description>
- </method>
<method name="has_setting" qualifiers="const">
<return type="bool">
</return>
diff --git a/doc/classes/EditorSpinSlider.xml b/doc/classes/EditorSpinSlider.xml
index f2c5a00640..935335943f 100644
--- a/doc/classes/EditorSpinSlider.xml
+++ b/doc/classes/EditorSpinSlider.xml
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="EditorSpinSlider" inherits="Range" version="4.0">
<brief_description>
- Godot editor's control for editing numertic values.
+ Godot editor's control for editing numeric values.
</brief_description>
<description>
This [Control] node is used in the editor's Inspector dock to allow editing of numeric values. Can be used with [EditorInspectorPlugin] to recreate the same behavior.
diff --git a/doc/classes/File.xml b/doc/classes/File.xml
index f0b9156b89..2206730523 100644
--- a/doc/classes/File.xml
+++ b/doc/classes/File.xml
@@ -61,7 +61,7 @@
</return>
<description>
Returns [code]true[/code] if the file cursor has read past the end of the file.
- [b]Note:[/b] This function will still return [code]false[/code] while at the end of the file and only activates when reading past it. This can be confusing but it conforms to how low-level file access works in all operating systems. There is always [method get_len] and [method get_position] to implement a custom logic.
+ [b]Note:[/b] This function will still return [code]false[/code] while at the end of the file and only activates when reading past it. This can be confusing but it conforms to how low-level file access works in all operating systems. There is always [method get_length] and [method get_position] to implement a custom logic.
</description>
</method>
<method name="file_exists" qualifiers="const">
@@ -121,10 +121,10 @@
<method name="get_buffer" qualifiers="const">
<return type="PackedByteArray">
</return>
- <argument index="0" name="len" type="int">
+ <argument index="0" name="length" type="int">
</argument>
<description>
- Returns next [code]len[/code] bytes of the file as a [PackedByteArray].
+ Returns next [code]length[/code] bytes of the file as a [PackedByteArray].
</description>
</method>
<method name="get_csv_line" qualifiers="const">
@@ -158,7 +158,7 @@
Returns the next 32 bits from the file as a floating-point number.
</description>
</method>
- <method name="get_len" qualifiers="const">
+ <method name="get_length" qualifiers="const">
<return type="int">
</return>
<description>
diff --git a/doc/classes/ProjectSettings.xml b/doc/classes/ProjectSettings.xml
index f6ade5583e..73899daaea 100644
--- a/doc/classes/ProjectSettings.xml
+++ b/doc/classes/ProjectSettings.xml
@@ -1502,6 +1502,16 @@
<member name="rendering/reflections/sky_reflections/texture_array_reflections.mobile" type="bool" setter="" getter="" default="false">
Lower-end override for [member rendering/reflections/sky_reflections/texture_array_reflections] on mobile devices, due to performance concerns or driver support.
</member>
+ <member name="rendering/shader_compiler/shader_cache/compress" type="bool" setter="" getter="" default="true">
+ </member>
+ <member name="rendering/shader_compiler/shader_cache/enabled" type="bool" setter="" getter="" default="true">
+ </member>
+ <member name="rendering/shader_compiler/shader_cache/strip_debug" type="bool" setter="" getter="" default="false">
+ </member>
+ <member name="rendering/shader_compiler/shader_cache/strip_debug.release" type="bool" setter="" getter="" default="true">
+ </member>
+ <member name="rendering/shader_compiler/shader_cache/use_zstd_compression" type="bool" setter="" getter="" default="true">
+ </member>
<member name="rendering/shading/overrides/force_blinn_over_ggx" type="bool" setter="" getter="" default="false">
If [code]true[/code], uses faster but lower-quality Blinn model to generate blurred reflections instead of the GGX model.
</member>
diff --git a/doc/classes/Resource.xml b/doc/classes/Resource.xml
index 2548f8d911..75736798fd 100644
--- a/doc/classes/Resource.xml
+++ b/doc/classes/Resource.xml
@@ -27,6 +27,7 @@
<description>
Duplicates the resource, returning a new resource. By default, sub-resources are shared between resource copies for efficiency. This can be changed by passing [code]true[/code] to the [code]subresources[/code] argument which will copy the subresources.
[b]Note:[/b] If [code]subresources[/code] is [code]true[/code], this method will only perform a shallow copy. Nested resources within subresources will not be duplicated and will still be shared.
+ [b]Note:[/b] When duplicating a resource, only [code]export[/code]ed properties are copied. Other properties will be set to their default value in the new resource.
</description>
</method>
<method name="emit_changed">
diff --git a/doc/classes/ScrollContainer.xml b/doc/classes/ScrollContainer.xml
index 40e29ac74b..60f3106b4b 100644
--- a/doc/classes/ScrollContainer.xml
+++ b/doc/classes/ScrollContainer.xml
@@ -11,6 +11,15 @@
<tutorials>
</tutorials>
<methods>
+ <method name="ensure_control_visible">
+ <return type="void">
+ </return>
+ <argument index="0" name="control" type="Control">
+ </argument>
+ <description>
+ Ensures the given [code]control[/code] is visible (must be a direct or indirect child of the ScrollContainer). Used by [member follow_focus].
+ </description>
+ </method>
<method name="get_h_scrollbar">
<return type="HScrollBar">
</return>
diff --git a/doc/classes/TileMap.xml b/doc/classes/TileMap.xml
index fd52a28486..93d5dd8037 100644
--- a/doc/classes/TileMap.xml
+++ b/doc/classes/TileMap.xml
@@ -132,6 +132,10 @@
<member name="cell_quadrant_size" type="int" setter="set_quadrant_size" getter="get_quadrant_size" default="16">
The TileMap's quadrant size. Optimizes drawing by batching, using chunks of this size.
</member>
+ <member name="show_collision" type="int" setter="set_collision_visibility_mode" getter="get_collision_visibility_mode" enum="TileMap.VisibilityMode" default="0">
+ </member>
+ <member name="show_navigation" type="int" setter="set_navigation_visibility_mode" getter="get_navigation_visibility_mode" enum="TileMap.VisibilityMode" default="0">
+ </member>
<member name="tile_set" type="TileSet" setter="set_tileset" getter="get_tileset">
The assigned [TileSet].
</member>
@@ -144,5 +148,11 @@
</signal>
</signals>
<constants>
+ <constant name="VISIBILITY_MODE_DEFAULT" value="0" enum="VisibilityMode">
+ </constant>
+ <constant name="VISIBILITY_MODE_FORCE_HIDE" value="2" enum="VisibilityMode">
+ </constant>
+ <constant name="VISIBILITY_MODE_FORCE_SHOW" value="1" enum="VisibilityMode">
+ </constant>
</constants>
</class>
diff --git a/doc/classes/Tree.xml b/doc/classes/Tree.xml
index 27b26801f3..c31467c67e 100644
--- a/doc/classes/Tree.xml
+++ b/doc/classes/Tree.xml
@@ -533,6 +533,12 @@
<theme_item name="checked" type="Texture2D">
The check icon to display when the [constant TreeItem.CELL_MODE_CHECK] mode cell is checked.
</theme_item>
+ <theme_item name="children_hl_line_color" type="Color" default="Color( 0.27, 0.27, 0.27, 1 )">
+ The [Color] of the relationship lines between the selected [TreeItem] and its children.
+ </theme_item>
+ <theme_item name="children_hl_line_width" type="int" default="1">
+ The width of the relationship lines between the selected [TreeItem] and its children.
+ </theme_item>
<theme_item name="cursor" type="StyleBox">
[StyleBox] used for the cursor, when the [Tree] is being focused.
</theme_item>
@@ -587,8 +593,20 @@
<theme_item name="outline_size" type="int" default="0">
The size of the text outline.
</theme_item>
+ <theme_item name="parent_hl_line_color" type="Color" default="Color( 0.27, 0.27, 0.27, 1 )">
+ The [Color] of the relationship lines between the selected [TreeItem] and its parents.
+ </theme_item>
+ <theme_item name="parent_hl_line_margin" type="int" default="0">
+ The space between the parent relationship lines for the selected [TreeItem] and the relationship lines to its siblings that are not selected.
+ </theme_item>
+ <theme_item name="parent_hl_line_width" type="int" default="1">
+ The width of the relationship lines between the selected [TreeItem] and its parents.
+ </theme_item>
<theme_item name="relationship_line_color" type="Color" default="Color( 0.27, 0.27, 0.27, 1 )">
- [Color] of the relationship lines.
+ The default [Color] of the relationship lines.
+ </theme_item>
+ <theme_item name="relationship_line_width" type="int" default="1">
+ The default width of the relationship lines.
</theme_item>
<theme_item name="scroll_border" type="int" default="4">
The maximum distance between the mouse cursor and the control's border to trigger border scrolling when dragging.
diff --git a/doc/classes/VisualShaderNodeBillboard.xml b/doc/classes/VisualShaderNodeBillboard.xml
new file mode 100644
index 0000000000..53bcfa7b5c
--- /dev/null
+++ b/doc/classes/VisualShaderNodeBillboard.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="VisualShaderNodeBillboard" inherits="VisualShaderNode" version="4.0">
+ <brief_description>
+ A node that controls how the object faces the camera to be used within the visual shader graph.
+ </brief_description>
+ <description>
+ The output port of this node needs to be connected to [code]Model View Matrix[/code] port of [VisualShaderNodeOutput].
+ </description>
+ <tutorials>
+ </tutorials>
+ <methods>
+ </methods>
+ <members>
+ <member name="billboard_type" type="int" setter="set_billboard_type" getter="get_billboard_type" enum="VisualShaderNodeBillboard.BillboardType" default="1">
+ Controls how the object faces the camera. See [enum BillboardType].
+ </member>
+ <member name="keep_scale" type="bool" setter="set_keep_scale_enabled" getter="is_keep_scale_enabled" default="false">
+ If [code]true[/code], the shader will keep the scale set for the mesh. Otherwise, the scale is lost when billboarding.
+ </member>
+ </members>
+ <constants>
+ <constant name="BILLBOARD_TYPE_DISABLED" value="0" enum="BillboardType">
+ Billboarding is disabled and the node does nothing.
+ </constant>
+ <constant name="BILLBOARD_TYPE_ENABLED" value="1" enum="BillboardType">
+ A standard billboarding algorithm is enabled.
+ </constant>
+ <constant name="BILLBOARD_TYPE_FIXED_Y" value="2" enum="BillboardType">
+ A billboarding algorithm to rotate around Y-axis is enabled.
+ </constant>
+ <constant name="BILLBOARD_TYPE_PARTICLES" value="3" enum="BillboardType">
+ A billboarding algorithm designed to use on particles is enabled.
+ </constant>
+ <constant name="BILLBOARD_TYPE_MAX" value="4" enum="BillboardType">
+ Represents the size of the [enum BillboardType] enum.
+ </constant>
+ </constants>
+</class>
diff --git a/drivers/png/image_loader_png.cpp b/drivers/png/image_loader_png.cpp
index 41554bc93f..8b2e786146 100644
--- a/drivers/png/image_loader_png.cpp
+++ b/drivers/png/image_loader_png.cpp
@@ -37,7 +37,7 @@
#include <string.h>
Error ImageLoaderPNG::load_image(Ref<Image> p_image, FileAccess *f, bool p_force_linear, float p_scale) {
- const uint64_t buffer_size = f->get_len();
+ const uint64_t buffer_size = f->get_length();
Vector<uint8_t> file_buffer;
Error err = file_buffer.resize(buffer_size);
if (err) {
diff --git a/drivers/unix/dir_access_unix.cpp b/drivers/unix/dir_access_unix.cpp
index 5abe5d2c87..a2c9bae852 100644
--- a/drivers/unix/dir_access_unix.cpp
+++ b/drivers/unix/dir_access_unix.cpp
@@ -460,7 +460,7 @@ uint64_t DirAccessUnix::get_space_left() {
return 0;
};
- return vfs.f_bfree * vfs.f_bsize;
+ return (uint64_t)vfs.f_bavail * (uint64_t)vfs.f_frsize;
#else
// FIXME: Implement this.
return 0;
diff --git a/drivers/unix/file_access_unix.cpp b/drivers/unix/file_access_unix.cpp
index de9a7867ee..ec23df62d0 100644
--- a/drivers/unix/file_access_unix.cpp
+++ b/drivers/unix/file_access_unix.cpp
@@ -212,7 +212,7 @@ uint64_t FileAccessUnix::get_position() const {
return pos;
}
-uint64_t FileAccessUnix::get_len() const {
+uint64_t FileAccessUnix::get_length() const {
ERR_FAIL_COND_V_MSG(!f, 0, "File must be opened before use.");
int64_t pos = ftello(f);
diff --git a/drivers/unix/file_access_unix.h b/drivers/unix/file_access_unix.h
index 6aa3e1529f..5b1599c67f 100644
--- a/drivers/unix/file_access_unix.h
+++ b/drivers/unix/file_access_unix.h
@@ -64,7 +64,7 @@ public:
virtual void seek(uint64_t p_position); ///< seek to a given position
virtual void seek_end(int64_t p_position = 0); ///< seek from the end of file
virtual uint64_t get_position() const; ///< get position in the file
- virtual uint64_t get_len() const; ///< get size of the file
+ virtual uint64_t get_length() const; ///< get size of the file
virtual bool eof_reached() const; ///< reading passed EOF
diff --git a/drivers/windows/file_access_windows.cpp b/drivers/windows/file_access_windows.cpp
index 0f52c70482..1f46b44f5e 100644
--- a/drivers/windows/file_access_windows.cpp
+++ b/drivers/windows/file_access_windows.cpp
@@ -219,7 +219,7 @@ uint64_t FileAccessWindows::get_position() const {
return aux_position;
}
-uint64_t FileAccessWindows::get_len() const {
+uint64_t FileAccessWindows::get_length() const {
ERR_FAIL_COND_V(!f, 0);
uint64_t pos = get_position();
diff --git a/drivers/windows/file_access_windows.h b/drivers/windows/file_access_windows.h
index 008c69de66..95f3a75548 100644
--- a/drivers/windows/file_access_windows.h
+++ b/drivers/windows/file_access_windows.h
@@ -59,7 +59,7 @@ public:
virtual void seek(uint64_t p_position); ///< seek to a given position
virtual void seek_end(int64_t p_position = 0); ///< seek from the end of file
virtual uint64_t get_position() const; ///< get position in the file
- virtual uint64_t get_len() const; ///< get size of the file
+ virtual uint64_t get_length() const; ///< get size of the file
virtual bool eof_reached() const; ///< reading passed EOF
diff --git a/editor/code_editor.cpp b/editor/code_editor.cpp
index 87ac16fdcf..97ba5d7a5f 100644
--- a/editor/code_editor.cpp
+++ b/editor/code_editor.cpp
@@ -302,7 +302,7 @@ void FindReplaceBar::_replace_all() {
matches_label->add_theme_color_override("font_color", rc > 0 ? get_theme_color("font_color", "Label") : get_theme_color("error_color", "Editor"));
matches_label->set_text(vformat(TTR("%d replaced."), rc));
- text_editor->call_deferred("connect", "text_changed", Callable(this, "_editor_text_changed"));
+ text_editor->call_deferred("connect", "text_changed", callable_mp(this, &FindReplaceBar::_editor_text_changed));
results_count = -1;
}
diff --git a/editor/editor_audio_buses.cpp b/editor/editor_audio_buses.cpp
index 466ca70130..4317760379 100644
--- a/editor/editor_audio_buses.cpp
+++ b/editor/editor_audio_buses.cpp
@@ -173,6 +173,9 @@ void EditorAudioBus::_notification(int p_what) {
bypass->set_icon(get_theme_icon("AudioBusBypass", "EditorIcons"));
bus_options->set_icon(get_theme_icon("GuiTabMenuHl", "EditorIcons"));
+
+ audio_value_preview_box->add_theme_color_override("font_color", get_theme_color("font_color", "TooltipLabel"));
+ audio_value_preview_box->add_theme_style_override("panel", get_theme_stylebox("panel", "TooltipPanel"));
} break;
case NOTIFICATION_MOUSE_EXIT:
case NOTIFICATION_DRAG_END: {
@@ -379,15 +382,24 @@ void EditorAudioBus::_show_value(float slider_value) {
db = _normalized_volume_to_scaled_db(slider_value);
}
- String text = vformat("%10.1f dB", db);
+ String text;
+ if (Math::is_zero_approx(Math::snapped(db, 0.1))) {
+ // Prevent displaying `-0.0 dB` and show ` 0.0 dB` instead.
+ // The leading space makes the text visually line up with its positive/negative counterparts.
+ text = " 0.0 dB";
+ } else {
+ // Show an explicit `+` sign if positive.
+ text = vformat("%+.1f dB", db);
+ }
+ // Also set the preview text as a standard Control tooltip.
+ // This way, it can be seen when the slider is merely hovered (instead of dragged).
slider->set_tooltip(text);
audio_value_preview_label->set_text(text);
- Vector2 slider_size = slider->get_size();
- Vector2 slider_position = slider->get_global_position();
- float left_padding = 5.0f;
- float vert_padding = 10.0f;
- Vector2 box_position = Vector2(slider_size.x + left_padding, (slider_size.y - vert_padding) * (1.0f - slider->get_value()) - vert_padding);
+ const Vector2 slider_size = slider->get_size();
+ const Vector2 slider_position = slider->get_global_position();
+ const float vert_padding = 10.0f;
+ const Vector2 box_position = Vector2(slider_size.x, (slider_size.y - vert_padding) * (1.0f - slider->get_value()) - vert_padding);
audio_value_preview_box->set_position(slider_position + box_position);
audio_value_preview_box->set_size(audio_value_preview_label->get_size());
if (slider->has_focus() && !audio_value_preview_box->is_visible()) {
@@ -830,14 +842,13 @@ EditorAudioBus::EditorAudioBus(EditorAudioBuses *p_buses, bool p_is_master) {
audio_value_preview_label->set_v_size_flags(SIZE_EXPAND_FILL);
audio_value_preview_label->set_h_size_flags(SIZE_EXPAND_FILL);
audio_value_preview_label->set_mouse_filter(MOUSE_FILTER_PASS);
+ audio_value_preview_box->add_theme_color_override("font_color", get_theme_color("font_color", "TooltipLabel"));
audioprev_hbc->add_child(audio_value_preview_label);
slider->add_child(audio_value_preview_box);
audio_value_preview_box->set_as_top_level(true);
- Ref<StyleBoxFlat> panel_style = memnew(StyleBoxFlat);
- panel_style->set_bg_color(Color(0.0f, 0.0f, 0.0f, 0.8f));
- audio_value_preview_box->add_theme_style_override("panel", panel_style);
+ audio_value_preview_box->add_theme_style_override("panel", get_theme_stylebox("panel", "TooltipPanel"));
audio_value_preview_box->set_mouse_filter(MOUSE_FILTER_PASS);
audio_value_preview_box->hide();
@@ -925,7 +936,7 @@ EditorAudioBus::EditorAudioBus(EditorAudioBuses *p_buses, bool p_is_master) {
bus_options->set_shortcut_context(this);
bus_options->set_h_size_flags(SIZE_SHRINK_END);
bus_options->set_anchor(SIDE_RIGHT, 0.0);
- bus_options->set_tooltip(TTR("Bus options"));
+ bus_options->set_tooltip(TTR("Bus Options"));
hbc->add_child(bus_options);
bus_popup = bus_options->get_popup();
@@ -1400,7 +1411,7 @@ void EditorAudioMeterNotches::_bind_methods() {
void EditorAudioMeterNotches::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_THEME_CHANGED: {
- notch_color = EditorSettings::get_singleton()->is_dark_theme() ? Color(1, 1, 1) : Color(0, 0, 0);
+ notch_color = get_theme_color("font_color", "Editor");
} break;
case NOTIFICATION_DRAW: {
_draw_audio_notches();
@@ -1416,13 +1427,13 @@ void EditorAudioMeterNotches::_draw_audio_notches() {
for (int i = 0; i < notches.size(); i++) {
AudioNotch n = notches[i];
draw_line(Vector2(0, (1.0f - n.relative_position) * (get_size().y - btm_padding - top_padding) + top_padding),
- Vector2(line_length, (1.0f - n.relative_position) * (get_size().y - btm_padding - top_padding) + top_padding),
+ Vector2(line_length * EDSCALE, (1.0f - n.relative_position) * (get_size().y - btm_padding - top_padding) + top_padding),
notch_color,
- 1);
+ Math::round(EDSCALE));
if (n.render_db_value) {
draw_string(font,
- Vector2(line_length + label_space,
+ Vector2((line_length + label_space) * EDSCALE,
(1.0f - n.relative_position) * (get_size().y - btm_padding - top_padding) + (font_height / 4) + top_padding),
String::num(Math::abs(n.db_value)) + "dB",
HALIGN_LEFT, -1, font_size,
@@ -1432,5 +1443,5 @@ void EditorAudioMeterNotches::_draw_audio_notches() {
}
EditorAudioMeterNotches::EditorAudioMeterNotches() {
- notch_color = EditorSettings::get_singleton()->is_dark_theme() ? Color(1, 1, 1) : Color(0, 0, 0);
+ notch_color = get_theme_color("font_color", "Editor");
}
diff --git a/editor/editor_audio_buses.h b/editor/editor_audio_buses.h
index 8dfc2137ef..0fbda8ece9 100644
--- a/editor/editor_audio_buses.h
+++ b/editor/editor_audio_buses.h
@@ -243,10 +243,10 @@ private:
List<AudioNotch> notches;
public:
- float line_length = 5.0f;
- float label_space = 2.0f;
- float btm_padding = 9.0f;
- float top_padding = 5.0f;
+ const float line_length = 5.0f;
+ const float label_space = 2.0f;
+ const float btm_padding = 9.0f;
+ const float top_padding = 5.0f;
Color notch_color;
void add_notch(float p_normalized_offset, float p_db_value, bool p_render_value = false);
diff --git a/editor/editor_export.cpp b/editor/editor_export.cpp
index b3755bef80..40313fbeff 100644
--- a/editor/editor_export.cpp
+++ b/editor/editor_export.cpp
@@ -1065,7 +1065,7 @@ Error EditorExportPlatform::export_project_files(const Ref<EditorExportPreset> &
}
} else {
// Use default text server data.
- String icu_data_file = EditorSettings::get_singleton()->get_cache_dir().plus_file("tmp_icu_data");
+ String icu_data_file = EditorPaths::get_singleton()->get_cache_dir().plus_file("tmp_icu_data");
TS->save_support_data(icu_data_file);
Vector<uint8_t> array = FileAccess::get_file_as_array(icu_data_file);
err = p_func(p_udata, ts_data, array, idx, total, enc_in_filters, enc_ex_filters, key);
@@ -1078,7 +1078,7 @@ Error EditorExportPlatform::export_project_files(const Ref<EditorExportPreset> &
}
String config_file = "project.binary";
- String engine_cfb = EditorSettings::get_singleton()->get_cache_dir().plus_file("tmp" + config_file);
+ String engine_cfb = EditorPaths::get_singleton()->get_cache_dir().plus_file("tmp" + config_file);
ProjectSettings::get_singleton()->save_custom(engine_cfb, custom_map, custom_list);
Vector<uint8_t> data = FileAccess::get_file_as_array(engine_cfb);
DirAccess::remove_file_or_error(engine_cfb);
@@ -1100,9 +1100,9 @@ Error EditorExportPlatform::save_pack(const Ref<EditorExportPreset> &p_preset, c
// Create the temporary export directory if it doesn't exist.
DirAccessRef da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
- da->make_dir_recursive(EditorSettings::get_singleton()->get_cache_dir());
+ da->make_dir_recursive(EditorPaths::get_singleton()->get_cache_dir());
- String tmppath = EditorSettings::get_singleton()->get_cache_dir().plus_file("packtmp");
+ String tmppath = EditorPaths::get_singleton()->get_cache_dir().plus_file("packtmp");
FileAccess *ftmp = FileAccess::open(tmppath, FileAccess::WRITE);
ERR_FAIL_COND_V_MSG(!ftmp, ERR_CANT_CREATE, "Cannot create file '" + tmppath + "'.");
@@ -1984,7 +1984,7 @@ void EditorExportTextSceneToBinaryPlugin::_export_file(const String &p_path, con
if (!convert) {
return;
}
- String tmp_path = EditorSettings::get_singleton()->get_cache_dir().plus_file("tmpfile.res");
+ String tmp_path = EditorPaths::get_singleton()->get_cache_dir().plus_file("tmpfile.res");
Error err = ResourceFormatLoaderText::convert_file_to_binary(p_path, tmp_path);
if (err != OK) {
DirAccess::remove_file_or_error(tmp_path);
diff --git a/editor/editor_feature_profile.cpp b/editor/editor_feature_profile.cpp
index bd00d86ec8..d232153206 100644
--- a/editor/editor_feature_profile.cpp
+++ b/editor/editor_feature_profile.cpp
@@ -46,6 +46,16 @@ const char *EditorFeatureProfile::feature_names[FEATURE_MAX] = {
TTRC("Import Dock"),
};
+const char *EditorFeatureProfile::feature_descriptions[FEATURE_MAX] = {
+ TTRC("Allows to view and edit 3D scenes."),
+ TTRC("Allows to edit scripts using the integrated script editor."),
+ TTRC("Provides built-in access to the Asset Library."),
+ TTRC("Allows editing the node hierarchy in the Scene dock."),
+ TTRC("Allows to work with signals and groups of the node selected in the Scene dock."),
+ TTRC("Allows to browse the local file system via a dedicated dock."),
+ TTRC("Allows to configure import settings for individual assets. Requires the FileSystem dock to function."),
+};
+
const char *EditorFeatureProfile::feature_identifiers[FEATURE_MAX] = {
"3d",
"script",
@@ -118,6 +128,18 @@ bool EditorFeatureProfile::has_class_properties_disabled(const StringName &p_cla
return disabled_properties.has(p_class);
}
+void EditorFeatureProfile::set_item_collapsed(const StringName &p_class, bool p_collapsed) {
+ if (p_collapsed) {
+ collapsed_classes.insert(p_class);
+ } else {
+ collapsed_classes.erase(p_class);
+ }
+}
+
+bool EditorFeatureProfile::is_item_collapsed(const StringName &p_class) const {
+ return collapsed_classes.has(p_class);
+}
+
void EditorFeatureProfile::set_disable_feature(Feature p_feature, bool p_disable) {
ERR_FAIL_INDEX(p_feature, FEATURE_MAX);
features_disabled[p_feature] = p_disable;
@@ -133,6 +155,11 @@ String EditorFeatureProfile::get_feature_name(Feature p_feature) {
return feature_names[p_feature];
}
+String EditorFeatureProfile::get_feature_description(Feature p_feature) {
+ ERR_FAIL_INDEX_V(p_feature, FEATURE_MAX, String());
+ return feature_descriptions[p_feature];
+}
+
Error EditorFeatureProfile::save_to_file(const String &p_path) {
Dictionary json;
json["type"] = "feature_profile";
@@ -406,7 +433,7 @@ void EditorFeatureProfileManager::_profile_action(int p_action) {
export_profile->set_current_file(_get_selected_profile() + ".profile");
} break;
case PROFILE_NEW: {
- new_profile_dialog->popup_centered();
+ new_profile_dialog->popup_centered(Size2(240, 60) * EDSCALE);
new_profile_name->clear();
new_profile_name->grab_focus();
} break;
@@ -414,8 +441,8 @@ void EditorFeatureProfileManager::_profile_action(int p_action) {
String selected = _get_selected_profile();
ERR_FAIL_COND(selected == String());
- erase_profile_dialog->set_text(vformat(TTR("Erase profile '%s'? (no undo)"), selected));
- erase_profile_dialog->popup_centered();
+ erase_profile_dialog->set_text(vformat(TTR("Remove currently selected profile, '%s'? Cannot be undone."), selected));
+ erase_profile_dialog->popup_centered(Size2(240, 60) * EDSCALE);
} break;
}
}
@@ -484,6 +511,9 @@ void EditorFeatureProfileManager::_fill_classes_from(TreeItem *p_parent, const S
class_item->set_selectable(0, true);
class_item->set_metadata(0, p_class);
+ bool collapsed = edited->is_item_collapsed(p_class);
+ class_item->set_collapsed(collapsed);
+
if (p_class == p_selected) {
class_item->select(0);
}
@@ -520,12 +550,28 @@ void EditorFeatureProfileManager::_class_list_item_selected() {
}
Variant md = item->get_metadata(0);
- if (md.get_type() != Variant::STRING && md.get_type() != Variant::STRING_NAME) {
+ if (md.get_type() == Variant::STRING || md.get_type() == Variant::STRING_NAME) {
+ String class_name = md;
+ String class_description;
+
+ DocTools *dd = EditorHelp::get_doc_data();
+ Map<String, DocData::ClassDoc>::Element *E = dd->class_list.find(class_name);
+ if (E) {
+ class_description = DTR(E->get().brief_description);
+ }
+
+ description_bit->set_text(class_description);
+ } else if (md.get_type() == Variant::INT) {
+ int feature_id = md;
+ String feature_description = EditorFeatureProfile::get_feature_description(EditorFeatureProfile::Feature(feature_id));
+
+ description_bit->set_text(feature_description);
+ return;
+ } else {
return;
}
String class_name = md;
-
if (edited->is_class_disabled(class_name)) {
return;
}
@@ -545,27 +591,28 @@ void EditorFeatureProfileManager::_class_list_item_selected() {
option->set_metadata(0, CLASS_OPTION_DISABLE_EDITOR);
}
- TreeItem *properties = property_list->create_item(root);
- properties->set_text(0, TTR("Enabled Properties:"));
-
List<PropertyInfo> props;
-
ClassDB::get_property_list(class_name, &props, true);
- for (List<PropertyInfo>::Element *E = props.front(); E; E = E->next()) {
- String name = E->get().name;
- if (!(E->get().usage & PROPERTY_USAGE_EDITOR)) {
- continue;
+ if (props.size() > 0) {
+ TreeItem *properties = property_list->create_item(root);
+ properties->set_text(0, TTR("Class Properties:"));
+
+ for (List<PropertyInfo>::Element *E = props.front(); E; E = E->next()) {
+ String name = E->get().name;
+ if (!(E->get().usage & PROPERTY_USAGE_EDITOR)) {
+ continue;
+ }
+ TreeItem *property = property_list->create_item(properties);
+ property->set_cell_mode(0, TreeItem::CELL_MODE_CHECK);
+ property->set_editable(0, true);
+ property->set_selectable(0, true);
+ property->set_checked(0, !edited->is_class_property_disabled(class_name, name));
+ property->set_text(0, name.capitalize());
+ property->set_metadata(0, name);
+ String icon_type = Variant::get_type_name(E->get().type);
+ property->set_icon(0, EditorNode::get_singleton()->get_class_icon(icon_type));
}
- TreeItem *property = property_list->create_item(properties);
- property->set_cell_mode(0, TreeItem::CELL_MODE_CHECK);
- property->set_editable(0, true);
- property->set_selectable(0, true);
- property->set_checked(0, !edited->is_class_property_disabled(class_name, name));
- property->set_text(0, name.capitalize());
- property->set_metadata(0, name);
- String icon_type = Variant::get_type_name(E->get().type);
- property->set_icon(0, EditorNode::get_singleton()->get_class_icon(icon_type));
}
updating_features = false;
@@ -596,6 +643,26 @@ void EditorFeatureProfileManager::_class_list_item_edited() {
}
}
+void EditorFeatureProfileManager::_class_list_item_collapsed(Object *p_item) {
+ if (updating_features) {
+ return;
+ }
+
+ TreeItem *item = Object::cast_to<TreeItem>(p_item);
+ if (!item) {
+ return;
+ }
+
+ Variant md = item->get_metadata(0);
+ if (md.get_type() != Variant::STRING && md.get_type() != Variant::STRING_NAME) {
+ return;
+ }
+
+ String class_name = md;
+ bool collapsed = item->is_collapsed();
+ edited->set_item_collapsed(class_name, collapsed);
+}
+
void EditorFeatureProfileManager::_property_item_edited() {
if (updating_features) {
return;
@@ -675,7 +742,7 @@ void EditorFeatureProfileManager::_update_selected_profile() {
TreeItem *features = class_list->create_item(root);
TreeItem *last_feature;
- features->set_text(0, TTR("Enabled Features:"));
+ features->set_text(0, TTR("Main Features") + ":");
for (int i = 0; i < EditorFeatureProfile::FEATURE_MAX; i++) {
TreeItem *feature;
if (i == EditorFeatureProfile::FEATURE_IMPORT_DOCK) {
@@ -699,7 +766,7 @@ void EditorFeatureProfileManager::_update_selected_profile() {
}
TreeItem *classes = class_list->create_item(root);
- classes->set_text(0, TTR("Enabled Classes:"));
+ classes->set_text(0, TTR("Nodes and Classes") + ":");
_fill_classes_from(classes, "Node", class_selected);
_fill_classes_from(classes, "Resource", class_selected);
@@ -797,47 +864,51 @@ EditorFeatureProfileManager::EditorFeatureProfileManager() {
current_profile_name->set_text(TTR("(none)"));
current_profile_name->set_editable(false);
current_profile_name->set_h_size_flags(Control::SIZE_EXPAND_FILL);
- profile_actions[PROFILE_CLEAR] = memnew(Button(TTR("Unset")));
+ profile_actions[PROFILE_CLEAR] = memnew(Button(TTR("Reset to Default")));
name_hbc->add_child(profile_actions[PROFILE_CLEAR]);
profile_actions[PROFILE_CLEAR]->set_disabled(true);
profile_actions[PROFILE_CLEAR]->connect("pressed", callable_mp(this, &EditorFeatureProfileManager::_profile_action), varray(PROFILE_CLEAR));
main_vbc->add_margin_child(TTR("Current Profile:"), name_hbc);
+ main_vbc->add_child(memnew(HSeparator));
+
HBoxContainer *profiles_hbc = memnew(HBoxContainer);
profile_list = memnew(OptionButton);
profile_list->set_h_size_flags(Control::SIZE_EXPAND_FILL);
profiles_hbc->add_child(profile_list);
profile_list->connect("item_selected", callable_mp(this, &EditorFeatureProfileManager::_profile_selected));
- profile_actions[PROFILE_SET] = memnew(Button(TTR("Make Current")));
- profiles_hbc->add_child(profile_actions[PROFILE_SET]);
- profile_actions[PROFILE_SET]->set_disabled(true);
- profile_actions[PROFILE_SET]->connect("pressed", callable_mp(this, &EditorFeatureProfileManager::_profile_action), varray(PROFILE_SET));
+ profile_actions[PROFILE_NEW] = memnew(Button(TTR("Create Profile")));
+ profiles_hbc->add_child(profile_actions[PROFILE_NEW]);
+ profile_actions[PROFILE_NEW]->connect("pressed", callable_mp(this, &EditorFeatureProfileManager::_profile_action), varray(PROFILE_NEW));
- profile_actions[PROFILE_ERASE] = memnew(Button(TTR("Remove")));
+ profile_actions[PROFILE_ERASE] = memnew(Button(TTR("Remove Profile")));
profiles_hbc->add_child(profile_actions[PROFILE_ERASE]);
profile_actions[PROFILE_ERASE]->set_disabled(true);
profile_actions[PROFILE_ERASE]->connect("pressed", callable_mp(this, &EditorFeatureProfileManager::_profile_action), varray(PROFILE_ERASE));
- profiles_hbc->add_child(memnew(VSeparator));
+ main_vbc->add_margin_child(TTR("Available Profiles:"), profiles_hbc);
- profile_actions[PROFILE_NEW] = memnew(Button(TTR("New")));
- profiles_hbc->add_child(profile_actions[PROFILE_NEW]);
- profile_actions[PROFILE_NEW]->connect("pressed", callable_mp(this, &EditorFeatureProfileManager::_profile_action), varray(PROFILE_NEW));
+ HBoxContainer *current_profile_hbc = memnew(HBoxContainer);
- profiles_hbc->add_child(memnew(VSeparator));
+ profile_actions[PROFILE_SET] = memnew(Button(TTR("Make Current")));
+ current_profile_hbc->add_child(profile_actions[PROFILE_SET]);
+ profile_actions[PROFILE_SET]->set_disabled(true);
+ profile_actions[PROFILE_SET]->connect("pressed", callable_mp(this, &EditorFeatureProfileManager::_profile_action), varray(PROFILE_SET));
+
+ current_profile_hbc->add_child(memnew(VSeparator));
profile_actions[PROFILE_IMPORT] = memnew(Button(TTR("Import")));
- profiles_hbc->add_child(profile_actions[PROFILE_IMPORT]);
+ current_profile_hbc->add_child(profile_actions[PROFILE_IMPORT]);
profile_actions[PROFILE_IMPORT]->connect("pressed", callable_mp(this, &EditorFeatureProfileManager::_profile_action), varray(PROFILE_IMPORT));
profile_actions[PROFILE_EXPORT] = memnew(Button(TTR("Export")));
- profiles_hbc->add_child(profile_actions[PROFILE_EXPORT]);
+ current_profile_hbc->add_child(profile_actions[PROFILE_EXPORT]);
profile_actions[PROFILE_EXPORT]->set_disabled(true);
profile_actions[PROFILE_EXPORT]->connect("pressed", callable_mp(this, &EditorFeatureProfileManager::_profile_action), varray(PROFILE_EXPORT));
- main_vbc->add_margin_child(TTR("Available Profiles:"), profiles_hbc);
+ main_vbc->add_child(current_profile_hbc);
h_split = memnew(HSplitContainer);
h_split->set_v_size_flags(Control::SIZE_EXPAND_FILL);
@@ -848,11 +919,12 @@ EditorFeatureProfileManager::EditorFeatureProfileManager() {
class_list_vbc->set_h_size_flags(Control::SIZE_EXPAND_FILL);
class_list = memnew(Tree);
- class_list_vbc->add_margin_child(TTR("Enabled Classes:"), class_list, true);
+ class_list_vbc->add_margin_child(TTR("Configure Selected Profile") + ":", class_list, true);
class_list->set_hide_root(true);
class_list->set_edit_checkbox_cell_only_when_checkbox_is_pressed(true);
class_list->connect("cell_selected", callable_mp(this, &EditorFeatureProfileManager::_class_list_item_selected));
class_list->connect("item_edited", callable_mp(this, &EditorFeatureProfileManager::_class_list_item_edited), varray(), CONNECT_DEFERRED);
+ class_list->connect("item_collapsed", callable_mp(this, &EditorFeatureProfileManager::_class_list_item_collapsed));
// It will be displayed once the user creates or chooses a profile.
class_list_vbc->hide();
@@ -860,8 +932,12 @@ EditorFeatureProfileManager::EditorFeatureProfileManager() {
h_split->add_child(property_list_vbc);
property_list_vbc->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+ description_bit = memnew(EditorHelpBit);
+ property_list_vbc->add_margin_child(TTR("Description") + ":", description_bit, false);
+ description_bit->set_custom_minimum_size(Size2(0, 80) * EDSCALE);
+
property_list = memnew(Tree);
- property_list_vbc->add_margin_child(TTR("Class Options:"), property_list, true);
+ property_list_vbc->add_margin_child(TTR("Extra Options") + ":", property_list, true);
property_list->set_hide_root(true);
property_list->set_hide_folding(true);
property_list->set_edit_checkbox_cell_only_when_checkbox_is_pressed(true);
@@ -879,9 +955,14 @@ EditorFeatureProfileManager::EditorFeatureProfileManager() {
h_split->add_child(no_profile_selected_help);
new_profile_dialog = memnew(ConfirmationDialog);
- new_profile_dialog->set_title(TTR("New profile name:"));
+ new_profile_dialog->set_title(TTR("Create Profile"));
+ VBoxContainer *new_profile_vb = memnew(VBoxContainer);
+ new_profile_dialog->add_child(new_profile_vb);
+ Label *new_profile_label = memnew(Label);
+ new_profile_label->set_text(TTR("New profile name") + ":");
+ new_profile_vb->add_child(new_profile_label);
new_profile_name = memnew(LineEdit);
- new_profile_dialog->add_child(new_profile_name);
+ new_profile_vb->add_child(new_profile_name);
new_profile_name->set_custom_minimum_size(Size2(300 * EDSCALE, 1));
add_child(new_profile_dialog);
new_profile_dialog->connect("confirmed", callable_mp(this, &EditorFeatureProfileManager::_create_new_profile));
@@ -890,7 +971,7 @@ EditorFeatureProfileManager::EditorFeatureProfileManager() {
erase_profile_dialog = memnew(ConfirmationDialog);
add_child(erase_profile_dialog);
- erase_profile_dialog->set_title(TTR("Erase Profile"));
+ erase_profile_dialog->set_title(TTR("Remove Profile"));
erase_profile_dialog->connect("confirmed", callable_mp(this, &EditorFeatureProfileManager::_erase_selected_profile));
import_profiles = memnew(EditorFileDialog);
diff --git a/editor/editor_feature_profile.h b/editor/editor_feature_profile.h
index 01e6a6a142..e118b5f287 100644
--- a/editor/editor_feature_profile.h
+++ b/editor/editor_feature_profile.h
@@ -34,6 +34,7 @@
#include "core/object/reference.h"
#include "core/os/file_access.h"
#include "editor/editor_file_dialog.h"
+#include "editor_help.h"
#include "scene/gui/dialogs.h"
#include "scene/gui/option_button.h"
#include "scene/gui/separator.h"
@@ -60,8 +61,11 @@ private:
Set<StringName> disabled_editors;
Map<StringName, Set<StringName>> disabled_properties;
+ Set<StringName> collapsed_classes;
+
bool features_disabled[FEATURE_MAX];
static const char *feature_names[FEATURE_MAX];
+ static const char *feature_descriptions[FEATURE_MAX];
static const char *feature_identifiers[FEATURE_MAX];
String _get_feature_name(Feature p_feature) { return get_feature_name(p_feature); }
@@ -80,6 +84,9 @@ public:
bool is_class_property_disabled(const StringName &p_class, const StringName &p_property) const;
bool has_class_properties_disabled(const StringName &p_class) const;
+ void set_item_collapsed(const StringName &p_class, bool p_collapsed);
+ bool is_item_collapsed(const StringName &p_class) const;
+
void set_disable_feature(Feature p_feature, bool p_disable);
bool is_feature_disabled(Feature p_feature) const;
@@ -87,6 +94,7 @@ public:
Error load_from_file(const String &p_path);
static String get_feature_name(Feature p_feature);
+ static String get_feature_description(Feature p_feature);
EditorFeatureProfile();
};
@@ -124,6 +132,7 @@ class EditorFeatureProfileManager : public AcceptDialog {
Tree *class_list;
VBoxContainer *property_list_vbc;
Tree *property_list;
+ EditorHelpBit *description_bit;
Label *no_profile_selected_help;
EditorFileDialog *import_profiles;
@@ -151,6 +160,7 @@ class EditorFeatureProfileManager : public AcceptDialog {
void _class_list_item_selected();
void _class_list_item_edited();
+ void _class_list_item_collapsed(Object *p_item);
void _property_item_edited();
void _save_and_update();
diff --git a/editor/editor_inspector.cpp b/editor/editor_inspector.cpp
index 0616ab07bd..f869031446 100644
--- a/editor/editor_inspector.cpp
+++ b/editor/editor_inspector.cpp
@@ -1626,7 +1626,7 @@ void EditorInspector::update_tree() {
bool draw_red = false;
- {
+ if (is_inside_tree()) {
Node *nod = Object::cast_to<Node>(object);
Node *es = EditorNode::get_singleton()->get_edited_scene();
if (nod && es != nod && nod->get_owner() != es) {
diff --git a/editor/editor_log.cpp b/editor/editor_log.cpp
index 622b3fe355..35d8021394 100644
--- a/editor/editor_log.cpp
+++ b/editor/editor_log.cpp
@@ -234,10 +234,9 @@ void EditorLog::_add_log_line(LogMessage &p_message, bool p_replace_previous) {
if (p_replace_previous) {
// Remove last line if replacing, as it will be replace by the next added line.
- // Why - 2? RichTextLabel is weird. When you add a line, it also adds a NEW line, which is null,
+ // Why "- 2"? RichTextLabel is weird. When you add a line with add_newline(), it also adds an element to the list of lines which is null/blank,
// but it still counts as a line. So if you remove the last line (count - 1) you are actually removing nothing...
- log->remove_line(log->get_line_count() - 2);
- log->increment_line_count();
+ log->remove_line(log->get_paragraph_count() - 2);
}
switch (p_message.type) {
@@ -271,13 +270,14 @@ void EditorLog::_add_log_line(LogMessage &p_message, bool p_replace_previous) {
}
log->add_text(p_message.text);
- log->add_newline();
// Need to use pop() to exit out of the RichTextLabels current "push" stack.
// We only "push" in the above switch when message type != STD, so only pop when that is the case.
if (p_message.type != MSG_TYPE_STD) {
log->pop();
}
+
+ log->add_newline();
}
void EditorLog::_set_filter_active(bool p_active, MessageType p_message_type) {
diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp
index 72963012d6..aa632b90ad 100644
--- a/editor/editor_node.cpp
+++ b/editor/editor_node.cpp
@@ -80,6 +80,7 @@
#include "editor/editor_inspector.h"
#include "editor/editor_layouts_dialog.h"
#include "editor/editor_log.h"
+#include "editor/editor_paths.h"
#include "editor/editor_plugin.h"
#include "editor/editor_properties.h"
#include "editor/editor_resource_picker.h"
@@ -1457,7 +1458,7 @@ void EditorNode::_save_scene_with_preview(String p_file, int p_idx) {
img->convert(Image::FORMAT_RGB8);
//save thumbnail directly, as thumbnailer may not update due to actual scene not changing md5
- String temp_path = EditorSettings::get_singleton()->get_cache_dir();
+ String temp_path = EditorPaths::get_singleton()->get_cache_dir();
String cache_base = ProjectSettings::get_singleton()->globalize_path(p_file).md5_text();
cache_base = temp_path.plus_file("resthumb-" + cache_base);
@@ -2745,10 +2746,10 @@ void EditorNode::_menu_option_confirm(int p_option, bool p_confirmed) {
settings_config_dialog->popup_edit_settings();
} break;
case SETTINGS_EDITOR_DATA_FOLDER: {
- OS::get_singleton()->shell_open(String("file://") + EditorSettings::get_singleton()->get_data_dir());
+ OS::get_singleton()->shell_open(String("file://") + EditorPaths::get_singleton()->get_data_dir());
} break;
case SETTINGS_EDITOR_CONFIG_FOLDER: {
- OS::get_singleton()->shell_open(String("file://") + EditorSettings::get_singleton()->get_settings_dir());
+ OS::get_singleton()->shell_open(String("file://") + EditorPaths::get_singleton()->get_settings_dir());
} break;
case SETTINGS_MANAGE_EXPORT_TEMPLATES: {
export_template_manager->popup_manager();
@@ -2892,7 +2893,7 @@ void EditorNode::_exit_editor() {
_save_docks();
// Dim the editor window while it's quitting to make it clearer that it's busy
- dim_editor(true, true);
+ dim_editor(true);
get_tree()->quit();
}
@@ -3727,10 +3728,15 @@ bool EditorNode::is_scene_in_use(const String &p_path) {
return false;
}
+void EditorNode::register_editor_paths(bool p_for_project_manager) {
+ EditorPaths::create(p_for_project_manager);
+}
+
void EditorNode::register_editor_types() {
ResourceLoader::set_timestamp_on_load(true);
ResourceSaver::set_timestamp_on_save(true);
+ ClassDB::register_class<EditorPaths>();
ClassDB::register_class<EditorPlugin>();
ClassDB::register_class<EditorTranslationParserPlugin>();
ClassDB::register_class<EditorImportPlugin>();
@@ -3774,6 +3780,9 @@ void EditorNode::register_editor_types() {
void EditorNode::unregister_editor_types() {
_init_callbacks.clear();
+ if (EditorPaths::get_singleton()) {
+ EditorPaths::free();
+ }
}
void EditorNode::stop_child_process(OS::ProcessID p_pid) {
@@ -5396,15 +5405,9 @@ void EditorNode::_open_imported() {
load_scene(open_import_request, true, false, true, true);
}
-void EditorNode::dim_editor(bool p_dimming, bool p_force_dim) {
- // Dimming can be forced regardless of the editor setting, which is useful when quitting the editor.
- if ((p_force_dim || EditorSettings::get_singleton()->get("interface/editor/dim_editor_on_dialog_popup")) && p_dimming) {
- dimmed = true;
- gui_base->set_modulate(Color(0.5, 0.5, 0.5));
- } else {
- dimmed = false;
- gui_base->set_modulate(Color(1, 1, 1));
- }
+void EditorNode::dim_editor(bool p_dimming) {
+ dimmed = p_dimming;
+ gui_base->set_modulate(p_dimming ? Color(0.5, 0.5, 0.5) : Color(1, 1, 1));
}
bool EditorNode::is_editor_dimmed() const {
diff --git a/editor/editor_node.h b/editor/editor_node.h
index 9824702d7b..9625b318e0 100644
--- a/editor/editor_node.h
+++ b/editor/editor_node.h
@@ -798,6 +798,7 @@ public:
Error export_preset(const String &p_preset, const String &p_path, bool p_debug, bool p_pack_only);
+ static void register_editor_paths(bool p_for_project_manager);
static void register_editor_types();
static void unregister_editor_types();
@@ -855,7 +856,7 @@ public:
void notify_settings_changed();
- void dim_editor(bool p_dimming, bool p_force_dim = false);
+ void dim_editor(bool p_dimming);
bool is_editor_dimmed() const;
void edit_current() { _edit_current(); };
diff --git a/editor/editor_paths.cpp b/editor/editor_paths.cpp
new file mode 100644
index 0000000000..96469d3143
--- /dev/null
+++ b/editor/editor_paths.cpp
@@ -0,0 +1,156 @@
+/*************************************************************************/
+/* editor_paths.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "editor_paths.h"
+#include "core/os/dir_access.h"
+#include "core/os/os.h"
+
+EditorPaths *EditorPaths::singleton = nullptr;
+
+bool EditorPaths::are_paths_valid() const {
+ return paths_valid;
+}
+
+String EditorPaths::get_settings_dir() const {
+ return settings_dir;
+}
+String EditorPaths::get_data_dir() const {
+ return data_dir;
+}
+String EditorPaths::get_config_dir() const {
+ return config_dir;
+}
+String EditorPaths::get_cache_dir() const {
+ return cache_dir;
+}
+bool EditorPaths::is_self_contained() const {
+ return self_contained;
+}
+String EditorPaths::get_self_contained_file() const {
+ return self_contained_file;
+}
+
+void EditorPaths::create(bool p_for_project_manager) {
+ ERR_FAIL_COND(singleton != nullptr);
+ memnew(EditorPaths(p_for_project_manager));
+}
+void EditorPaths::free() {
+ ERR_FAIL_COND(singleton == nullptr);
+ memdelete(singleton);
+}
+
+void EditorPaths::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("get_settings_dir"), &EditorPaths::get_settings_dir);
+ ClassDB::bind_method(D_METHOD("get_data_dir"), &EditorPaths::get_data_dir);
+ ClassDB::bind_method(D_METHOD("get_config_dir"), &EditorPaths::get_config_dir);
+ ClassDB::bind_method(D_METHOD("get_cache_dir"), &EditorPaths::get_cache_dir);
+ ClassDB::bind_method(D_METHOD("is_self_contained"), &EditorPaths::is_self_contained);
+ ClassDB::bind_method(D_METHOD("get_self_contained_file"), &EditorPaths::get_self_contained_file);
+}
+
+EditorPaths::EditorPaths(bool p_for_project_mamanger) {
+ singleton = this;
+
+ String exe_path = OS::get_singleton()->get_executable_path().get_base_dir();
+ {
+ DirAccessRef d = DirAccess::create_for_path(exe_path);
+
+ if (d->file_exists(exe_path + "/._sc_")) {
+ self_contained = true;
+ self_contained_file = exe_path + "/._sc_";
+ } else if (d->file_exists(exe_path + "/_sc_")) {
+ self_contained = true;
+ self_contained_file = exe_path + "/_sc_";
+ }
+ }
+
+ String data_path;
+ String config_path;
+ String cache_path;
+
+ if (self_contained) {
+ // editor is self contained, all in same folder
+ data_path = exe_path;
+ data_dir = data_path.plus_file("editor_data");
+ config_path = exe_path;
+ config_dir = data_dir;
+ cache_path = exe_path;
+ cache_dir = data_dir.plus_file("cache");
+ } else {
+ // Typically XDG_DATA_HOME or %APPDATA%
+ data_path = OS::get_singleton()->get_data_path();
+ data_dir = data_path.plus_file(OS::get_singleton()->get_godot_dir_name());
+ // Can be different from data_path e.g. on Linux or macOS
+ config_path = OS::get_singleton()->get_config_path();
+ config_dir = config_path.plus_file(OS::get_singleton()->get_godot_dir_name());
+ // Can be different from above paths, otherwise a subfolder of data_dir
+ cache_path = OS::get_singleton()->get_cache_path();
+ if (cache_path == data_path) {
+ cache_dir = data_dir.plus_file("cache");
+ } else {
+ cache_dir = cache_path.plus_file(OS::get_singleton()->get_godot_dir_name());
+ }
+ }
+
+ paths_valid = (data_path != "" && config_path != "" && cache_path != "");
+
+ if (paths_valid) {
+ DirAccessRef dir = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
+ if (dir->change_dir(data_dir) != OK) {
+ dir->make_dir_recursive(data_dir);
+ if (dir->change_dir(data_dir) != OK) {
+ ERR_PRINT("Cannot create data directory!");
+ paths_valid = false;
+ }
+ }
+
+ // Validate/create cache dir
+
+ if (dir->change_dir(EditorPaths::get_singleton()->get_cache_dir()) != OK) {
+ dir->make_dir_recursive(cache_dir);
+ if (dir->change_dir(cache_dir) != OK) {
+ ERR_PRINT("Cannot create cache directory!");
+ }
+ }
+
+ if (p_for_project_mamanger) {
+ Engine::get_singleton()->set_shader_cache_path(get_data_dir());
+ } else {
+ DirAccessRef dir2 = DirAccess::open("res://");
+ if (dir2->change_dir(".godot") != OK) { //ensure the .godot subdir exists
+ if (dir2->make_dir(".godot") != OK) {
+ ERR_PRINT("Cannot create res://.godot directory!");
+ }
+ }
+
+ Engine::get_singleton()->set_shader_cache_path("res://.godot");
+ }
+ }
+}
diff --git a/editor/editor_paths.h b/editor/editor_paths.h
new file mode 100644
index 0000000000..096174943d
--- /dev/null
+++ b/editor/editor_paths.h
@@ -0,0 +1,72 @@
+/*************************************************************************/
+/* editor_paths.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef EDITORPATHS_H
+#define EDITORPATHS_H
+
+#include "core/config/engine.h"
+
+class EditorPaths : public Object {
+ GDCLASS(EditorPaths, Object)
+
+ bool paths_valid = false;
+ String settings_dir;
+ String data_dir; //editor data dir
+ String config_dir; //editor config dir
+ String cache_dir; //editor cache dir
+ bool self_contained = false; //true if running self contained
+ String self_contained_file; //self contained file with configuration
+
+ static EditorPaths *singleton;
+
+protected:
+ static void _bind_methods();
+
+public:
+ bool are_paths_valid() const;
+
+ String get_settings_dir() const;
+ String get_data_dir() const;
+ String get_config_dir() const;
+ String get_cache_dir() const;
+ bool is_self_contained() const;
+ String get_self_contained_file() const;
+
+ static EditorPaths *get_singleton() {
+ return singleton;
+ }
+
+ static void create(bool p_for_project_manager);
+ static void free();
+
+ EditorPaths(bool p_for_project_mamanger = false);
+};
+
+#endif // EDITORPATHS_H
diff --git a/editor/editor_plugin.cpp b/editor/editor_plugin.cpp
index 6b96cb0f5c..c5097a17a5 100644
--- a/editor/editor_plugin.cpp
+++ b/editor/editor_plugin.cpp
@@ -32,6 +32,7 @@
#include "editor/editor_export.h"
#include "editor/editor_node.h"
+#include "editor/editor_paths.h"
#include "editor/editor_settings.h"
#include "editor/filesystem_dock.h"
#include "editor/project_settings_editor.h"
@@ -257,6 +258,9 @@ EditorSelection *EditorInterface::get_selection() {
Ref<EditorSettings> EditorInterface::get_editor_settings() {
return EditorSettings::get_singleton();
}
+EditorPaths *EditorInterface::get_editor_paths() {
+ return EditorPaths::get_singleton();
+}
EditorResourcePreview *EditorInterface::get_resource_previewer() {
return EditorResourcePreview::get_singleton();
@@ -335,6 +339,7 @@ void EditorInterface::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_selected_path"), &EditorInterface::get_selected_path);
ClassDB::bind_method(D_METHOD("get_current_path"), &EditorInterface::get_current_path);
ClassDB::bind_method(D_METHOD("get_file_system_dock"), &EditorInterface::get_file_system_dock);
+ ClassDB::bind_method(D_METHOD("get_editor_paths"), &EditorInterface::get_editor_paths);
ClassDB::bind_method(D_METHOD("set_plugin_enabled", "plugin", "enabled"), &EditorInterface::set_plugin_enabled);
ClassDB::bind_method(D_METHOD("is_plugin_enabled", "plugin"), &EditorInterface::is_plugin_enabled);
diff --git a/editor/editor_plugin.h b/editor/editor_plugin.h
index 37412e5ebe..3f72e468b2 100644
--- a/editor/editor_plugin.h
+++ b/editor/editor_plugin.h
@@ -54,6 +54,7 @@ class EditorNode3DGizmoPlugin;
class EditorResourcePreview;
class EditorFileSystem;
class EditorToolAddons;
+class EditorPaths;
class FileSystemDock;
class ScriptEditor;
@@ -95,6 +96,7 @@ public:
EditorSelection *get_selection();
//EditorImportExport *get_import_export();
Ref<EditorSettings> get_editor_settings();
+ EditorPaths *get_editor_paths();
EditorResourcePreview *get_resource_previewer();
EditorFileSystem *get_resource_file_system();
diff --git a/editor/editor_resource_picker.cpp b/editor/editor_resource_picker.cpp
index 086afde07c..d1a0bfeded 100644
--- a/editor/editor_resource_picker.cpp
+++ b/editor/editor_resource_picker.cpp
@@ -224,6 +224,13 @@ void EditorResourcePicker::_edit_menu_cbk(int p_which) {
valid_extensions.insert(E->get());
}
+ if (!file_dialog) {
+ file_dialog = memnew(EditorFileDialog);
+ file_dialog->set_file_mode(EditorFileDialog::FILE_MODE_OPEN_FILE);
+ add_child(file_dialog);
+ file_dialog->connect("file_selected", callable_mp(this, &EditorResourcePicker::_file_selected));
+ }
+
file_dialog->clear_filters();
for (Set<String>::Element *E = valid_extensions.front(); E; E = E->next()) {
file_dialog->add_filter("*." + E->get() + " ; " + E->get().to_upper());
@@ -781,10 +788,11 @@ EditorResourcePicker::EditorResourcePicker() {
assign_button->set_flat(true);
assign_button->set_h_size_flags(SIZE_EXPAND_FILL);
assign_button->set_clip_text(true);
- assign_button->connect("pressed", callable_mp(this, &EditorResourcePicker::_resource_selected));
assign_button->set_drag_forwarding(this);
- assign_button->connect("draw", callable_mp(this, &EditorResourcePicker::_button_draw));
add_child(assign_button);
+ assign_button->connect("pressed", callable_mp(this, &EditorResourcePicker::_resource_selected));
+ assign_button->connect("draw", callable_mp(this, &EditorResourcePicker::_button_draw));
+ assign_button->connect("gui_input", callable_mp(this, &EditorResourcePicker::_button_input));
preview_rect = memnew(TextureRect);
preview_rect->set_expand(true);
@@ -793,23 +801,17 @@ EditorResourcePicker::EditorResourcePicker() {
preview_rect->set_offset(SIDE_BOTTOM, -1);
preview_rect->set_offset(SIDE_RIGHT, -1);
assign_button->add_child(preview_rect);
- assign_button->connect("gui_input", callable_mp(this, &EditorResourcePicker::_button_input));
- edit_menu = memnew(PopupMenu);
- add_child(edit_menu);
edit_button = memnew(Button);
edit_button->set_flat(true);
edit_button->set_toggle_mode(true);
- edit_menu->connect("id_pressed", callable_mp(this, &EditorResourcePicker::_edit_menu_cbk));
- edit_menu->connect("popup_hide", callable_mp((BaseButton *)edit_button, &BaseButton::set_pressed), varray(false));
edit_button->connect("pressed", callable_mp(this, &EditorResourcePicker::_update_menu));
add_child(edit_button);
edit_button->connect("gui_input", callable_mp(this, &EditorResourcePicker::_button_input));
-
- file_dialog = memnew(EditorFileDialog);
- file_dialog->set_file_mode(EditorFileDialog::FILE_MODE_OPEN_FILE);
- add_child(file_dialog);
- file_dialog->connect("file_selected", callable_mp(this, &EditorResourcePicker::_file_selected));
+ edit_menu = memnew(PopupMenu);
+ add_child(edit_menu);
+ edit_menu->connect("id_pressed", callable_mp(this, &EditorResourcePicker::_edit_menu_cbk));
+ edit_menu->connect("popup_hide", callable_mp((BaseButton *)edit_button, &BaseButton::set_pressed), varray(false));
}
void EditorScriptPicker::set_create_options(Object *p_menu_node) {
diff --git a/editor/editor_resource_picker.h b/editor/editor_resource_picker.h
index 20fafe1780..9a4b945bc7 100644
--- a/editor/editor_resource_picker.h
+++ b/editor/editor_resource_picker.h
@@ -51,7 +51,7 @@ class EditorResourcePicker : public HBoxContainer {
Button *assign_button;
TextureRect *preview_rect;
Button *edit_button;
- EditorFileDialog *file_dialog;
+ EditorFileDialog *file_dialog = nullptr;
enum MenuOption {
OBJ_MENU_LOAD,
diff --git a/editor/editor_resource_preview.cpp b/editor/editor_resource_preview.cpp
index 138830cdc6..35cf08b4d7 100644
--- a/editor/editor_resource_preview.cpp
+++ b/editor/editor_resource_preview.cpp
@@ -241,7 +241,7 @@ void EditorResourcePreview::_thread() {
_preview_ready(item.path + ":" + itos(item.resource->hash_edited_version()), texture, small_texture, item.id, item.function, item.userdata);
} else {
- String temp_path = EditorSettings::get_singleton()->get_cache_dir();
+ String temp_path = EditorPaths::get_singleton()->get_cache_dir();
String cache_base = ProjectSettings::get_singleton()->globalize_path(item.path).md5_text();
cache_base = temp_path.plus_file("resthumb-" + cache_base);
diff --git a/editor/editor_resource_preview.h b/editor/editor_resource_preview.h
index c4e796dcf1..ffeb22162e 100644
--- a/editor/editor_resource_preview.h
+++ b/editor/editor_resource_preview.h
@@ -101,7 +101,8 @@ protected:
public:
static EditorResourcePreview *get_singleton();
- //callback function is callback(String p_path,Ref<Texture2D> preview,Variant udata) preview null if could not load
+ // p_receiver_func callback has signature (String p_path, Ref<Texture2D> p_preview, Ref<Texture2D> p_preview_small, Variant p_userdata)
+ // p_preview will be null if there was an error
void queue_resource_preview(const String &p_path, Object *p_receiver, const StringName &p_receiver_func, const Variant &p_userdata);
void queue_edited_resource_preview(const Ref<Resource> &p_res, Object *p_receiver, const StringName &p_receiver_func, const Variant &p_userdata);
diff --git a/editor/editor_settings.cpp b/editor/editor_settings.cpp
index 0868c31c45..1255526a4a 100644
--- a/editor/editor_settings.cpp
+++ b/editor/editor_settings.cpp
@@ -419,7 +419,6 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) {
hints["interface/editor/main_font_bold"] = PropertyInfo(Variant::STRING, "interface/editor/main_font_bold", PROPERTY_HINT_GLOBAL_FILE, "*.ttf,*.otf", PROPERTY_USAGE_DEFAULT);
_initial_set("interface/editor/code_font", "");
hints["interface/editor/code_font"] = PropertyInfo(Variant::STRING, "interface/editor/code_font", PROPERTY_HINT_GLOBAL_FILE, "*.ttf,*.otf", PROPERTY_USAGE_DEFAULT);
- _initial_set("interface/editor/dim_editor_on_dialog_popup", true);
_initial_set("interface/editor/low_processor_mode_sleep_usec", 6900); // ~144 FPS
hints["interface/editor/low_processor_mode_sleep_usec"] = PropertyInfo(Variant::FLOAT, "interface/editor/low_processor_mode_sleep_usec", PROPERTY_HINT_RANGE, "1,100000,1", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_RESTART_IF_CHANGED);
_initial_set("interface/editor/unfocused_low_processor_mode_sleep_usec", 50000); // 20 FPS
@@ -503,12 +502,12 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) {
/* Text editor */
// Theme
- _initial_set("text_editor/theme/color_theme", "Adaptive");
- hints["text_editor/theme/color_theme"] = PropertyInfo(Variant::STRING, "text_editor/theme/color_theme", PROPERTY_HINT_ENUM, "Adaptive,Default,Custom");
+ _initial_set("text_editor/theme/color_theme", "Default");
+ hints["text_editor/theme/color_theme"] = PropertyInfo(Variant::STRING, "text_editor/theme/color_theme", PROPERTY_HINT_ENUM, "Default,Godot 2,Custom");
_initial_set("text_editor/theme/line_spacing", 6);
hints["text_editor/theme/line_spacing"] = PropertyInfo(Variant::INT, "text_editor/theme/line_spacing", PROPERTY_HINT_RANGE, "0,50,1");
- _load_default_text_editor_theme();
+ _load_godot2_text_editor_theme();
// Highlighting
_initial_set("text_editor/highlighting/highlight_all_occurrences", true);
@@ -704,6 +703,10 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) {
_initial_set("editors/2d/scroll_to_pan", false);
_initial_set("editors/2d/pan_speed", 20);
+ // Tiles editor
+ _initial_set("editors/tiles_editor/display_grid", true);
+ _initial_set("editors/tiles_editor/grid_color", Color(1.0, 0.5, 0.2, 0.5));
+
// Polygon editor
_initial_set("editors/poly_editor/point_grab_radius", 8);
_initial_set("editors/poly_editor/show_previous_outline", true);
@@ -782,9 +785,8 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) {
};
}
-void EditorSettings::_load_default_text_editor_theme() {
- bool dark_theme = is_dark_theme();
-
+void EditorSettings::_load_godot2_text_editor_theme() {
+ // Godot 2 is only a dark theme; it doesn't have a light theme counterpart.
_initial_set("text_editor/highlighting/symbol_color", Color(0.73, 0.87, 1.0));
_initial_set("text_editor/highlighting/keyword_color", Color(1.0, 1.0, 0.7));
_initial_set("text_editor/highlighting/control_flow_keyword_color", Color(1.0, 0.85, 0.7));
@@ -793,7 +795,7 @@ void EditorSettings::_load_default_text_editor_theme() {
_initial_set("text_editor/highlighting/user_type_color", Color(0.42, 0.67, 0.93));
_initial_set("text_editor/highlighting/comment_color", Color(0.4, 0.4, 0.4));
_initial_set("text_editor/highlighting/string_color", Color(0.94, 0.43, 0.75));
- _initial_set("text_editor/highlighting/background_color", dark_theme ? Color(0.0, 0.0, 0.0, 0.23) : Color(0.2, 0.23, 0.31));
+ _initial_set("text_editor/highlighting/background_color", Color(0.13, 0.12, 0.15));
_initial_set("text_editor/highlighting/completion_background_color", Color(0.17, 0.16, 0.2));
_initial_set("text_editor/highlighting/completion_selected_color", Color(0.26, 0.26, 0.27));
_initial_set("text_editor/highlighting/completion_existing_color", Color(0.13, 0.87, 0.87, 0.87));
@@ -843,7 +845,7 @@ bool EditorSettings::_save_text_editor_theme(String p_file) {
}
bool EditorSettings::_is_default_text_editor_theme(String p_theme_name) {
- return p_theme_name == "default" || p_theme_name == "adaptive" || p_theme_name == "custom";
+ return p_theme_name == "default" || p_theme_name == "godot 2" || p_theme_name == "custom";
}
static Dictionary _get_builtin_script_templates() {
@@ -898,67 +900,26 @@ void EditorSettings::create() {
return; //pointless
}
- DirAccess *dir = nullptr;
-
- String data_path;
- String data_dir;
- String config_path;
- String config_dir;
- String cache_path;
- String cache_dir;
-
Ref<ConfigFile> extra_config = memnew(ConfigFile);
- String exe_path = OS::get_singleton()->get_executable_path().get_base_dir();
- DirAccess *d = DirAccess::create_for_path(exe_path);
- bool self_contained = false;
-
- if (d->file_exists(exe_path + "/._sc_")) {
- self_contained = true;
- Error err = extra_config->load(exe_path + "/._sc_");
+ if (EditorPaths::get_singleton()->is_self_contained()) {
+ Error err = extra_config->load(EditorPaths::get_singleton()->get_self_contained_file());
if (err != OK) {
- ERR_PRINT("Can't load config from path '" + exe_path + "/._sc_'.");
- }
- } else if (d->file_exists(exe_path + "/_sc_")) {
- self_contained = true;
- Error err = extra_config->load(exe_path + "/_sc_");
- if (err != OK) {
- ERR_PRINT("Can't load config from path '" + exe_path + "/_sc_'.");
+ ERR_PRINT("Can't load extra config from path :" + EditorPaths::get_singleton()->get_self_contained_file());
}
}
- memdelete(d);
- if (self_contained) {
- // editor is self contained, all in same folder
- data_path = exe_path;
- data_dir = data_path.plus_file("editor_data");
- config_path = exe_path;
- config_dir = data_dir;
- cache_path = exe_path;
- cache_dir = data_dir.plus_file("cache");
- } else {
- // Typically XDG_DATA_HOME or %APPDATA%
- data_path = OS::get_singleton()->get_data_path();
- data_dir = data_path.plus_file(OS::get_singleton()->get_godot_dir_name());
- // Can be different from data_path e.g. on Linux or macOS
- config_path = OS::get_singleton()->get_config_path();
- config_dir = config_path.plus_file(OS::get_singleton()->get_godot_dir_name());
- // Can be different from above paths, otherwise a subfolder of data_dir
- cache_path = OS::get_singleton()->get_cache_path();
- if (cache_path == data_path) {
- cache_dir = data_dir.plus_file("cache");
- } else {
- cache_dir = cache_path.plus_file(OS::get_singleton()->get_godot_dir_name());
- }
- }
+ DirAccess *dir = nullptr;
ClassDB::register_class<EditorSettings>(); //otherwise it can't be unserialized
String config_file_path;
- if (data_path != "" && config_path != "" && cache_path != "") {
+ if (EditorPaths::get_singleton()->are_paths_valid()) {
// Validate/create data dir and subdirectories
+ String data_dir = EditorPaths::get_singleton()->get_data_dir();
+
dir = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
if (dir->change_dir(data_dir) != OK) {
dir->make_dir_recursive(data_dir);
@@ -975,22 +936,11 @@ void EditorSettings::create() {
dir->change_dir("..");
}
- // Validate/create cache dir
-
- if (dir->change_dir(cache_dir) != OK) {
- dir->make_dir_recursive(cache_dir);
- if (dir->change_dir(cache_dir) != OK) {
- ERR_PRINT("Cannot create cache directory!");
- memdelete(dir);
- goto fail;
- }
- }
-
// Validate/create config dir and subdirectories
- if (dir->change_dir(config_dir) != OK) {
- dir->make_dir_recursive(config_dir);
- if (dir->change_dir(config_dir) != OK) {
+ if (dir->change_dir(EditorPaths::get_singleton()->get_config_dir()) != OK) {
+ dir->make_dir_recursive(EditorPaths::get_singleton()->get_config_dir());
+ if (dir->change_dir(EditorPaths::get_singleton()->get_config_dir()) != OK) {
ERR_PRINT("Cannot create config directory!");
memdelete(dir);
goto fail;
@@ -1031,7 +981,7 @@ void EditorSettings::create() {
// Validate editor config file
String config_file_name = "editor_settings-" + itos(VERSION_MAJOR) + ".tres";
- config_file_path = config_dir.plus_file(config_file_name);
+ config_file_path = EditorPaths::get_singleton()->get_config_dir().plus_file(config_file_name);
if (!dir->file_exists(config_file_name)) {
memdelete(dir);
goto fail;
@@ -1048,9 +998,6 @@ void EditorSettings::create() {
singleton->save_changed_setting = true;
singleton->config_file_path = config_file_path;
- singleton->settings_dir = config_dir;
- singleton->data_dir = data_dir;
- singleton->cache_dir = cache_dir;
print_verbose("EditorSettings: Load OK!");
@@ -1065,6 +1012,8 @@ void EditorSettings::create() {
fail:
// patch init projects
+ String exe_path = OS::get_singleton()->get_executable_path().get_base_dir();
+
if (extra_config->has_section("init_projects")) {
Vector<String> list = extra_config->get_value("init_projects", "list");
for (int i = 0; i < list.size(); i++) {
@@ -1076,9 +1025,6 @@ fail:
singleton = Ref<EditorSettings>(memnew(EditorSettings));
singleton->save_changed_setting = true;
singleton->config_file_path = config_file_path;
- singleton->settings_dir = config_dir;
- singleton->data_dir = data_dir;
- singleton->cache_dir = cache_dir;
singleton->_load_defaults(extra_config);
singleton->setup_language();
singleton->setup_network();
@@ -1308,30 +1254,22 @@ void EditorSettings::add_property_hint(const PropertyInfo &p_hint) {
// Data directories
-String EditorSettings::get_data_dir() const {
- return data_dir;
-}
-
String EditorSettings::get_templates_dir() const {
- return get_data_dir().plus_file("templates");
+ return EditorPaths::get_singleton()->get_data_dir().plus_file("templates");
}
// Config directories
-String EditorSettings::get_settings_dir() const {
- return settings_dir;
-}
-
String EditorSettings::get_project_settings_dir() const {
return EditorSettings::PROJECT_EDITOR_SETTINGS_PATH;
}
String EditorSettings::get_text_editor_themes_dir() const {
- return get_settings_dir().plus_file("text_editor_themes");
+ return EditorPaths::get_singleton()->get_settings_dir().plus_file("text_editor_themes");
}
String EditorSettings::get_script_templates_dir() const {
- return get_settings_dir().plus_file("script_templates");
+ return EditorPaths::get_singleton()->get_settings_dir().plus_file("script_templates");
}
String EditorSettings::get_project_script_templates_dir() const {
@@ -1340,12 +1278,8 @@ String EditorSettings::get_project_script_templates_dir() const {
// Cache directory
-String EditorSettings::get_cache_dir() const {
- return cache_dir;
-}
-
String EditorSettings::get_feature_profiles_dir() const {
- return get_settings_dir().plus_file("feature_profiles");
+ return EditorPaths::get_singleton()->get_settings_dir().plus_file("feature_profiles");
}
// Metadata
@@ -1432,7 +1366,7 @@ bool EditorSettings::is_dark_theme() {
}
void EditorSettings::list_text_editor_themes() {
- String themes = "Adaptive,Default,Custom";
+ String themes = "Default,Godot 2,Custom";
DirAccess *d = DirAccess::open(get_text_editor_themes_dir());
if (d) {
@@ -1460,8 +1394,8 @@ void EditorSettings::load_text_editor_theme() {
String p_file = get("text_editor/theme/color_theme");
if (_is_default_text_editor_theme(p_file.get_file().to_lower())) {
- if (p_file == "Default") {
- _load_default_text_editor_theme();
+ if (p_file == "Godot 2") {
+ _load_godot2_text_editor_theme();
}
return; // sorry for "Settings changed" console spam
}
@@ -1572,7 +1506,7 @@ Vector<String> EditorSettings::get_script_templates(const String &p_extension, c
}
String EditorSettings::get_editor_layouts_config() const {
- return get_settings_dir().plus_file("editor_layouts.cfg");
+ return EditorPaths::get_singleton()->get_settings_dir().plus_file("editor_layouts.cfg");
}
// Shortcuts
@@ -1774,7 +1708,6 @@ void EditorSettings::_bind_methods() {
ClassDB::bind_method(D_METHOD("property_get_revert", "name"), &EditorSettings::property_get_revert);
ClassDB::bind_method(D_METHOD("add_property_info", "info"), &EditorSettings::_add_property_info_bind);
- ClassDB::bind_method(D_METHOD("get_settings_dir"), &EditorSettings::get_settings_dir);
ClassDB::bind_method(D_METHOD("get_project_settings_dir"), &EditorSettings::get_project_settings_dir);
ClassDB::bind_method(D_METHOD("set_project_metadata", "section", "key", "data"), &EditorSettings::set_project_metadata);
diff --git a/editor/editor_settings.h b/editor/editor_settings.h
index e5f8527faf..4c361403a9 100644
--- a/editor/editor_settings.h
+++ b/editor/editor_settings.h
@@ -36,6 +36,7 @@
#include "core/object/class_db.h"
#include "core/os/thread_safe.h"
#include "core/string/translation.h"
+#include "editor/editor_paths.h"
#include "scene/gui/shortcut.h"
class EditorPlugin;
@@ -87,12 +88,7 @@ private:
mutable Map<String, Ref<Shortcut>> shortcuts;
Map<String, List<Ref<InputEvent>>> builtin_action_overrides;
- String resource_path;
- String settings_dir;
- String data_dir;
- String cache_dir;
String config_file_path;
- String project_config_dir;
Vector<String> favorites;
Vector<String> recent_dirs;
@@ -108,7 +104,7 @@ private:
void _add_property_info_bind(const Dictionary &p_info);
void _load_defaults(Ref<ConfigFile> p_extra_config = Ref<ConfigFile>());
- void _load_default_text_editor_theme();
+ void _load_godot2_text_editor_theme();
bool _save_text_editor_theme(String p_file);
bool _is_default_text_editor_theme(String p_theme_name);
@@ -153,12 +149,10 @@ public:
String get_data_dir() const;
String get_templates_dir() const;
- String get_settings_dir() const;
String get_project_settings_dir() const;
String get_text_editor_themes_dir() const;
String get_script_templates_dir() const;
String get_project_script_templates_dir() const;
- String get_cache_dir() const;
String get_feature_profiles_dir() const;
void set_project_metadata(const String &p_section, const String &p_key, Variant p_data);
diff --git a/editor/editor_themes.cpp b/editor/editor_themes.cpp
index 81f6096abf..7971dfc313 100644
--- a/editor/editor_themes.cpp
+++ b/editor/editor_themes.cpp
@@ -329,16 +329,18 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
preset_contrast = default_contrast;
} else if (preset == "Light") {
preset_accent_color = Color(0.18, 0.50, 1.00);
- preset_base_color = Color(1.00, 1.00, 1.00);
- preset_contrast = 0.08;
+ preset_base_color = Color(0.9, 0.9, 0.9);
+ // A negative contrast rate looks better for light themes, since it better follows the natural order of UI "elevation".
+ preset_contrast = -0.08;
} else if (preset == "Solarized (Dark)") {
preset_accent_color = Color(0.15, 0.55, 0.82);
preset_base_color = Color(0.04, 0.23, 0.27);
preset_contrast = default_contrast;
} else if (preset == "Solarized (Light)") {
preset_accent_color = Color(0.15, 0.55, 0.82);
- preset_base_color = Color(0.99, 0.96, 0.89);
- preset_contrast = 0.08;
+ preset_base_color = Color(0.89, 0.86, 0.79);
+ // A negative contrast rate looks better for light themes, since it better follows the natural order of UI "elevation".
+ preset_contrast = -0.08;
} else { // Default
preset_accent_color = Color(0.44, 0.73, 0.98);
preset_base_color = Color(0.21, 0.24, 0.29);
@@ -799,8 +801,6 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
style_tree_bg->set_border_color(dark_color_3);
theme->set_stylebox("bg", "Tree", style_tree_bg);
- const Color guide_color = mono_color * Color(1, 1, 1, 0.05);
- Color relationship_line_color = mono_color * Color(1, 1, 1, relationship_line_opacity);
// Tree
theme->set_icon("checked", "Tree", theme->get_icon("GuiChecked", "EditorIcons"));
theme->set_icon("unchecked", "Tree", theme->get_icon("GuiUnchecked", "EditorIcons"));
@@ -817,19 +817,33 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
theme->set_color("font_color", "Tree", font_color);
theme->set_color("font_selected_color", "Tree", mono_color);
theme->set_color("title_button_color", "Tree", font_color);
- theme->set_color("guide_color", "Tree", guide_color);
- theme->set_color("relationship_line_color", "Tree", relationship_line_color);
theme->set_color("drop_position_color", "Tree", accent_color);
theme->set_constant("vseparation", "Tree", widget_default_margin.y - EDSCALE);
theme->set_constant("hseparation", "Tree", 6 * EDSCALE);
theme->set_constant("guide_width", "Tree", border_width);
theme->set_constant("item_margin", "Tree", 3 * default_margin_size * EDSCALE);
theme->set_constant("button_margin", "Tree", default_margin_size * EDSCALE);
- theme->set_constant("draw_relationship_lines", "Tree", relationship_line_opacity >= 0.01);
- theme->set_constant("draw_guides", "Tree", relationship_line_opacity < 0.01);
theme->set_constant("scroll_border", "Tree", 40 * EDSCALE);
theme->set_constant("scroll_speed", "Tree", 12);
+ const Color guide_color = mono_color * Color(1, 1, 1, 0.05);
+ Color relationship_line_color = mono_color * Color(1, 1, 1, relationship_line_opacity);
+
+ theme->set_constant("draw_guides", "Tree", relationship_line_opacity < 0.01);
+ theme->set_color("guide_color", "Tree", guide_color);
+
+ int relationship_line_width = 1;
+ Color parent_line_color = mono_color * Color(1, 1, 1, CLAMP(relationship_line_opacity + 0.45, 0.0, 1.0));
+ Color children_line_color = mono_color * Color(1, 1, 1, CLAMP(relationship_line_opacity + 0.25, 0.0, 1.0));
+ theme->set_constant("draw_relationship_lines", "Tree", relationship_line_opacity >= 0.01);
+ theme->set_constant("relationship_line_width", "Tree", relationship_line_width);
+ theme->set_constant("parent_hl_line_width", "Tree", relationship_line_width * 2);
+ theme->set_constant("children_hl_line_width", "Tree", relationship_line_width);
+ theme->set_constant("parent_hl_line_margin", "Tree", relationship_line_width * 3);
+ theme->set_color("relationship_line_color", "Tree", relationship_line_color);
+ theme->set_color("parent_hl_line_color", "Tree", parent_line_color);
+ theme->set_color("children_hl_line_color", "Tree", children_line_color);
+
Ref<StyleBoxFlat> style_tree_btn = style_default->duplicate();
style_tree_btn->set_bg_color(highlight_color);
style_tree_btn->set_border_width_all(0);
@@ -1343,7 +1357,8 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
const Color comment_color = dim_color;
const Color string_color = (dark_theme ? Color(1.0, 0.85, 0.26) : Color(1.0, 0.82, 0.09)).lerp(mono_color, dark_theme ? 0.5 : 0.3);
- const Color te_background_color = dark_theme ? background_color : base_color;
+ // Use the brightest background color on a light theme (which generally uses a negative contrast rate).
+ const Color te_background_color = dark_theme ? background_color : dark_color_3;
const Color completion_background_color = dark_theme ? base_color : background_color;
const Color completion_selected_color = alpha1;
const Color completion_existing_color = alpha2;
@@ -1372,7 +1387,7 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
EditorSettings *setting = EditorSettings::get_singleton();
String text_editor_color_theme = setting->get("text_editor/theme/color_theme");
- if (text_editor_color_theme == "Adaptive") {
+ if (text_editor_color_theme == "Default") {
setting->set_initial_value("text_editor/highlighting/symbol_color", symbol_color, true);
setting->set_initial_value("text_editor/highlighting/keyword_color", keyword_color, true);
setting->set_initial_value("text_editor/highlighting/control_flow_keyword_color", control_flow_keyword_color, true);
@@ -1408,7 +1423,7 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
setting->set_initial_value("text_editor/highlighting/code_folding_color", code_folding_color, true);
setting->set_initial_value("text_editor/highlighting/search_result_color", search_result_color, true);
setting->set_initial_value("text_editor/highlighting/search_result_border_color", search_result_border_color, true);
- } else if (text_editor_color_theme == "Default") {
+ } else if (text_editor_color_theme == "Godot 2") {
setting->load_text_editor_theme();
}
diff --git a/editor/export_template_manager.cpp b/editor/export_template_manager.cpp
index 0f5c01be0e..6e0ae403a2 100644
--- a/editor/export_template_manager.cpp
+++ b/editor/export_template_manager.cpp
@@ -444,7 +444,7 @@ void ExportTemplateManager::_begin_template_download(const String &p_url) {
}
download_data.clear();
- download_templates->set_download_file(EditorSettings::get_singleton()->get_cache_dir().plus_file("tmp_templates.tpz"));
+ download_templates->set_download_file(EditorPaths::get_singleton()->get_cache_dir().plus_file("tmp_templates.tpz"));
download_templates->set_use_threads(true);
Error err = download_templates->request(p_url);
diff --git a/editor/fileserver/editor_file_server.cpp b/editor/fileserver/editor_file_server.cpp
index b04e518b0b..654915e3e5 100644
--- a/editor/fileserver/editor_file_server.cpp
+++ b/editor/fileserver/editor_file_server.cpp
@@ -200,7 +200,7 @@ void EditorFileServer::_subthread_start(void *s) {
cd->connection->put_data(buf4, 4);
encode_uint32(OK, buf4);
cd->connection->put_data(buf4, 4);
- encode_uint64(fa->get_len(), buf4);
+ encode_uint64(fa->get_length(), buf4);
cd->connection->put_data(buf4, 8);
cd->files[id] = fa;
diff --git a/editor/icons/Grid.svg b/editor/icons/Grid.svg
index b80a9d6525..3b63bfb051 100644
--- a/editor/icons/Grid.svg
+++ b/editor/icons/Grid.svg
@@ -1 +1 @@
-<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m1 1v2 10 2h2 12v-2-12h-12zm2 2h2v2h-2zm4 0h2v2h-2zm4 0h2v2h-2zm-8 4h2v2h-2zm4 0h2v2h-2zm4 0h2v2h-2zm-8 4h2v2h-2zm4 0h2v2h-2zm4 0h2v2h-2z" fill="#8da5f3" fill-opacity=".98824"/></svg>
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m1 1v14h14v-14zm1.9999999 2h2v2h-2zm4 0h2v2h-2zm4.0000001 0h2v2h-2zm-8.0000001 4.0000004h2v2h-2zm4 0h2v2h-2zm4.0000001 0h2v2h-2zm-8.0000001 3.9999996h2v2h-2zm4 0h2v2h-2zm4.0000001 0h2v2h-2z" fill="#e0e0e0"/></svg>
diff --git a/editor/icons/RibbonTrailMesh.svg b/editor/icons/RibbonTrailMesh.svg
new file mode 100644
index 0000000000..3f6cf0bfef
--- /dev/null
+++ b/editor/icons/RibbonTrailMesh.svg
@@ -0,0 +1 @@
+<svg height="16" viewBox="0 0 14.999999 14.999999" width="16" xmlns="http://www.w3.org/2000/svg"><g fill="#ffca5f" stroke-width=".25" transform="rotate(90)"><path d="m6.5625-14.062499h1.875v13.124999h-1.875z"/><path d="m.9375-11.249999h1.875v7.499999h-1.875z"/><path d="m12.187499-11.249999h1.875v7.5h-1.875z"/></g></svg>
diff --git a/editor/icons/TubeTrailMesh.svg b/editor/icons/TubeTrailMesh.svg
new file mode 100644
index 0000000000..3ca524226f
--- /dev/null
+++ b/editor/icons/TubeTrailMesh.svg
@@ -0,0 +1 @@
+<svg height="16" viewBox="0 0 14.999999 14.999999" width="16" xmlns="http://www.w3.org/2000/svg"><g fill="#ffca5f"><path d="m14.0625 7.5c0-1.6377-.2123-3.12-.5969-4.2737-.1923-.57682-.4237-1.0754-.7508-1.4905-.3271-.41505-.8259-.79834-1.4648-.79834h-7.5c-.6389 0-1.1396.38329-1.4667.79834s-.5585.91366-.7507 1.4905c-.3846 1.1536-.5951 2.6359-.5951 4.2737s.2105 3.12.5951 4.2737c.1922.57682.4236 1.0754.7507 1.4905.3271.41505.8278.79834 1.4667.79834h7.5c.6389 0 1.1377-.38329 1.4648-.79834s.5585-.91366.7508-1.4905c.3846-1.1536.5969-2.6359.5969-4.2737zm-2.8125-4.6875002c.4358 1.0052002 0 0 .4358 1.0052002.2941.88221.5017 2.2134.5017 3.6823s-.2076 2.8-.5017 3.6823c-.1449.4347 0 0-.4358 1.005199l-7.5.000041c-.1212-.15705-.2929-.57092-.4376-1.0052-.2941-.88221-.4999-2.2134-.4999-3.6823s.2058-2.8.4999-3.6823c.1447-.43433.3164-.8482.4376-1.0052"/><path d="m6.5625-11.25h1.875v7.5h-1.875z" stroke-width=".25" transform="rotate(90)"/></g></svg>
diff --git a/editor/import/resource_importer_image.cpp b/editor/import/resource_importer_image.cpp
index fa6ce5fc89..c5b2a8dc3a 100644
--- a/editor/import/resource_importer_image.cpp
+++ b/editor/import/resource_importer_image.cpp
@@ -75,7 +75,7 @@ Error ResourceImporterImage::import(const String &p_source_file, const String &p
ERR_FAIL_COND_V_MSG(!f, ERR_CANT_OPEN, "Cannot open file from path '" + p_source_file + "'.");
- uint64_t len = f->get_len();
+ uint64_t len = f->get_length();
Vector<uint8_t> data;
data.resize(len);
diff --git a/editor/plugin_config_dialog.cpp b/editor/plugin_config_dialog.cpp
index f496811e0a..7dda61f0bf 100644
--- a/editor/plugin_config_dialog.cpp
+++ b/editor/plugin_config_dialog.cpp
@@ -214,6 +214,7 @@ PluginConfigDialog::PluginConfigDialog() {
desc_edit = memnew(TextEdit);
desc_edit->set_custom_minimum_size(Size2(400, 80) * EDSCALE);
+ desc_edit->set_wrap_enabled(true);
grid->add_child(desc_edit);
Label *author_lb = memnew(Label);
diff --git a/editor/plugins/asset_library_editor_plugin.cpp b/editor/plugins/asset_library_editor_plugin.cpp
index a0d9afee74..93bb170128 100644
--- a/editor/plugins/asset_library_editor_plugin.cpp
+++ b/editor/plugins/asset_library_editor_plugin.cpp
@@ -464,7 +464,7 @@ void EditorAssetLibraryItemDownload::_make_request() {
retry->hide();
download->cancel_request();
- download->set_download_file(EditorSettings::get_singleton()->get_cache_dir().plus_file("tmp_asset_" + itos(asset_id)) + ".zip");
+ download->set_download_file(EditorPaths::get_singleton()->get_cache_dir().plus_file("tmp_asset_" + itos(asset_id)) + ".zip");
Error err = download->request(host);
if (err != OK) {
@@ -702,7 +702,7 @@ void EditorAssetLibrary::_image_update(bool use_cache, bool final, const PackedB
PackedByteArray image_data = p_data;
if (use_cache) {
- String cache_filename_base = EditorSettings::get_singleton()->get_cache_dir().plus_file("assetimage_" + image_queue[p_queue_id].image_url.md5_text());
+ String cache_filename_base = EditorPaths::get_singleton()->get_cache_dir().plus_file("assetimage_" + image_queue[p_queue_id].image_url.md5_text());
FileAccess *file = FileAccess::open(cache_filename_base + ".data", FileAccess::READ);
@@ -781,7 +781,7 @@ void EditorAssetLibrary::_image_request_completed(int p_status, int p_code, cons
if (p_code != HTTPClient::RESPONSE_NOT_MODIFIED) {
for (int i = 0; i < headers.size(); i++) {
if (headers[i].findn("ETag:") == 0) { // Save etag
- String cache_filename_base = EditorSettings::get_singleton()->get_cache_dir().plus_file("assetimage_" + image_queue[p_queue_id].image_url.md5_text());
+ String cache_filename_base = EditorPaths::get_singleton()->get_cache_dir().plus_file("assetimage_" + image_queue[p_queue_id].image_url.md5_text());
String new_etag = headers[i].substr(headers[i].find(":") + 1, headers[i].length()).strip_edges();
FileAccess *file;
@@ -829,7 +829,7 @@ void EditorAssetLibrary::_update_image_queue() {
List<int> to_delete;
for (Map<int, ImageQueue>::Element *E = image_queue.front(); E; E = E->next()) {
if (!E->get().active && current_images < max_images) {
- String cache_filename_base = EditorSettings::get_singleton()->get_cache_dir().plus_file("assetimage_" + E->get().image_url.md5_text());
+ String cache_filename_base = EditorPaths::get_singleton()->get_cache_dir().plus_file("assetimage_" + E->get().image_url.md5_text());
Vector<String> headers;
if (FileAccess::exists(cache_filename_base + ".etag") && FileAccess::exists(cache_filename_base + ".data")) {
diff --git a/editor/plugins/canvas_item_editor_plugin.cpp b/editor/plugins/canvas_item_editor_plugin.cpp
index 21bff41498..2be586733b 100644
--- a/editor/plugins/canvas_item_editor_plugin.cpp
+++ b/editor/plugins/canvas_item_editor_plugin.cpp
@@ -4380,7 +4380,7 @@ void CanvasItemEditor::_update_scrollbars() {
// Calculate scrollable area.
Rect2 canvas_item_rect = Rect2(Point2(), screen_rect);
- if (editor->get_edited_scene()) {
+ if (editor->is_inside_tree() && editor->get_edited_scene()) {
Rect2 content_rect = _get_encompassing_rect(editor->get_edited_scene());
canvas_item_rect.expand_to(content_rect.position);
canvas_item_rect.expand_to(content_rect.position + content_rect.size);
@@ -5361,8 +5361,10 @@ void CanvasItemEditor::_bind_methods() {
ClassDB::bind_method("_queue_update_bone_list", &CanvasItemEditor::_update_bone_list);
ClassDB::bind_method("_update_bone_list", &CanvasItemEditor::_update_bone_list);
ClassDB::bind_method("_reset_create_position", &CanvasItemEditor::_reset_create_position);
+ ClassDB::bind_method(D_METHOD("get_state"), &CanvasItemEditor::get_state);
ClassDB::bind_method(D_METHOD("set_state"), &CanvasItemEditor::set_state);
ClassDB::bind_method(D_METHOD("update_viewport"), &CanvasItemEditor::update_viewport);
+ ClassDB::bind_method(D_METHOD("_zoom_on_position"), &CanvasItemEditor::_zoom_on_position);
ADD_SIGNAL(MethodInfo("item_lock_status_changed"));
ADD_SIGNAL(MethodInfo("item_group_status_changed"));
diff --git a/editor/plugins/editor_preview_plugins.cpp b/editor/plugins/editor_preview_plugins.cpp
index 18cc5d43fb..2d79e4f3e3 100644
--- a/editor/plugins/editor_preview_plugins.cpp
+++ b/editor/plugins/editor_preview_plugins.cpp
@@ -265,7 +265,7 @@ Ref<Texture2D> EditorPackedScenePreviewPlugin::generate(const RES &p_from, const
}
Ref<Texture2D> EditorPackedScenePreviewPlugin::generate_from_path(const String &p_path, const Size2 &p_size) const {
- String temp_path = EditorSettings::get_singleton()->get_cache_dir();
+ String temp_path = EditorPaths::get_singleton()->get_cache_dir();
String cache_base = ProjectSettings::get_singleton()->globalize_path(p_path).md5_text();
cache_base = temp_path.plus_file("resthumb-" + cache_base);
diff --git a/editor/plugins/script_editor_plugin.cpp b/editor/plugins/script_editor_plugin.cpp
index 1147267ce0..623e4ea66e 100644
--- a/editor/plugins/script_editor_plugin.cpp
+++ b/editor/plugins/script_editor_plugin.cpp
@@ -1504,8 +1504,10 @@ void ScriptEditor::_notification(int p_what) {
recent_scripts->set_as_minsize();
- _update_script_colors();
- _update_script_names();
+ if (is_inside_tree()) {
+ _update_script_colors();
+ _update_script_names();
+ }
} break;
case NOTIFICATION_READY: {
diff --git a/editor/plugins/script_text_editor.cpp b/editor/plugins/script_text_editor.cpp
index 72a4bd8243..fe5d830239 100644
--- a/editor/plugins/script_text_editor.cpp
+++ b/editor/plugins/script_text_editor.cpp
@@ -1429,11 +1429,17 @@ void ScriptTextEditor::drop_data_fw(const Point2 &p_point, const Variant &p_data
Array files = d["files"];
String text_to_drop;
+ bool preload = Input::get_singleton()->is_key_pressed(KEY_CTRL);
for (int i = 0; i < files.size(); i++) {
if (i > 0) {
- text_to_drop += ",";
+ text_to_drop += ", ";
+ }
+
+ if (preload) {
+ text_to_drop += "preload(\"" + String(files[i]).c_escape() + "\")";
+ } else {
+ text_to_drop += "\"" + String(files[i]).c_escape() + "\"";
}
- text_to_drop += "\"" + String(files[i]).c_escape() + "\"";
}
te->cursor_set_line(row);
diff --git a/editor/plugins/texture_region_editor_plugin.cpp b/editor/plugins/texture_region_editor_plugin.cpp
index 62ecdb8c32..9526160674 100644
--- a/editor/plugins/texture_region_editor_plugin.cpp
+++ b/editor/plugins/texture_region_editor_plugin.cpp
@@ -937,7 +937,6 @@ void TextureRegionEditor::_edit_region() {
if (cache_map.has(texture->get_rid())) {
autoslice_cache = cache_map[texture->get_rid()];
autoslice_is_dirty = false;
- return;
} else {
if (is_visible() && snap_mode == SNAP_AUTOSLICE) {
_update_autoslice();
diff --git a/editor/plugins/tiles/tile_atlas_view.cpp b/editor/plugins/tiles/tile_atlas_view.cpp
index 6e82951d3d..78f181e321 100644
--- a/editor/plugins/tiles/tile_atlas_view.cpp
+++ b/editor/plugins/tiles/tile_atlas_view.cpp
@@ -39,6 +39,7 @@
#include "scene/gui/texture_rect.h"
#include "editor/editor_scale.h"
+#include "editor/editor_settings.h"
void TileAtlasView::_gui_input(const Ref<InputEvent> &p_event) {
bool ctrl = Input::get_singleton()->is_key_pressed(KEY_CTRL);
@@ -316,6 +317,7 @@ void TileAtlasView::_draw_base_tiles_dark() {
void TileAtlasView::_draw_base_tiles_shape_grid() {
// Draw the shapes.
+ Color grid_color = EditorSettings::get_singleton()->get("editors/tiles_editor/grid_color");
Vector2i tile_shape_size = tile_set->get_tile_size();
for (int i = 0; i < tile_set_atlas_source->get_tiles_count(); i++) {
Vector2i tile_id = tile_set_atlas_source->get_tile_id(i);
@@ -324,7 +326,7 @@ void TileAtlasView::_draw_base_tiles_shape_grid() {
Vector2 origin = texture_region.position + (texture_region.size - tile_shape_size) / 2 + in_tile_base_offset;
// Draw only if the tile shape fits in the texture region
- tile_set->draw_tile_shape(base_tiles_shape_grid, Rect2(origin, tile_shape_size), Color(1.0, 0.5, 0.2, 0.8));
+ tile_set->draw_tile_shape(base_tiles_shape_grid, Rect2(origin, tile_shape_size), grid_color);
}
}
diff --git a/editor/plugins/tiles/tile_map_editor.cpp b/editor/plugins/tiles/tile_map_editor.cpp
index 80ba396936..ef13d8ea12 100644
--- a/editor/plugins/tiles/tile_map_editor.cpp
+++ b/editor/plugins/tiles/tile_map_editor.cpp
@@ -56,10 +56,18 @@ void TileMapEditorTilesPlugin::_notification(int p_what) {
picker_button->set_icon(get_theme_icon("ColorPick", "EditorIcons"));
erase_button->set_icon(get_theme_icon("Eraser", "EditorIcons"));
+ toggle_grid_button->set_icon(get_theme_icon("Grid", "EditorIcons"));
+
missing_atlas_texture_icon = get_theme_icon("TileSet", "EditorIcons");
+
+ toggle_grid_button->set_pressed(EditorSettings::get_singleton()->get("editors/tiles_editor/display_grid"));
break;
case NOTIFICATION_VISIBILITY_CHANGED:
_stop_dragging();
+ break;
+ case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED:
+ toggle_grid_button->set_pressed(EditorSettings::get_singleton()->get("editors/tiles_editor/display_grid"));
+ break;
}
}
@@ -77,6 +85,10 @@ void TileMapEditorTilesPlugin::_on_scattering_spinbox_changed(double p_value) {
scattering = p_value;
}
+void TileMapEditorTilesPlugin::_on_grid_toggled(bool p_pressed) {
+ EditorSettings::get_singleton()->set("editors/tiles_editor/display_grid", p_pressed);
+}
+
void TileMapEditorTilesPlugin::_update_toolbar() {
// Stop draggig if needed.
_stop_dragging();
@@ -604,7 +616,9 @@ void TileMapEditorTilesPlugin::forward_canvas_draw_over_viewport(Control *p_over
if (drag_type == DRAG_TYPE_MOVE || (drag_type == DRAG_TYPE_SELECT && !Input::get_singleton()->is_key_pressed(KEY_CTRL) && !Input::get_singleton()->is_key_pressed(KEY_SHIFT))) {
// Do nothing
} else {
- tile_map->draw_cells_outline(p_overlay, tile_map_selection, Color(0.0, 0.0, 1.0), xform);
+ Color grid_color = EditorSettings::get_singleton()->get("editors/tiles_editor/grid_color");
+ Color selection_color = Color().from_hsv(Math::fposmod(grid_color.get_h() + 0.5, 1.0), grid_color.get_s(), grid_color.get_v(), 1.0);
+ tile_map->draw_cells_outline(p_overlay, tile_map_selection, selection_color, xform);
}
}
@@ -703,21 +717,27 @@ void TileMapEditorTilesPlugin::forward_canvas_draw_over_viewport(Control *p_over
const int fading = 5;
// Draw the lines of the grid behind the preview.
- if (drawn_grid_rect.size.x > 0 && drawn_grid_rect.size.y > 0) {
- drawn_grid_rect = drawn_grid_rect.grow(fading);
- for (int x = drawn_grid_rect.position.x; x < (drawn_grid_rect.position.x + drawn_grid_rect.size.x); x++) {
- for (int y = drawn_grid_rect.position.y; y < (drawn_grid_rect.position.y + drawn_grid_rect.size.y); y++) {
- Vector2i pos_in_rect = Vector2i(x, y) - drawn_grid_rect.position;
-
- // Fade out the border of the grid.
- float left_opacity = CLAMP(Math::inverse_lerp(0.0f, (float)fading, (float)pos_in_rect.x), 0.0f, 1.0f);
- float right_opacity = CLAMP(Math::inverse_lerp((float)drawn_grid_rect.size.x, (float)(drawn_grid_rect.size.x - fading), (float)pos_in_rect.x), 0.0f, 1.0f);
- float top_opacity = CLAMP(Math::inverse_lerp(0.0f, (float)fading, (float)pos_in_rect.y), 0.0f, 1.0f);
- float bottom_opacity = CLAMP(Math::inverse_lerp((float)drawn_grid_rect.size.y, (float)(drawn_grid_rect.size.y - fading), (float)pos_in_rect.y), 0.0f, 1.0f);
- float opacity = CLAMP(MIN(left_opacity, MIN(right_opacity, MIN(top_opacity, bottom_opacity))) + 0.1, 0.0f, 1.0f);
-
- Rect2 cell_region = xform.xform(Rect2(tile_map->map_to_world(Vector2(x, y)) - tile_shape_size / 2, tile_shape_size));
- tile_set->draw_tile_shape(p_overlay, cell_region, Color(1.0, 0.5, 0.2, 0.5 * opacity), false);
+ bool display_grid = EditorSettings::get_singleton()->get("editors/tiles_editor/display_grid");
+ if (display_grid) {
+ Color grid_color = EditorSettings::get_singleton()->get("editors/tiles_editor/grid_color");
+ if (drawn_grid_rect.size.x > 0 && drawn_grid_rect.size.y > 0) {
+ drawn_grid_rect = drawn_grid_rect.grow(fading);
+ for (int x = drawn_grid_rect.position.x; x < (drawn_grid_rect.position.x + drawn_grid_rect.size.x); x++) {
+ for (int y = drawn_grid_rect.position.y; y < (drawn_grid_rect.position.y + drawn_grid_rect.size.y); y++) {
+ Vector2i pos_in_rect = Vector2i(x, y) - drawn_grid_rect.position;
+
+ // Fade out the border of the grid.
+ float left_opacity = CLAMP(Math::inverse_lerp(0.0f, (float)fading, (float)pos_in_rect.x), 0.0f, 1.0f);
+ float right_opacity = CLAMP(Math::inverse_lerp((float)drawn_grid_rect.size.x, (float)(drawn_grid_rect.size.x - fading), (float)pos_in_rect.x), 0.0f, 1.0f);
+ float top_opacity = CLAMP(Math::inverse_lerp(0.0f, (float)fading, (float)pos_in_rect.y), 0.0f, 1.0f);
+ float bottom_opacity = CLAMP(Math::inverse_lerp((float)drawn_grid_rect.size.y, (float)(drawn_grid_rect.size.y - fading), (float)pos_in_rect.y), 0.0f, 1.0f);
+ float opacity = CLAMP(MIN(left_opacity, MIN(right_opacity, MIN(top_opacity, bottom_opacity))) + 0.1, 0.0f, 1.0f);
+
+ Rect2 cell_region = xform.xform(Rect2(tile_map->map_to_world(Vector2(x, y)) - tile_shape_size / 2, tile_shape_size));
+ Color color = grid_color;
+ color.a = color.a * opacity;
+ tile_set->draw_tile_shape(p_overlay, cell_region, color, false);
+ }
}
}
}
@@ -1417,9 +1437,11 @@ void TileMapEditorTilesPlugin::_tile_atlas_control_draw() {
}
// Draw the selection.
+ Color grid_color = EditorSettings::get_singleton()->get("editors/tiles_editor/grid_color");
+ Color selection_color = Color().from_hsv(Math::fposmod(grid_color.get_h() + 0.5, 1.0), grid_color.get_s(), grid_color.get_v(), 1.0);
for (Set<TileMapCell>::Element *E = tile_set_selection.front(); E; E = E->next()) {
if (E->get().source_id == source_id && E->get().alternative_tile == 0) {
- tile_atlas_control->draw_rect(atlas->get_tile_texture_region(E->get().get_atlas_coords()), Color(0.0, 0.0, 1.0), false);
+ tile_atlas_control->draw_rect(atlas->get_tile_texture_region(E->get().get_atlas_coords()), selection_color, false);
}
}
@@ -1445,9 +1467,9 @@ void TileMapEditorTilesPlugin::_tile_atlas_control_draw() {
}
}
}
-
+ Color selection_rect_color = selection_color.lightened(0.2);
for (Set<Vector2i>::Element *E = to_draw.front(); E; E = E->next()) {
- tile_atlas_control->draw_rect(atlas->get_tile_texture_region(E->get()), Color(0.8, 0.8, 1.0), false);
+ tile_atlas_control->draw_rect(atlas->get_tile_texture_region(E->get()), selection_rect_color, false);
}
}
}
@@ -1730,6 +1752,7 @@ TileMapEditorTilesPlugin::TileMapEditorTilesPlugin() {
// --- Toolbar ---
toolbar = memnew(HBoxContainer);
+ toolbar->set_h_size_flags(SIZE_EXPAND_FILL);
HBoxContainer *tilemap_tiles_tools_buttons = memnew(HBoxContainer);
@@ -1774,7 +1797,6 @@ TileMapEditorTilesPlugin::TileMapEditorTilesPlugin() {
bucket_tool_button->set_shortcut(ED_SHORTCUT("tiles_editor/bucket_tool", "Bucket", KEY_B));
bucket_tool_button->connect("pressed", callable_mp(this, &TileMapEditorTilesPlugin::_update_toolbar));
tilemap_tiles_tools_buttons->add_child(bucket_tool_button);
-
toolbar->add_child(tilemap_tiles_tools_buttons);
// -- TileMap tool settings --
@@ -1834,6 +1856,18 @@ TileMapEditorTilesPlugin::TileMapEditorTilesPlugin() {
_on_random_tile_checkbox_toggled(false);
+ // Wide empty separation control.
+ Control *h_empty_space = memnew(Control);
+ h_empty_space->set_h_size_flags(SIZE_EXPAND_FILL);
+ toolbar->add_child(h_empty_space);
+
+ // Grid toggle.
+ toggle_grid_button = memnew(Button);
+ toggle_grid_button->set_flat(true);
+ toggle_grid_button->set_toggle_mode(true);
+ toggle_grid_button->connect("toggled", callable_mp(this, &TileMapEditorTilesPlugin::_on_grid_toggled));
+ toolbar->add_child(toggle_grid_button);
+
// Default tool.
paint_tool_button->set_pressed(true);
_update_toolbar();
@@ -3426,19 +3460,25 @@ void TileMapEditor::forward_canvas_draw_over_viewport(Control *p_overlay) {
}
// Draw the grid.
- for (int x = displayed_rect.position.x; x < (displayed_rect.position.x + displayed_rect.size.x); x++) {
- for (int y = displayed_rect.position.y; y < (displayed_rect.position.y + displayed_rect.size.y); y++) {
- Vector2i pos_in_rect = Vector2i(x, y) - displayed_rect.position;
+ bool display_grid = EditorSettings::get_singleton()->get("editors/tiles_editor/display_grid");
+ if (display_grid) {
+ Color grid_color = EditorSettings::get_singleton()->get("editors/tiles_editor/grid_color");
+ for (int x = displayed_rect.position.x; x < (displayed_rect.position.x + displayed_rect.size.x); x++) {
+ for (int y = displayed_rect.position.y; y < (displayed_rect.position.y + displayed_rect.size.y); y++) {
+ Vector2i pos_in_rect = Vector2i(x, y) - displayed_rect.position;
- // Fade out the border of the grid.
- float left_opacity = CLAMP(Math::inverse_lerp(0.0f, (float)fading, (float)pos_in_rect.x), 0.0f, 1.0f);
- float right_opacity = CLAMP(Math::inverse_lerp((float)displayed_rect.size.x, (float)(displayed_rect.size.x - fading), (float)pos_in_rect.x), 0.0f, 1.0f);
- float top_opacity = CLAMP(Math::inverse_lerp(0.0f, (float)fading, (float)pos_in_rect.y), 0.0f, 1.0f);
- float bottom_opacity = CLAMP(Math::inverse_lerp((float)displayed_rect.size.y, (float)(displayed_rect.size.y - fading), (float)pos_in_rect.y), 0.0f, 1.0f);
- float opacity = CLAMP(MIN(left_opacity, MIN(right_opacity, MIN(top_opacity, bottom_opacity))) + 0.1, 0.0f, 1.0f);
+ // Fade out the border of the grid.
+ float left_opacity = CLAMP(Math::inverse_lerp(0.0f, (float)fading, (float)pos_in_rect.x), 0.0f, 1.0f);
+ float right_opacity = CLAMP(Math::inverse_lerp((float)displayed_rect.size.x, (float)(displayed_rect.size.x - fading), (float)pos_in_rect.x), 0.0f, 1.0f);
+ float top_opacity = CLAMP(Math::inverse_lerp(0.0f, (float)fading, (float)pos_in_rect.y), 0.0f, 1.0f);
+ float bottom_opacity = CLAMP(Math::inverse_lerp((float)displayed_rect.size.y, (float)(displayed_rect.size.y - fading), (float)pos_in_rect.y), 0.0f, 1.0f);
+ float opacity = CLAMP(MIN(left_opacity, MIN(right_opacity, MIN(top_opacity, bottom_opacity))) + 0.1, 0.0f, 1.0f);
- Rect2 cell_region = xform.xform(Rect2(tile_map->map_to_world(Vector2(x, y)) - tile_shape_size / 2, tile_shape_size));
- tile_set->draw_tile_shape(p_overlay, cell_region, Color(1.0, 0.5, 0.2, 0.5 * opacity), false);
+ Rect2 cell_region = xform.xform(Rect2(tile_map->map_to_world(Vector2(x, y)) - tile_shape_size / 2, tile_shape_size));
+ Color color = grid_color;
+ color.a = color.a * opacity;
+ tile_set->draw_tile_shape(p_overlay, cell_region, color, false);
+ }
}
}
@@ -3500,9 +3540,8 @@ TileMapEditor::TileMapEditor() {
// --- TileMap toolbar ---
tilemap_toolbar = memnew(HBoxContainer);
- //tilemap_toolbar->add_child(memnew(VSeparator));
+ tilemap_toolbar->set_h_size_flags(SIZE_EXPAND_FILL);
tilemap_toolbar->add_child(tabs);
- //tilemap_toolbar->add_child(memnew(VSeparator));
for (int i = 0; i < tile_map_editor_plugins.size(); i++) {
tile_map_editor_plugins[i]->get_toolbar()->hide();
tilemap_toolbar->add_child(tile_map_editor_plugins[i]->get_toolbar());
diff --git a/editor/plugins/tiles/tile_map_editor.h b/editor/plugins/tiles/tile_map_editor.h
index a1fb9af3ef..a6f4ec3021 100644
--- a/editor/plugins/tiles/tile_map_editor.h
+++ b/editor/plugins/tiles/tile_map_editor.h
@@ -82,6 +82,9 @@ private:
void _on_random_tile_checkbox_toggled(bool p_pressed);
void _on_scattering_spinbox_changed(double p_value);
+ Button *toggle_grid_button;
+ void _on_grid_toggled(bool p_pressed);
+
void _update_toolbar();
///// Tilemap editing. /////
diff --git a/editor/plugins/tiles/tile_set_atlas_source_editor.cpp b/editor/plugins/tiles/tile_set_atlas_source_editor.cpp
index b6e1a318cb..8e7d613027 100644
--- a/editor/plugins/tiles/tile_set_atlas_source_editor.cpp
+++ b/editor/plugins/tiles/tile_set_atlas_source_editor.cpp
@@ -1178,6 +1178,10 @@ Array TileSetAtlasSourceEditor::_get_selection_as_array() {
}
void TileSetAtlasSourceEditor::_tile_atlas_control_draw() {
+ // Colors.
+ Color grid_color = EditorSettings::get_singleton()->get("editors/tiles_editor/grid_color");
+ Color selection_color = Color().from_hsv(Math::fposmod(grid_color.get_h() + 0.5, 1.0), grid_color.get_s(), grid_color.get_v(), 1.0);
+
// Draw the selected tile.
if (tools_button_group->get_pressed_button() == tool_select_button) {
for (Set<TileSelection>::Element *E = selection.front(); E; E = E->next()) {
@@ -1185,7 +1189,7 @@ void TileSetAtlasSourceEditor::_tile_atlas_control_draw() {
if (selected.alternative == 0) {
// Draw the rect.
Rect2 region = tile_set_atlas_source->get_tile_texture_region(selected.tile);
- tile_atlas_control->draw_rect(region, Color(0.2, 0.2, 1.0), false);
+ tile_atlas_control->draw_rect(region, selection_color, false);
}
}
@@ -1234,7 +1238,7 @@ void TileSetAtlasSourceEditor::_tile_atlas_control_draw() {
Color color = Color(0.0, 0.0, 0.0);
if (drag_type == DRAG_TYPE_RECT_SELECT) {
- color = Color(0.5, 0.5, 1.0);
+ color = selection_color.lightened(0.2);
}
Set<Vector2i> to_paint;
@@ -1393,6 +1397,9 @@ void TileSetAtlasSourceEditor::_tile_alternatives_control_mouse_exited() {
}
void TileSetAtlasSourceEditor::_tile_alternatives_control_draw() {
+ Color grid_color = EditorSettings::get_singleton()->get("editors/tiles_editor/grid_color");
+ Color selection_color = Color().from_hsv(Math::fposmod(grid_color.get_h() + 0.5, 1.0), grid_color.get_s(), grid_color.get_v(), 1.0);
+
// Update the hovered alternative tile.
if (tools_button_group->get_pressed_button() == tool_select_button) {
// Draw hovered tile.
@@ -1410,7 +1417,7 @@ void TileSetAtlasSourceEditor::_tile_alternatives_control_draw() {
if (selected.alternative >= 1) {
Rect2i rect = tile_atlas_view->get_alternative_tile_rect(selected.tile, selected.alternative);
if (rect != Rect2i()) {
- alternative_tiles_control->draw_rect(rect, Color(0.2, 0.2, 1.0), false);
+ alternative_tiles_control->draw_rect(rect, selection_color, false);
}
}
}
diff --git a/editor/plugins/tiles/tiles_editor_plugin.cpp b/editor/plugins/tiles/tiles_editor_plugin.cpp
index 971ff15073..fb111efc17 100644
--- a/editor/plugins/tiles/tiles_editor_plugin.cpp
+++ b/editor/plugins/tiles/tiles_editor_plugin.cpp
@@ -202,6 +202,7 @@ TilesEditor::TilesEditor(EditorNode *p_editor) {
// Toolbar.
HBoxContainer *toolbar = memnew(HBoxContainer);
+ toolbar->set_h_size_flags(SIZE_EXPAND_FILL);
add_child(toolbar);
// Switch button.
diff --git a/editor/plugins/visual_shader_editor_plugin.cpp b/editor/plugins/visual_shader_editor_plugin.cpp
index 8be55296d9..a0757439c3 100644
--- a/editor/plugins/visual_shader_editor_plugin.cpp
+++ b/editor/plugins/visual_shader_editor_plugin.cpp
@@ -322,6 +322,12 @@ void VisualShaderGraphPlugin::register_uniform_name(int p_node_id, LineEdit *p_u
links[p_node_id].uniform_name = p_uniform_name;
}
+void VisualShaderGraphPlugin::update_theme() {
+ vector_expanded_color[0] = VisualShaderEditor::get_singleton()->get_theme_color("axis_x_color", "Editor"); // red
+ vector_expanded_color[1] = VisualShaderEditor::get_singleton()->get_theme_color("axis_y_color", "Editor"); // green
+ vector_expanded_color[2] = VisualShaderEditor::get_singleton()->get_theme_color("axis_z_color", "Editor"); // blue
+}
+
void VisualShaderGraphPlugin::add_node(VisualShader::Type p_type, int p_id) {
if (p_type != visual_shader->get_shader_type()) {
return;
@@ -340,6 +346,12 @@ void VisualShaderGraphPlugin::add_node(VisualShader::Type p_type, int p_id) {
Color(1.0, 1.0, 0.0), // sampler
};
+ static const String vector_expanded_name[3] = {
+ "red",
+ "green",
+ "blue"
+ };
+
Ref<VisualShaderNode> vsnode = visual_shader->get_node(p_type, p_id);
Ref<VisualShaderNodeResizableBase> resizable_node = Object::cast_to<VisualShaderNodeResizableBase>(vsnode.ptr());
@@ -553,13 +565,32 @@ void VisualShaderGraphPlugin::add_node(VisualShader::Type p_type, int p_id) {
}
}
- for (int i = 0; i < MAX(vsnode->get_input_port_count(), vsnode->get_output_port_count()); i++) {
+ int output_port_count = 0;
+ for (int i = 0; i < vsnode->get_output_port_count(); i++) {
+ if (vsnode->_is_output_port_expanded(i)) {
+ if (vsnode->get_output_port_type(i) == VisualShaderNode::PORT_TYPE_VECTOR) {
+ output_port_count += 3;
+ }
+ }
+ output_port_count++;
+ }
+ int max_ports = MAX(vsnode->get_input_port_count(), output_port_count);
+ VisualShaderNode::PortType expanded_type = VisualShaderNode::PORT_TYPE_SCALAR;
+ int expanded_port_counter = 0;
+
+ for (int i = 0, j = 0; i < max_ports; i++, j++) {
+ if (expanded_type == VisualShaderNode::PORT_TYPE_VECTOR && expanded_port_counter >= 3) {
+ expanded_type = VisualShaderNode::PORT_TYPE_SCALAR;
+ expanded_port_counter = 0;
+ i -= 3;
+ }
+
if (vsnode->is_port_separator(i)) {
node->add_child(memnew(HSeparator));
port_offset++;
}
- bool valid_left = i < vsnode->get_input_port_count();
+ bool valid_left = j < vsnode->get_input_port_count();
VisualShaderNode::PortType port_left = VisualShaderNode::PORT_TYPE_SCALAR;
bool port_left_used = false;
String name_left;
@@ -567,18 +598,24 @@ void VisualShaderGraphPlugin::add_node(VisualShader::Type p_type, int p_id) {
name_left = vsnode->get_input_port_name(i);
port_left = vsnode->get_input_port_type(i);
for (List<VisualShader::Connection>::Element *E = connections.front(); E; E = E->next()) {
- if (E->get().to_node == p_id && E->get().to_port == i) {
+ if (E->get().to_node == p_id && E->get().to_port == j) {
port_left_used = true;
}
}
}
- bool valid_right = i < vsnode->get_output_port_count();
+ bool valid_right = true;
VisualShaderNode::PortType port_right = VisualShaderNode::PORT_TYPE_SCALAR;
String name_right;
- if (valid_right) {
- name_right = vsnode->get_output_port_name(i);
- port_right = vsnode->get_output_port_type(i);
+
+ if (expanded_type == VisualShaderNode::PORT_TYPE_SCALAR) {
+ valid_right = i < vsnode->get_output_port_count();
+ if (valid_right) {
+ name_right = vsnode->get_output_port_name(i);
+ port_right = vsnode->get_output_port_type(i);
+ }
+ } else {
+ name_right = vector_expanded_name[expanded_port_counter++];
}
HBoxContainer *hb = memnew(HBoxContainer);
@@ -686,17 +723,29 @@ void VisualShaderGraphPlugin::add_node(VisualShader::Type p_type, int p_id) {
}
}
- if (valid_right && visual_shader->get_shader_type() == VisualShader::TYPE_FRAGMENT && port_right != VisualShaderNode::PORT_TYPE_TRANSFORM && port_right != VisualShaderNode::PORT_TYPE_SAMPLER) {
- TextureButton *preview = memnew(TextureButton);
- preview->set_toggle_mode(true);
- preview->set_normal_texture(VisualShaderEditor::get_singleton()->get_theme_icon("GuiVisibilityHidden", "EditorIcons"));
- preview->set_pressed_texture(VisualShaderEditor::get_singleton()->get_theme_icon("GuiVisibilityVisible", "EditorIcons"));
- preview->set_v_size_flags(Control::SIZE_SHRINK_CENTER);
+ if (valid_right) {
+ if (vsnode->is_output_port_expandable(i)) {
+ TextureButton *expand = memnew(TextureButton);
+ expand->set_toggle_mode(true);
+ expand->set_normal_texture(VisualShaderEditor::get_singleton()->get_theme_icon("GuiTreeArrowDown", "EditorIcons"));
+ expand->set_pressed_texture(VisualShaderEditor::get_singleton()->get_theme_icon("GuiTreeArrowRight", "EditorIcons"));
+ expand->set_v_size_flags(Control::SIZE_SHRINK_CENTER);
+ expand->set_pressed(vsnode->_is_output_port_expanded(i));
+ expand->connect("pressed", callable_mp(VisualShaderEditor::get_singleton(), &VisualShaderEditor::_expand_output_port), varray(p_id, i, !vsnode->_is_output_port_expanded(i)), CONNECT_DEFERRED);
+ hb->add_child(expand);
+ }
+ if (visual_shader->get_shader_type() == VisualShader::TYPE_FRAGMENT && port_right != VisualShaderNode::PORT_TYPE_TRANSFORM && port_right != VisualShaderNode::PORT_TYPE_SAMPLER) {
+ TextureButton *preview = memnew(TextureButton);
+ preview->set_toggle_mode(true);
+ preview->set_normal_texture(VisualShaderEditor::get_singleton()->get_theme_icon("GuiVisibilityHidden", "EditorIcons"));
+ preview->set_pressed_texture(VisualShaderEditor::get_singleton()->get_theme_icon("GuiVisibilityVisible", "EditorIcons"));
+ preview->set_v_size_flags(Control::SIZE_SHRINK_CENTER);
- register_output_port(p_id, i, preview);
+ register_output_port(p_id, j, preview);
- preview->connect("pressed", callable_mp(VisualShaderEditor::get_singleton(), &VisualShaderEditor::_preview_select_port), varray(p_id, i), CONNECT_DEFERRED);
- hb->add_child(preview);
+ preview->connect("pressed", callable_mp(VisualShaderEditor::get_singleton(), &VisualShaderEditor::_preview_select_port), varray(p_id, j), CONNECT_DEFERRED);
+ hb->add_child(preview);
+ }
}
if (is_group) {
@@ -708,7 +757,40 @@ void VisualShaderGraphPlugin::add_node(VisualShader::Type p_type, int p_id) {
node->add_child(hb);
+ if (expanded_type != VisualShaderNode::PORT_TYPE_SCALAR) {
+ continue;
+ }
+
node->set_slot(i + port_offset, valid_left, port_left, type_color[port_left], valid_right, port_right, type_color[port_right]);
+
+ if (vsnode->_is_output_port_expanded(i)) {
+ if (vsnode->get_output_port_type(i) == VisualShaderNode::PORT_TYPE_VECTOR) {
+ port_offset++;
+ valid_left = (i + 1) < vsnode->get_input_port_count();
+ port_left = VisualShaderNode::PORT_TYPE_SCALAR;
+ if (valid_left) {
+ port_left = vsnode->get_input_port_type(i + 1);
+ }
+ node->set_slot(i + port_offset, valid_left, port_left, type_color[port_left], true, VisualShaderNode::PORT_TYPE_SCALAR, vector_expanded_color[0]);
+ port_offset++;
+
+ valid_left = (i + 2) < vsnode->get_input_port_count();
+ port_left = VisualShaderNode::PORT_TYPE_SCALAR;
+ if (valid_left) {
+ port_left = vsnode->get_input_port_type(i + 2);
+ }
+ node->set_slot(i + port_offset, valid_left, port_left, type_color[port_left], true, VisualShaderNode::PORT_TYPE_SCALAR, vector_expanded_color[1]);
+ port_offset++;
+
+ valid_left = (i + 3) < vsnode->get_input_port_count();
+ port_left = VisualShaderNode::PORT_TYPE_SCALAR;
+ if (valid_left) {
+ port_left = vsnode->get_input_port_type(i + 3);
+ }
+ node->set_slot(i + port_offset, valid_left, port_left, type_color[port_left], true, VisualShaderNode::PORT_TYPE_SCALAR, vector_expanded_color[2]);
+ expanded_type = VisualShaderNode::PORT_TYPE_VECTOR;
+ }
+ }
}
if (vsnode->get_output_port_for_preview() >= 0) {
@@ -1293,6 +1375,7 @@ void VisualShaderEditor::_update_graph() {
graph_plugin->clear_links();
graph_plugin->make_dirty(true);
+ graph_plugin->update_theme();
for (int n_i = 0; n_i < nodes.size(); n_i++) {
graph_plugin->add_node(type, nodes[n_i]);
@@ -1441,6 +1524,92 @@ void VisualShaderEditor::_change_output_port_name(const String &p_text, Object *
undo_redo->commit_action();
}
+void VisualShaderEditor::_expand_output_port(int p_node, int p_port, bool p_expand) {
+ VisualShader::Type type = get_current_shader_type();
+
+ Ref<VisualShaderNode> node = visual_shader->get_node(type, p_node);
+ ERR_FAIL_COND(!node.is_valid());
+
+ if (p_expand) {
+ undo_redo->create_action(TTR("Expand Output Port"));
+ } else {
+ undo_redo->create_action(TTR("Shrink Output Port"));
+ }
+
+ undo_redo->add_do_method(node.ptr(), "_set_output_port_expanded", p_port, p_expand);
+ undo_redo->add_undo_method(node.ptr(), "_set_output_port_expanded", p_port, !p_expand);
+
+ int type_size = 0;
+ if (node->get_output_port_type(p_port) == VisualShaderNode::PORT_TYPE_VECTOR) {
+ type_size = 3;
+ }
+
+ List<VisualShader::Connection> conns;
+ visual_shader->get_node_connections(type, &conns);
+
+ for (List<VisualShader::Connection>::Element *E = conns.front(); E; E = E->next()) {
+ int from_node = E->get().from_node;
+ int from_port = E->get().from_port;
+ int to_node = E->get().to_node;
+ int to_port = E->get().to_port;
+
+ if (from_node == p_node) {
+ if (p_expand) {
+ if (from_port > p_port) { // reconnect ports after expanded ports
+ undo_redo->add_do_method(visual_shader.ptr(), "disconnect_nodes", type, from_node, from_port, to_node, to_port);
+ undo_redo->add_undo_method(visual_shader.ptr(), "connect_nodes_forced", type, from_node, from_port, to_node, to_port);
+
+ undo_redo->add_do_method(graph_plugin.ptr(), "disconnect_nodes", type, from_node, from_port, to_node, to_port);
+ undo_redo->add_undo_method(graph_plugin.ptr(), "connect_nodes", type, from_node, from_port, to_node, to_port);
+
+ undo_redo->add_do_method(visual_shader.ptr(), "connect_nodes_forced", type, from_node, from_port + type_size, to_node, to_port);
+ undo_redo->add_undo_method(visual_shader.ptr(), "disconnect_nodes", type, from_node, from_port + type_size, to_node, to_port);
+
+ undo_redo->add_do_method(graph_plugin.ptr(), "connect_nodes", type, from_node, from_port + type_size, to_node, to_port);
+ undo_redo->add_undo_method(graph_plugin.ptr(), "disconnect_nodes", type, from_node, from_port + type_size, to_node, to_port);
+ }
+ } else {
+ if (from_port > p_port + type_size) { // reconnect ports after expanded ports
+ undo_redo->add_do_method(visual_shader.ptr(), "disconnect_nodes", type, from_node, from_port, to_node, to_port);
+ undo_redo->add_undo_method(visual_shader.ptr(), "connect_nodes_forced", type, from_node, from_port, to_node, to_port);
+
+ undo_redo->add_do_method(graph_plugin.ptr(), "disconnect_nodes", type, from_node, from_port, to_node, to_port);
+ undo_redo->add_undo_method(graph_plugin.ptr(), "connect_nodes", type, from_node, from_port, to_node, to_port);
+
+ undo_redo->add_do_method(visual_shader.ptr(), "connect_nodes", type, from_node, from_port - type_size, to_node, to_port);
+ undo_redo->add_undo_method(visual_shader.ptr(), "disconnect_nodes", type, from_node, from_port - type_size, to_node, to_port);
+
+ undo_redo->add_do_method(graph_plugin.ptr(), "connect_nodes", type, from_node, from_port - type_size, to_node, to_port);
+ undo_redo->add_undo_method(graph_plugin.ptr(), "disconnect_nodes", type, from_node, from_port - type_size, to_node, to_port);
+ } else if (from_port > p_port) { // disconnect component ports
+ undo_redo->add_do_method(visual_shader.ptr(), "disconnect_nodes", type, from_node, from_port, to_node, to_port);
+ undo_redo->add_undo_method(visual_shader.ptr(), "connect_nodes_forced", type, from_node, from_port, to_node, to_port);
+
+ undo_redo->add_do_method(graph_plugin.ptr(), "disconnect_nodes", type, from_node, from_port, to_node, to_port);
+ undo_redo->add_undo_method(graph_plugin.ptr(), "connect_nodes", type, from_node, from_port, to_node, to_port);
+ }
+ }
+ }
+ }
+
+ int preview_port = node->get_output_port_for_preview();
+ if (p_expand) {
+ if (preview_port > p_port) {
+ undo_redo->add_do_method(node.ptr(), "set_output_port_for_preview", preview_port + type_size);
+ undo_redo->add_undo_method(node.ptr(), "set_output_port_for_preview", preview_port);
+ }
+ } else {
+ if (preview_port > p_port + type_size) {
+ undo_redo->add_do_method(node.ptr(), "set_output_port_for_preview", preview_port - type_size);
+ undo_redo->add_undo_method(node.ptr(), "set_output_port_for_preview", preview_port);
+ }
+ }
+
+ undo_redo->add_do_method(graph_plugin.ptr(), "update_node", type, p_node);
+ undo_redo->add_undo_method(graph_plugin.ptr(), "update_node", type, p_node);
+ undo_redo->commit_action();
+}
+
void VisualShaderEditor::_remove_input_port(int p_node, int p_port) {
VisualShader::Type type = get_current_shader_type();
Ref<VisualShaderNodeGroupBase> node = visual_shader->get_node(type, p_node);
@@ -3469,6 +3638,7 @@ void VisualShaderEditor::_bind_methods() {
ClassDB::bind_method("_float_constant_selected", &VisualShaderEditor::_float_constant_selected);
ClassDB::bind_method("_update_constant", &VisualShaderEditor::_update_constant);
ClassDB::bind_method("_update_uniform", &VisualShaderEditor::_update_uniform);
+ ClassDB::bind_method("_expand_output_port", &VisualShaderEditor::_expand_output_port);
ClassDB::bind_method(D_METHOD("get_drag_data_fw"), &VisualShaderEditor::get_drag_data_fw);
ClassDB::bind_method(D_METHOD("can_drop_data_fw"), &VisualShaderEditor::can_drop_data_fw);
@@ -4090,6 +4260,7 @@ VisualShaderEditor::VisualShaderEditor() {
add_options.push_back(AddOption("TransformDecompose", "Transform", "Composition", "VisualShaderNodeTransformDecompose", TTR("Decomposes transform to four vectors.")));
add_options.push_back(AddOption("Determinant", "Transform", "Functions", "VisualShaderNodeDeterminant", TTR("Calculates the determinant of a transform."), -1, VisualShaderNode::PORT_TYPE_SCALAR));
+ add_options.push_back(AddOption("GetBillboardMatrix", "Transform", "Functions", "VisualShaderNodeBillboard", TTR("Calculates how the object should face the camera to be applied on Model View Matrix output port for 3D objects."), -1, VisualShaderNode::PORT_TYPE_TRANSFORM, TYPE_FLAGS_VERTEX, Shader::MODE_SPATIAL));
add_options.push_back(AddOption("Inverse", "Transform", "Functions", "VisualShaderNodeTransformFunc", TTR("Calculates the inverse of a transform."), VisualShaderNodeTransformFunc::FUNC_INVERSE, VisualShaderNode::PORT_TYPE_TRANSFORM));
add_options.push_back(AddOption("Transpose", "Transform", "Functions", "VisualShaderNodeTransformFunc", TTR("Calculates the transpose of a transform."), VisualShaderNodeTransformFunc::FUNC_TRANSPOSE, VisualShaderNode::PORT_TYPE_TRANSFORM));
diff --git a/editor/plugins/visual_shader_editor_plugin.h b/editor/plugins/visual_shader_editor_plugin.h
index 2f438cc6c8..b3510aafa1 100644
--- a/editor/plugins/visual_shader_editor_plugin.h
+++ b/editor/plugins/visual_shader_editor_plugin.h
@@ -83,6 +83,8 @@ private:
List<VisualShader::Connection> connections;
bool dirty = false;
+ Color vector_expanded_color[3];
+
protected:
static void _bind_methods();
@@ -119,6 +121,7 @@ public:
void set_expression(VisualShader::Type p_type, int p_node_id, const String &p_expression);
int get_constant_index(float p_constant) const;
void update_node_size(int p_node_id);
+ void update_theme();
VisualShader::Type get_shader_type() const;
VisualShaderGraphPlugin();
@@ -401,6 +404,7 @@ class VisualShaderEditor : public VBoxContainer {
void _remove_output_port(int p_node, int p_port);
void _change_output_port_type(int p_type, int p_node, int p_port);
void _change_output_port_name(const String &p_text, Object *p_line_edit, int p_node, int p_port);
+ void _expand_output_port(int p_node, int p_port, bool p_expand);
void _expression_focus_out(Object *code_edit, int p_node);
diff --git a/editor/project_manager.cpp b/editor/project_manager.cpp
index 49a6d28dc1..8bde397b5c 100644
--- a/editor/project_manager.cpp
+++ b/editor/project_manager.cpp
@@ -1484,16 +1484,7 @@ Vector<ProjectList::Item> ProjectList::get_selected_projects() const {
void ProjectList::ensure_project_visible(int p_index) {
const Item &item = _projects[p_index];
-
- int item_top = item.control->get_position().y;
- int item_bottom = item.control->get_position().y + item.control->get_size().y;
-
- if (item_top < get_v_scroll()) {
- set_v_scroll(item_top);
-
- } else if (item_bottom > get_v_scroll() + get_size().y) {
- set_v_scroll(item_bottom - get_size().y);
- }
+ ensure_control_visible(item.control);
}
int ProjectList::get_single_selected_index() const {
diff --git a/main/main.cpp b/main/main.cpp
index 2f191b5f63..d67761db55 100644
--- a/main/main.cpp
+++ b/main/main.cpp
@@ -1445,6 +1445,12 @@ Error Main::setup2(Thread::ID p_main_tid_override) {
}
#endif
+#ifdef TOOLS_ENABLED
+ if (editor || project_manager) {
+ EditorNode::register_editor_paths(project_manager);
+ }
+#endif
+
/* Determine text driver */
if (text_driver == "") {
diff --git a/methods.py b/methods.py
index 6f1e7a7279..1afd1ca0d4 100644
--- a/methods.py
+++ b/methods.py
@@ -787,9 +787,18 @@ def get_compiler_version(env):
return None
else: # TODO: Implement for MSVC
return None
- match = re.search("[0-9]+\.[0-9.]+", version)
+ match = re.search(
+ "(?:(?<=version )|(?<=\) )|(?<=^))"
+ "(?P<major>\d+)"
+ "(?:\.(?P<minor>\d*))?"
+ "(?:\.(?P<patch>\d*))?"
+ "(?:-(?P<metadata1>[0-9a-zA-Z-]*))?"
+ "(?:\+(?P<metadata2>[0-9a-zA-Z-]*))?"
+ "(?: (?P<date>[0-9]{8}|[0-9]{6})(?![0-9a-zA-Z]))?",
+ version,
+ )
if match is not None:
- return list(map(int, match.group().split(".")))
+ return match.groupdict()
else:
return None
diff --git a/misc/dist/ios_xcode/godot_ios.xcodeproj/project.pbxproj b/misc/dist/ios_xcode/godot_ios.xcodeproj/project.pbxproj
index b9ad431e6e..fd69725a21 100644
--- a/misc/dist/ios_xcode/godot_ios.xcodeproj/project.pbxproj
+++ b/misc/dist/ios_xcode/godot_ios.xcodeproj/project.pbxproj
@@ -255,7 +255,7 @@
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
OTHER_LDFLAGS = "$linker_flags";
SDKROOT = iphoneos;
- TARGETED_DEVICE_FAMILY = "1,2";
+ TARGETED_DEVICE_FAMILY = "$targeted_device_family";
};
name = Debug;
};
@@ -294,7 +294,7 @@
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
OTHER_LDFLAGS = "$linker_flags";
SDKROOT = iphoneos;
- TARGETED_DEVICE_FAMILY = "1,2";
+ TARGETED_DEVICE_FAMILY = "$targeted_device_family";
VALIDATE_PRODUCT = YES;
};
name = Release;
@@ -323,7 +323,7 @@
PRODUCT_BUNDLE_IDENTIFIER = $bundle_identifier;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE = "$provisioning_profile_uuid_debug";
- TARGETED_DEVICE_FAMILY = "1,2";
+ TARGETED_DEVICE_FAMILY = "$targeted_device_family";
VALID_ARCHS = "armv7 armv7s arm64 i386 x86_64";
WRAPPER_EXTENSION = app;
};
@@ -353,7 +353,7 @@
PRODUCT_BUNDLE_IDENTIFIER = $bundle_identifier;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE = "$provisioning_profile_uuid_release";
- TARGETED_DEVICE_FAMILY = "1,2";
+ TARGETED_DEVICE_FAMILY = "$targeted_device_family";
VALID_ARCHS = "armv7 armv7s arm64 i386 x86_64";
WRAPPER_EXTENSION = app;
};
diff --git a/misc/dist/ios_xcode/godot_ios.xcodeproj/xcshareddata/xcschemes/godot_ios.xcscheme b/misc/dist/ios_xcode/godot_ios.xcodeproj/xcshareddata/xcschemes/godot_ios.xcscheme
index b6beeb012f..d61a53d5c2 100644
--- a/misc/dist/ios_xcode/godot_ios.xcodeproj/xcshareddata/xcschemes/godot_ios.xcscheme
+++ b/misc/dist/ios_xcode/godot_ios.xcodeproj/xcshareddata/xcschemes/godot_ios.xcscheme
@@ -23,7 +23,7 @@
</BuildActionEntries>
</BuildAction>
<TestAction
- buildConfiguration = "Debug"
+ buildConfiguration = "$default_build_config"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
@@ -42,7 +42,7 @@
</AdditionalOptions>
</TestAction>
<LaunchAction
- buildConfiguration = "Debug"
+ buildConfiguration = "$default_build_config"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
@@ -67,7 +67,7 @@
</AdditionalOptions>
</LaunchAction>
<ProfileAction
- buildConfiguration = "Debug"
+ buildConfiguration = "$default_build_config"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
@@ -84,10 +84,10 @@
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
- buildConfiguration = "Debug">
+ buildConfiguration = "$default_build_config">
</AnalyzeAction>
<ArchiveAction
- buildConfiguration = "Debug"
+ buildConfiguration = "$default_build_config"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>
diff --git a/modules/bmp/image_loader_bmp.cpp b/modules/bmp/image_loader_bmp.cpp
index f22e74cafb..27b49a6609 100644
--- a/modules/bmp/image_loader_bmp.cpp
+++ b/modules/bmp/image_loader_bmp.cpp
@@ -205,7 +205,7 @@ Error ImageLoaderBMP::load_image(Ref<Image> p_image, FileAccess *f,
// A valid bmp file should always at least have a
// file header and a minimal info header
- if (f->get_len() > BITMAP_FILE_HEADER_SIZE + BITMAP_INFO_HEADER_MIN_SIZE) {
+ if (f->get_length() > BITMAP_FILE_HEADER_SIZE + BITMAP_INFO_HEADER_MIN_SIZE) {
// File Header
bmp_header.bmp_file_header.bmp_signature = f->get_16();
if (bmp_header.bmp_file_header.bmp_signature == BITMAP_SIGNATURE) {
diff --git a/modules/csg/csg_shape.cpp b/modules/csg/csg_shape.cpp
index 541b7036ac..67dfdfb5eb 100644
--- a/modules/csg/csg_shape.cpp
+++ b/modules/csg/csg_shape.cpp
@@ -923,45 +923,43 @@ CSGBrush *CSGSphere3D::_build_brush() {
Ref<Material> *materialsw = materials.ptrw();
bool *invertw = invert.ptrw();
+ const double lat_step = 1.0 / rings;
+ const double lon_step = 1.0 / radial_segments;
int face = 0;
- const double lat_step = Math_TAU / rings;
- const double lon_step = Math_TAU / radial_segments;
-
for (int i = 1; i <= rings; i++) {
- double lat0 = lat_step * (i - 1) - Math_TAU / 4;
- double z0 = Math::sin(lat0);
- double zr0 = Math::cos(lat0);
- double u0 = double(i - 1) / rings;
-
- double lat1 = lat_step * i - Math_TAU / 4;
- double z1 = Math::sin(lat1);
- double zr1 = Math::cos(lat1);
- double u1 = double(i) / rings;
-
- for (int j = radial_segments; j >= 1; j--) {
- double lng0 = lon_step * (j - 1);
+ double lat0 = Math_PI * (0.5 - (i - 1) * lat_step);
+ double c0 = Math::cos(lat0);
+ double s0 = Math::sin(lat0);
+ double v0 = double(i - 1) / rings;
+
+ double lat1 = Math_PI * (0.5 - i * lat_step);
+ double c1 = Math::cos(lat1);
+ double s1 = Math::sin(lat1);
+ double v1 = double(i) / rings;
+
+ for (int j = 1; j <= radial_segments; j++) {
+ double lng0 = Math_TAU * (0.5 - (j - 1) * lon_step);
double x0 = Math::cos(lng0);
double y0 = Math::sin(lng0);
- double v0 = double(i - 1) / radial_segments;
+ double u0 = double(j - 1) / radial_segments;
- double lng1 = lon_step * j;
+ double lng1 = Math_TAU * (0.5 - j * lon_step);
double x1 = Math::cos(lng1);
double y1 = Math::sin(lng1);
- double v1 = double(i) / radial_segments;
+ double u1 = double(j) / radial_segments;
Vector3 v[4] = {
- Vector3(x1 * zr0, z0, y1 * zr0) * radius,
- Vector3(x1 * zr1, z1, y1 * zr1) * radius,
- Vector3(x0 * zr1, z1, y0 * zr1) * radius,
- Vector3(x0 * zr0, z0, y0 * zr0) * radius
+ Vector3(x0 * c0, s0, y0 * c0) * radius,
+ Vector3(x1 * c0, s0, y1 * c0) * radius,
+ Vector3(x1 * c1, s1, y1 * c1) * radius,
+ Vector3(x0 * c1, s1, y0 * c1) * radius,
};
Vector2 u[4] = {
- Vector2(v1, u0),
- Vector2(v1, u1),
- Vector2(v0, u1),
- Vector2(v0, u0),
-
+ Vector2(u0, v0),
+ Vector2(u1, v0),
+ Vector2(u1, v1),
+ Vector2(u0, v1),
};
if (i < rings) {
diff --git a/modules/fbx/editor_scene_importer_fbx.cpp b/modules/fbx/editor_scene_importer_fbx.cpp
index 58bc450e9d..f60277f60a 100644
--- a/modules/fbx/editor_scene_importer_fbx.cpp
+++ b/modules/fbx/editor_scene_importer_fbx.cpp
@@ -102,7 +102,7 @@ Node3D *EditorSceneImporterFBX::import_scene(const String &p_path, uint32_t p_fl
FBXDocParser::TokenList tokens;
bool is_binary = false;
- data.resize(f->get_len());
+ data.resize(f->get_length());
ERR_FAIL_COND_V(data.size() < 64, nullptr);
diff --git a/modules/gdnative/pluginscript/pluginscript_script.cpp b/modules/gdnative/pluginscript/pluginscript_script.cpp
index a0d7fbf726..a48245814f 100644
--- a/modules/gdnative/pluginscript/pluginscript_script.cpp
+++ b/modules/gdnative/pluginscript/pluginscript_script.cpp
@@ -212,6 +212,8 @@ ScriptInstance *PluginScript::instance_create(Object *p_this) {
}
bool PluginScript::instance_has(const Object *p_this) const {
+ ERR_FAIL_COND_V(!_language, false);
+
_language->lock();
bool hasit = _instances.has((Object *)p_this);
_language->unlock();
@@ -441,7 +443,7 @@ Error PluginScript::load_source_code(const String &p_path) {
FileAccess *f = FileAccess::open(p_path, FileAccess::READ, &err);
ERR_FAIL_COND_V_MSG(err, err, "Cannot open file '" + p_path + "'.");
- uint64_t len = f->get_len();
+ uint64_t len = f->get_length();
sourcef.resize(len + 1);
uint8_t *w = sourcef.ptrw();
uint64_t r = f->get_buffer(w, len);
diff --git a/modules/gdnative/videodecoder/video_stream_gdnative.cpp b/modules/gdnative/videodecoder/video_stream_gdnative.cpp
index 1b7838846f..8b0434c7dd 100644
--- a/modules/gdnative/videodecoder/video_stream_gdnative.cpp
+++ b/modules/gdnative/videodecoder/video_stream_gdnative.cpp
@@ -58,7 +58,7 @@ int64_t GDAPI godot_videodecoder_file_seek(void *ptr, int64_t pos, int whence) {
FileAccess *file = reinterpret_cast<FileAccess *>(ptr);
if (file) {
- int64_t len = file->get_len();
+ int64_t len = file->get_length();
switch (whence) {
case SEEK_SET: {
if (pos > len) {
diff --git a/modules/gdscript/editor/gdscript_highlighter.cpp b/modules/gdscript/editor/gdscript_highlighter.cpp
index ca646dff15..d34cccb3af 100644
--- a/modules/gdscript/editor/gdscript_highlighter.cpp
+++ b/modules/gdscript/editor/gdscript_highlighter.cpp
@@ -546,7 +546,7 @@ void GDScriptSyntaxHighlighter::_update_cache() {
}
const String text_edit_color_theme = EditorSettings::get_singleton()->get("text_editor/theme/color_theme");
- const bool default_theme = text_edit_color_theme == "Default";
+ const bool default_theme = text_edit_color_theme == "Godot 2";
if (default_theme || EditorSettings::get_singleton()->is_dark_theme()) {
function_definition_color = Color(0.4, 0.9, 1.0);
@@ -558,7 +558,7 @@ void GDScriptSyntaxHighlighter::_update_cache() {
EDITOR_DEF("text_editor/highlighting/gdscript/function_definition_color", function_definition_color);
EDITOR_DEF("text_editor/highlighting/gdscript/node_path_color", node_path_color);
- if (text_edit_color_theme == "Adaptive" || default_theme) {
+ if (text_edit_color_theme == "Default" || default_theme) {
EditorSettings::get_singleton()->set_initial_value(
"text_editor/highlighting/gdscript/function_definition_color",
function_definition_color,
diff --git a/modules/gdscript/gdscript.cpp b/modules/gdscript/gdscript.cpp
index 2896420239..d7814e85b0 100644
--- a/modules/gdscript/gdscript.cpp
+++ b/modules/gdscript/gdscript.cpp
@@ -1045,7 +1045,7 @@ Error GDScript::load_source_code(const String &p_path) {
ERR_FAIL_COND_V(err, err);
}
- uint64_t len = f->get_len();
+ uint64_t len = f->get_length();
sourcef.resize(len + 1);
uint8_t *w = sourcef.ptrw();
uint64_t r = f->get_buffer(w, len);
diff --git a/modules/gdscript/gdscript_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp
index 7b04959227..d8d60b35c6 100644
--- a/modules/gdscript/gdscript_analyzer.cpp
+++ b/modules/gdscript/gdscript_analyzer.cpp
@@ -543,6 +543,7 @@ void GDScriptAnalyzer::resolve_class_interface(GDScriptParser::ClassNode *p_clas
} else {
// TODO: Add warning.
mark_node_unsafe(member.variable->initializer);
+ member.variable->use_conversion_assign = true;
}
} else if (datatype.builtin_type == Variant::INT && member.variable->initializer->get_datatype().builtin_type == Variant::FLOAT) {
#ifdef DEBUG_ENABLED
@@ -552,6 +553,7 @@ void GDScriptAnalyzer::resolve_class_interface(GDScriptParser::ClassNode *p_clas
if (member.variable->initializer->get_datatype().is_variant()) {
// TODO: Warn unsafe assign.
mark_node_unsafe(member.variable->initializer);
+ member.variable->use_conversion_assign = true;
}
}
} else if (member.variable->infer_datatype) {
@@ -1145,6 +1147,7 @@ void GDScriptAnalyzer::resolve_variable(GDScriptParser::VariableNode *p_variable
} else {
// TODO: Add warning.
mark_node_unsafe(p_variable->initializer);
+ p_variable->use_conversion_assign = true;
}
#ifdef DEBUG_ENABLED
} else if (type.builtin_type == Variant::INT && p_variable->initializer->get_datatype().builtin_type == Variant::FLOAT) {
@@ -1154,6 +1157,7 @@ void GDScriptAnalyzer::resolve_variable(GDScriptParser::VariableNode *p_variable
if (p_variable->initializer->get_datatype().is_variant()) {
// TODO: Warn unsafe assign.
mark_node_unsafe(p_variable->initializer);
+ p_variable->use_conversion_assign = true;
}
}
} else if (p_variable->infer_datatype) {
@@ -1608,10 +1612,12 @@ void GDScriptAnalyzer::reduce_assignment(GDScriptParser::AssignmentNode *p_assig
} else {
// TODO: Add warning.
mark_node_unsafe(p_assignment);
+ p_assignment->use_conversion_assign = true;
}
} else {
// TODO: Warning in this case.
mark_node_unsafe(p_assignment);
+ p_assignment->use_conversion_assign = true;
}
}
} else {
@@ -1621,6 +1627,9 @@ void GDScriptAnalyzer::reduce_assignment(GDScriptParser::AssignmentNode *p_assig
if (assignee_type.has_no_type() || assigned_value_type.is_variant()) {
mark_node_unsafe(p_assignment);
+ if (assignee_type.is_hard_type()) {
+ p_assignment->use_conversion_assign = true;
+ }
}
if (p_assignment->assignee->type == GDScriptParser::Node::IDENTIFIER) {
@@ -2059,9 +2068,11 @@ void GDScriptAnalyzer::reduce_call(GDScriptParser::CallNode *p_call, bool is_awa
if (p_call->is_super) {
base_type = parser->current_class->base_type;
+ base_type.is_meta_type = false;
is_self = true;
} else if (callee_type == GDScriptParser::Node::IDENTIFIER) {
base_type = parser->current_class->get_datatype();
+ base_type.is_meta_type = false;
is_self = true;
} else if (callee_type == GDScriptParser::Node::SUBSCRIPT) {
GDScriptParser::SubscriptNode *subscript = static_cast<GDScriptParser::SubscriptNode *>(p_call->callee);
diff --git a/modules/gdscript/gdscript_byte_codegen.cpp b/modules/gdscript/gdscript_byte_codegen.cpp
index ea34a2ca2d..77a972ef12 100644
--- a/modules/gdscript/gdscript_byte_codegen.cpp
+++ b/modules/gdscript/gdscript_byte_codegen.cpp
@@ -779,63 +779,43 @@ void GDScriptByteCodeGenerator::write_get_member(const Address &p_target, const
append(p_name);
}
-void GDScriptByteCodeGenerator::write_assign(const Address &p_target, const Address &p_source) {
- if (p_target.type.has_type && !p_source.type.has_type) {
- // Typed assignment.
- switch (p_target.type.kind) {
- case GDScriptDataType::BUILTIN: {
- if (p_target.type.builtin_type == Variant::ARRAY && p_target.type.has_container_element_type()) {
- append(GDScriptFunction::OPCODE_ASSIGN_TYPED_ARRAY, 2);
- append(p_target);
- append(p_source);
- } else {
- append(GDScriptFunction::OPCODE_ASSIGN_TYPED_BUILTIN, 2);
- append(p_target);
- append(p_source);
- append(p_target.type.builtin_type);
- }
- } break;
- case GDScriptDataType::NATIVE: {
- int class_idx = GDScriptLanguage::get_singleton()->get_global_map()[p_target.type.native_type];
- Variant nc = GDScriptLanguage::get_singleton()->get_global_array()[class_idx];
- class_idx = get_constant_pos(nc) | (GDScriptFunction::ADDR_TYPE_CONSTANT << GDScriptFunction::ADDR_BITS);
- append(GDScriptFunction::OPCODE_ASSIGN_TYPED_NATIVE, 3);
- append(p_target);
- append(p_source);
- append(class_idx);
- } break;
- case GDScriptDataType::SCRIPT:
- case GDScriptDataType::GDSCRIPT: {
- Variant script = p_target.type.script_type;
- int idx = get_constant_pos(script) | (GDScriptFunction::ADDR_TYPE_CONSTANT << GDScriptFunction::ADDR_BITS);
-
- append(GDScriptFunction::OPCODE_ASSIGN_TYPED_SCRIPT, 3);
+void GDScriptByteCodeGenerator::write_assign_with_conversion(const Address &p_target, const Address &p_source) {
+ switch (p_target.type.kind) {
+ case GDScriptDataType::BUILTIN: {
+ if (p_target.type.builtin_type == Variant::ARRAY && p_target.type.has_container_element_type()) {
+ append(GDScriptFunction::OPCODE_ASSIGN_TYPED_ARRAY, 2);
append(p_target);
append(p_source);
- append(idx);
- } break;
- default: {
- ERR_PRINT("Compiler bug: unresolved assign.");
-
- // Shouldn't get here, but fail-safe to a regular assignment
- append(GDScriptFunction::OPCODE_ASSIGN, 2);
+ } else {
+ append(GDScriptFunction::OPCODE_ASSIGN_TYPED_BUILTIN, 2);
append(p_target);
append(p_source);
+ append(p_target.type.builtin_type);
}
- }
- } else {
- if (p_target.type.kind == GDScriptDataType::BUILTIN && p_target.type.builtin_type == Variant::ARRAY && p_target.type.has_container_element_type()) {
- append(GDScriptFunction::OPCODE_ASSIGN_TYPED_ARRAY, 2);
+ } break;
+ case GDScriptDataType::NATIVE: {
+ int class_idx = GDScriptLanguage::get_singleton()->get_global_map()[p_target.type.native_type];
+ Variant nc = GDScriptLanguage::get_singleton()->get_global_array()[class_idx];
+ class_idx = get_constant_pos(nc) | (GDScriptFunction::ADDR_TYPE_CONSTANT << GDScriptFunction::ADDR_BITS);
+ append(GDScriptFunction::OPCODE_ASSIGN_TYPED_NATIVE, 3);
append(p_target);
append(p_source);
- } else if (p_target.type.kind == GDScriptDataType::BUILTIN && p_source.type.kind == GDScriptDataType::BUILTIN && p_target.type.builtin_type != p_source.type.builtin_type) {
- // Need conversion..
- append(GDScriptFunction::OPCODE_ASSIGN_TYPED_BUILTIN, 2);
+ append(class_idx);
+ } break;
+ case GDScriptDataType::SCRIPT:
+ case GDScriptDataType::GDSCRIPT: {
+ Variant script = p_target.type.script_type;
+ int idx = get_constant_pos(script) | (GDScriptFunction::ADDR_TYPE_CONSTANT << GDScriptFunction::ADDR_BITS);
+
+ append(GDScriptFunction::OPCODE_ASSIGN_TYPED_SCRIPT, 3);
append(p_target);
append(p_source);
- append(p_target.type.builtin_type);
- } else {
- // Either untyped assignment or already type-checked by the parser
+ append(idx);
+ } break;
+ default: {
+ ERR_PRINT("Compiler bug: unresolved assign.");
+
+ // Shouldn't get here, but fail-safe to a regular assignment
append(GDScriptFunction::OPCODE_ASSIGN, 2);
append(p_target);
append(p_source);
@@ -843,6 +823,24 @@ void GDScriptByteCodeGenerator::write_assign(const Address &p_target, const Addr
}
}
+void GDScriptByteCodeGenerator::write_assign(const Address &p_target, const Address &p_source) {
+ if (p_target.type.kind == GDScriptDataType::BUILTIN && p_target.type.builtin_type == Variant::ARRAY && p_target.type.has_container_element_type()) {
+ append(GDScriptFunction::OPCODE_ASSIGN_TYPED_ARRAY, 2);
+ append(p_target);
+ append(p_source);
+ } else if (p_target.type.kind == GDScriptDataType::BUILTIN && p_source.type.kind == GDScriptDataType::BUILTIN && p_target.type.builtin_type != p_source.type.builtin_type) {
+ // Need conversion.
+ append(GDScriptFunction::OPCODE_ASSIGN_TYPED_BUILTIN, 2);
+ append(p_target);
+ append(p_source);
+ append(p_target.type.builtin_type);
+ } else {
+ append(GDScriptFunction::OPCODE_ASSIGN, 2);
+ append(p_target);
+ append(p_source);
+ }
+}
+
void GDScriptByteCodeGenerator::write_assign_true(const Address &p_target) {
append(GDScriptFunction::OPCODE_ASSIGN_TRUE, 1);
append(p_target);
diff --git a/modules/gdscript/gdscript_byte_codegen.h b/modules/gdscript/gdscript_byte_codegen.h
index f8c05fea83..b1f3cd5fb3 100644
--- a/modules/gdscript/gdscript_byte_codegen.h
+++ b/modules/gdscript/gdscript_byte_codegen.h
@@ -450,6 +450,7 @@ public:
virtual void write_set_member(const Address &p_value, const StringName &p_name) override;
virtual void write_get_member(const Address &p_target, const StringName &p_name) override;
virtual void write_assign(const Address &p_target, const Address &p_source) override;
+ virtual void write_assign_with_conversion(const Address &p_target, const Address &p_source) override;
virtual void write_assign_true(const Address &p_target) override;
virtual void write_assign_false(const Address &p_target) override;
virtual void write_assign_default_parameter(const Address &p_dst, const Address &p_src) override;
diff --git a/modules/gdscript/gdscript_cache.cpp b/modules/gdscript/gdscript_cache.cpp
index 6aa76703f1..601cdb4080 100644
--- a/modules/gdscript/gdscript_cache.cpp
+++ b/modules/gdscript/gdscript_cache.cpp
@@ -153,7 +153,7 @@ String GDScriptCache::get_source_code(const String &p_path) {
ERR_FAIL_COND_V(err, "");
}
- uint64_t len = f->get_len();
+ uint64_t len = f->get_length();
source_file.resize(len + 1);
uint64_t r = f->get_buffer(source_file.ptrw(), len);
f->close();
diff --git a/modules/gdscript/gdscript_codegen.h b/modules/gdscript/gdscript_codegen.h
index 399c9d6de7..cac6544f03 100644
--- a/modules/gdscript/gdscript_codegen.h
+++ b/modules/gdscript/gdscript_codegen.h
@@ -111,6 +111,7 @@ public:
virtual void write_set_member(const Address &p_value, const StringName &p_name) = 0;
virtual void write_get_member(const Address &p_target, const StringName &p_name) = 0;
virtual void write_assign(const Address &p_target, const Address &p_source) = 0;
+ virtual void write_assign_with_conversion(const Address &p_target, const Address &p_source) = 0;
virtual void write_assign_true(const Address &p_target) = 0;
virtual void write_assign_false(const Address &p_target) = 0;
virtual void write_assign_default_parameter(const Address &dst, const Address &src) = 0;
diff --git a/modules/gdscript/gdscript_compiler.cpp b/modules/gdscript/gdscript_compiler.cpp
index 4ac9864d4f..c7ca9449c2 100644
--- a/modules/gdscript/gdscript_compiler.cpp
+++ b/modules/gdscript/gdscript_compiler.cpp
@@ -1084,7 +1084,11 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
gen->write_call(GDScriptCodeGenerator::Address(), GDScriptCodeGenerator::Address(GDScriptCodeGenerator::Address::SELF), setter_function, args);
} else {
// Just assign.
- gen->write_assign(target, op_result);
+ if (assignment->use_conversion_assign) {
+ gen->write_assign_with_conversion(target, op_result);
+ } else {
+ gen->write_assign(target, op_result);
+ }
}
if (op_result.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
@@ -1792,7 +1796,11 @@ Error GDScriptCompiler::_parse_block(CodeGen &codegen, const GDScriptParser::Sui
if (error) {
return error;
}
- gen->write_assign(local, src_address);
+ if (lv->use_conversion_assign) {
+ gen->write_assign_with_conversion(local, src_address);
+ } else {
+ gen->write_assign(local, src_address);
+ }
if (src_address.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
codegen.generator->pop_temporary();
}
@@ -1930,7 +1938,11 @@ GDScriptFunction *GDScriptCompiler::_parse_function(Error &r_error, GDScript *p_
return nullptr;
}
- codegen.generator->write_assign(dst_address, src_address);
+ if (field->use_conversion_assign) {
+ codegen.generator->write_assign_with_conversion(dst_address, src_address);
+ } else {
+ codegen.generator->write_assign(dst_address, src_address);
+ }
if (src_address.mode == GDScriptCodeGenerator::Address::TEMPORARY) {
codegen.generator->pop_temporary();
}
@@ -2211,7 +2223,7 @@ Error GDScriptCompiler::_parse_class_level(GDScript *p_script, const GDScriptPar
if (err) {
return err;
}
- if (base.is_null() && !base->is_valid()) {
+ if (base.is_null() || !base->is_valid()) {
return ERR_COMPILATION_FAILED;
}
}
diff --git a/modules/gdscript/gdscript_parser.h b/modules/gdscript/gdscript_parser.h
index b1b29a7bd1..ee5e411cad 100644
--- a/modules/gdscript/gdscript_parser.h
+++ b/modules/gdscript/gdscript_parser.h
@@ -370,6 +370,7 @@ public:
Variant::Operator variant_op = Variant::OP_MAX;
ExpressionNode *assignee = nullptr;
ExpressionNode *assigned_value = nullptr;
+ bool use_conversion_assign = false;
AssignmentNode() {
type = ASSIGNMENT;
@@ -1119,6 +1120,7 @@ public:
MultiplayerAPI::RPCMode rpc_mode = MultiplayerAPI::RPC_MODE_DISABLED;
int assignments = 0;
int usages = 0;
+ bool use_conversion_assign = false;
#ifdef TOOLS_ENABLED
String doc_description;
#endif // TOOLS_ENABLED
diff --git a/modules/gdscript/tests/scripts/analyzer/features/call_self_get_name.gd b/modules/gdscript/tests/scripts/analyzer/features/call_self_get_name.gd
new file mode 100644
index 0000000000..d21d8bce96
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/call_self_get_name.gd
@@ -0,0 +1,9 @@
+extends Node
+
+func test():
+ set_name("TestNodeName")
+ if get_name() == &"TestNodeName":
+ print("Name is equal")
+ else:
+ print("Name is not equal")
+ print(get_name() is StringName)
diff --git a/modules/gdscript/tests/scripts/analyzer/features/call_self_get_name.out b/modules/gdscript/tests/scripts/analyzer/features/call_self_get_name.out
new file mode 100644
index 0000000000..dc4348d9c3
--- /dev/null
+++ b/modules/gdscript/tests/scripts/analyzer/features/call_self_get_name.out
@@ -0,0 +1,3 @@
+GDTEST_OK
+Name is equal
+True
diff --git a/modules/gdscript/tests/test_gdscript.cpp b/modules/gdscript/tests/test_gdscript.cpp
index 8ad5cdacad..fc73b5bb72 100644
--- a/modules/gdscript/tests/test_gdscript.cpp
+++ b/modules/gdscript/tests/test_gdscript.cpp
@@ -215,8 +215,8 @@ void test(TestType p_type) {
init_language(fa->get_path_absolute().get_base_dir());
Vector<uint8_t> buf;
- uint64_t flen = fa->get_len();
- buf.resize(fa->get_len() + 1);
+ uint64_t flen = fa->get_length();
+ buf.resize(flen + 1);
fa->get_buffer(buf.ptrw(), flen);
buf.write[flen] = 0;
diff --git a/modules/glslang/register_types.cpp b/modules/glslang/register_types.cpp
index 4331daadfc..8979eabfc3 100644
--- a/modules/glslang/register_types.cpp
+++ b/modules/glslang/register_types.cpp
@@ -179,11 +179,18 @@ static Vector<uint8_t> _compile_shader_glsl(RenderingDevice::ShaderStage p_stage
return ret;
}
+static String _get_cache_key_function_glsl(const RenderingDevice::Capabilities *p_capabilities) {
+ String version;
+ version = "SpirVGen=" + itos(glslang::GetSpirvGeneratorVersion()) + ", major=" + itos(p_capabilities->version_major) + ", minor=" + itos(p_capabilities->version_minor) + " , subgroup_size=" + itos(p_capabilities->subgroup_operations) + " , subgroup_ops=" + itos(p_capabilities->subgroup_operations) + " , subgroup_in_shaders=" + itos(p_capabilities->subgroup_in_shaders);
+ return version;
+}
+
void preregister_glslang_types() {
// initialize in case it's not initialized. This is done once per thread
// and it's safe to call multiple times
glslang::InitializeProcess();
RenderingDevice::shader_set_compile_function(_compile_shader_glsl);
+ RenderingDevice::shader_set_get_cache_key_function(_get_cache_key_function_glsl);
}
void register_glslang_types() {
diff --git a/modules/gltf/doc_classes/GLTFNode.xml b/modules/gltf/doc_classes/GLTFNode.xml
index 5b7d4fadec..af7be55e4b 100644
--- a/modules/gltf/doc_classes/GLTFNode.xml
+++ b/modules/gltf/doc_classes/GLTFNode.xml
@@ -13,8 +13,6 @@
</member>
<member name="children" type="PackedInt32Array" setter="set_children" getter="get_children" default="PackedInt32Array( )">
</member>
- <member name="fake_joint_parent" type="int" setter="set_fake_joint_parent" getter="get_fake_joint_parent" default="-1">
- </member>
<member name="height" type="int" setter="set_height" getter="get_height" default="-1">
</member>
<member name="joint" type="bool" setter="set_joint" getter="get_joint" default="false">
diff --git a/modules/gltf/gltf_document.cpp b/modules/gltf/gltf_document.cpp
index b3fa17bfb5..abac0a2e17 100644
--- a/modules/gltf/gltf_document.cpp
+++ b/modules/gltf/gltf_document.cpp
@@ -233,7 +233,7 @@ Error GLTFDocument::_parse_json(const String &p_path, Ref<GLTFState> state) {
}
Vector<uint8_t> array;
- array.resize(f->get_len());
+ array.resize(f->get_length());
f->get_buffer(array.ptrw(), array.size());
String text;
text.parse_utf8((const char *)array.ptr(), array.size());
@@ -2821,8 +2821,8 @@ Error GLTFDocument::_parse_meshes(Ref<GLTFState> state) {
}
blend_weights.write[j] = weights[j];
}
- mesh->set_blend_weights(blend_weights);
}
+ mesh->set_blend_weights(blend_weights);
mesh->set_mesh(import_mesh);
state->meshes.push_back(mesh);
@@ -4108,81 +4108,10 @@ Error GLTFDocument::_reparent_non_joint_skeleton_subtrees(Ref<GLTFState> state,
subtree_set.get_members(subtree_nodes, subtree_root);
for (int subtree_i = 0; subtree_i < subtree_nodes.size(); ++subtree_i) {
- ERR_FAIL_COND_V(_reparent_to_fake_joint(state, skeleton, subtree_nodes[subtree_i]), FAILED);
-
- // We modified the tree, recompute all the heights
- _compute_node_heights(state);
- }
- }
-
- return OK;
-}
-
-Error GLTFDocument::_reparent_to_fake_joint(Ref<GLTFState> state, Ref<GLTFSkeleton> skeleton, const GLTFNodeIndex node_index) {
- Ref<GLTFNode> node = state->nodes[node_index];
-
- // Can we just "steal" this joint if it is just a spatial node?
- if (node->skin < 0 && node->mesh < 0 && node->camera < 0) {
- node->joint = true;
- // Add the joint to the skeletons joints
- skeleton->joints.push_back(node_index);
- return OK;
- }
-
- GLTFNode *fake_joint = memnew(GLTFNode);
- const GLTFNodeIndex fake_joint_index = state->nodes.size();
- state->nodes.push_back(fake_joint);
-
- // We better not be a joint, or we messed up in our logic
- if (node->joint) {
- return FAILED;
- }
-
- fake_joint->translation = node->translation;
- fake_joint->rotation = node->rotation;
- fake_joint->scale = node->scale;
- fake_joint->xform = node->xform;
- fake_joint->joint = true;
-
- // We can use the exact same name here, because the joint will be inside a skeleton and not the scene
- fake_joint->set_name(node->get_name());
-
- // Clear the nodes transforms, since it will be parented to the fake joint
- node->translation = Vector3(0, 0, 0);
- node->rotation = Quat();
- node->scale = Vector3(1, 1, 1);
- node->xform = Transform();
-
- // Transfer the node children to the fake joint
- for (int child_i = 0; child_i < node->children.size(); ++child_i) {
- Ref<GLTFNode> child = state->nodes[node->children[child_i]];
- child->parent = fake_joint_index;
- }
-
- fake_joint->children = node->children;
- node->children.clear();
-
- // add the fake joint to the parent and remove the original joint
- if (node->parent >= 0) {
- Ref<GLTFNode> parent = state->nodes[node->parent];
- parent->children.erase(node_index);
- parent->children.push_back(fake_joint_index);
- fake_joint->parent = node->parent;
- }
-
- // Add the node to the fake joint
- fake_joint->children.push_back(node_index);
- node->parent = fake_joint_index;
- node->fake_joint_parent = fake_joint_index;
-
- // Add the fake joint to the skeletons joints
- skeleton->joints.push_back(fake_joint_index);
-
- // Replace skin_skeletons with fake joints if we must.
- for (GLTFSkinIndex skin_i = 0; skin_i < state->skins.size(); ++skin_i) {
- Ref<GLTFSkin> skin = state->skins.write[skin_i];
- if (skin->skin_root == node_index) {
- skin->skin_root = fake_joint_index;
+ Ref<GLTFNode> node = state->nodes[subtree_nodes[subtree_i]];
+ node->joint = true;
+ // Add the joint to the skeletons joints
+ skeleton->joints.push_back(subtree_nodes[subtree_i]);
}
}
@@ -4920,10 +4849,9 @@ void GLTFDocument::_assign_scene_names(Ref<GLTFState> state) {
}
}
-BoneAttachment3D *GLTFDocument::_generate_bone_attachment(Ref<GLTFState> state, Skeleton3D *skeleton, const GLTFNodeIndex node_index) {
+BoneAttachment3D *GLTFDocument::_generate_bone_attachment(Ref<GLTFState> state, Skeleton3D *skeleton, const GLTFNodeIndex node_index, const GLTFNodeIndex bone_index) {
Ref<GLTFNode> gltf_node = state->nodes[node_index];
- Ref<GLTFNode> bone_node = state->nodes[gltf_node->parent];
-
+ Ref<GLTFNode> bone_node = state->nodes[bone_index];
BoneAttachment3D *bone_attachment = memnew(BoneAttachment3D);
print_verbose("glTF: Creating bone attachment for: " + gltf_node->get_name());
@@ -5008,7 +4936,7 @@ EditorSceneImporterMeshNode3D *GLTFDocument::_generate_mesh_instance(Ref<GLTFSta
return mi;
}
-Light3D *GLTFDocument::_generate_light(Ref<GLTFState> state, Node *scene_parent, const GLTFNodeIndex node_index) {
+Node3D *GLTFDocument::_generate_light(Ref<GLTFState> state, Node *scene_parent, const GLTFNodeIndex node_index) {
Ref<GLTFNode> gltf_node = state->nodes[node_index];
ERR_FAIL_INDEX_V(gltf_node->light, state->lights.size(), nullptr);
@@ -5057,7 +4985,7 @@ Light3D *GLTFDocument::_generate_light(Ref<GLTFState> state, Node *scene_parent,
light->set_param(SpotLight3D::PARAM_SPOT_ATTENUATION, angle_attenuation);
return light;
}
- return nullptr;
+ return memnew(Node3D);
}
Camera3D *GLTFDocument::_generate_camera(Ref<GLTFState> state, Node *scene_parent, const GLTFNodeIndex node_index) {
@@ -5429,31 +5357,22 @@ void GLTFDocument::_convert_mesh_to_gltf(Node *p_scene_parent, Ref<GLTFState> st
void GLTFDocument::_generate_scene_node(Ref<GLTFState> state, Node *scene_parent, Node3D *scene_root, const GLTFNodeIndex node_index) {
Ref<GLTFNode> gltf_node = state->nodes[node_index];
+ if (gltf_node->skeleton >= 0) {
+ _generate_skeleton_bone_node(state, scene_parent, scene_root, node_index);
+ return;
+ }
+
Node3D *current_node = nullptr;
// Is our parent a skeleton
Skeleton3D *active_skeleton = Object::cast_to<Skeleton3D>(scene_parent);
- if (gltf_node->skeleton >= 0) {
- Skeleton3D *skeleton = state->skeletons[gltf_node->skeleton]->godot_skeleton;
-
- if (active_skeleton != skeleton) {
- ERR_FAIL_COND_MSG(active_skeleton != nullptr, "glTF: Generating scene detected direct parented Skeletons");
-
- // Add it to the scene if it has not already been added
- if (skeleton->get_parent() == nullptr) {
- scene_parent->add_child(skeleton);
- skeleton->set_owner(scene_root);
- }
- }
-
- active_skeleton = skeleton;
- current_node = skeleton;
- }
+ const bool non_bone_parented_to_skeleton = active_skeleton;
- // If we have an active skeleton, and the node is node skinned, we need to create a bone attachment
- if (current_node == nullptr && active_skeleton != nullptr && gltf_node->skin < 0) {
- BoneAttachment3D *bone_attachment = _generate_bone_attachment(state, active_skeleton, node_index);
+ // skinned meshes must not be placed in a bone attachment.
+ if (non_bone_parented_to_skeleton && gltf_node->skin < 0) {
+ // Bone Attachment - Parent Case
+ BoneAttachment3D *bone_attachment = _generate_bone_attachment(state, active_skeleton, node_index, gltf_node->parent);
scene_parent->add_child(bone_attachment);
bone_attachment->set_owner(scene_root);
@@ -5467,7 +5386,86 @@ void GLTFDocument::_generate_scene_node(Ref<GLTFState> state, Node *scene_parent
}
// We still have not managed to make a node
- if (current_node == nullptr) {
+ if (gltf_node->mesh >= 0) {
+ current_node = _generate_mesh_instance(state, scene_parent, node_index);
+ } else if (gltf_node->camera >= 0) {
+ current_node = _generate_camera(state, scene_parent, node_index);
+ } else if (gltf_node->light >= 0) {
+ current_node = _generate_light(state, scene_parent, node_index);
+ } else {
+ current_node = _generate_spatial(state, scene_parent, node_index);
+ }
+
+ scene_parent->add_child(current_node);
+ if (current_node != scene_root) {
+ current_node->set_owner(scene_root);
+ }
+ current_node->set_transform(gltf_node->xform);
+ current_node->set_name(gltf_node->get_name());
+
+ state->scene_nodes.insert(node_index, current_node);
+
+ for (int i = 0; i < gltf_node->children.size(); ++i) {
+ _generate_scene_node(state, current_node, scene_root, gltf_node->children[i]);
+ }
+}
+
+void GLTFDocument::_generate_skeleton_bone_node(Ref<GLTFState> state, Node *scene_parent, Node3D *scene_root, const GLTFNodeIndex node_index) {
+ Ref<GLTFNode> gltf_node = state->nodes[node_index];
+
+ Node3D *current_node = nullptr;
+
+ Skeleton3D *skeleton = state->skeletons[gltf_node->skeleton]->godot_skeleton;
+ // In this case, this node is already a bone in skeleton.
+ const bool is_skinned_mesh = (gltf_node->skin >= 0 && gltf_node->mesh >= 0);
+ const bool requires_extra_node = (gltf_node->mesh >= 0 || gltf_node->camera >= 0 || gltf_node->light >= 0);
+
+ Skeleton3D *active_skeleton = Object::cast_to<Skeleton3D>(scene_parent);
+ if (active_skeleton != skeleton) {
+ if (active_skeleton) {
+ // Bone Attachment - Direct Parented Skeleton Case
+ BoneAttachment3D *bone_attachment = _generate_bone_attachment(state, active_skeleton, node_index, gltf_node->parent);
+
+ scene_parent->add_child(bone_attachment);
+ bone_attachment->set_owner(scene_root);
+
+ // There is no gltf_node that represent this, so just directly create a unique name
+ bone_attachment->set_name(_gen_unique_name(state, "BoneAttachment3D"));
+
+ // We change the scene_parent to our bone attachment now. We do not set current_node because we want to make the node
+ // and attach it to the bone_attachment
+ scene_parent = bone_attachment;
+ WARN_PRINT(vformat("glTF: Generating scene detected direct parented Skeletons at node %d", node_index));
+ }
+
+ // Add it to the scene if it has not already been added
+ if (skeleton->get_parent() == nullptr) {
+ scene_parent->add_child(skeleton);
+ skeleton->set_owner(scene_root);
+ }
+ }
+
+ active_skeleton = skeleton;
+ current_node = skeleton;
+
+ if (requires_extra_node) {
+ // skinned meshes must not be placed in a bone attachment.
+ if (!is_skinned_mesh) {
+ // Bone Attachment - Same Node Case
+ BoneAttachment3D *bone_attachment = _generate_bone_attachment(state, active_skeleton, node_index, node_index);
+
+ scene_parent->add_child(bone_attachment);
+ bone_attachment->set_owner(scene_root);
+
+ // There is no gltf_node that represent this, so just directly create a unique name
+ bone_attachment->set_name(_gen_unique_name(state, "BoneAttachment3D"));
+
+ // We change the scene_parent to our bone attachment now. We do not set current_node because we want to make the node
+ // and attach it to the bone_attachment
+ scene_parent = bone_attachment;
+ }
+
+ // We still have not managed to make a node
if (gltf_node->mesh >= 0) {
current_node = _generate_mesh_instance(state, scene_parent, node_index);
} else if (gltf_node->camera >= 0) {
@@ -5476,22 +5474,18 @@ void GLTFDocument::_generate_scene_node(Ref<GLTFState> state, Node *scene_parent
current_node = _generate_light(state, scene_parent, node_index);
}
- if (!current_node) {
- current_node = _generate_spatial(state, scene_parent, node_index);
- }
-
scene_parent->add_child(current_node);
if (current_node != scene_root) {
current_node->set_owner(scene_root);
}
- current_node->set_transform(gltf_node->xform);
+ // Do not set transform here. Transform is already applied to our bone.
current_node->set_name(gltf_node->get_name());
}
state->scene_nodes.insert(node_index, current_node);
for (int i = 0; i < gltf_node->children.size(); ++i) {
- _generate_scene_node(state, current_node, scene_root, gltf_node->children[i]);
+ _generate_scene_node(state, active_skeleton, scene_root, gltf_node->children[i]);
}
}
@@ -5632,28 +5626,30 @@ void GLTFDocument::_import_animation(Ref<GLTFState> state, AnimationPlayer *ap,
for (Map<int, GLTFAnimation::Track>::Element *track_i = anim->get_tracks().front(); track_i; track_i = track_i->next()) {
const GLTFAnimation::Track &track = track_i->get();
- //need to find the path
+ //need to find the path: for skeletons, weight tracks will affect the mesh
NodePath node_path;
+ //for skeletons, transform tracks always affect bones
+ NodePath transform_node_path;
GLTFNodeIndex node_index = track_i->key();
- if (state->nodes[node_index]->fake_joint_parent >= 0) {
- // Should be same as parent
- node_index = state->nodes[node_index]->fake_joint_parent;
- }
const Ref<GLTFNode> gltf_node = state->nodes[track_i->key()];
+ Node *root = ap->get_parent();
+ ERR_FAIL_COND(root == nullptr);
+ Map<GLTFNodeIndex, Node *>::Element *node_element = state->scene_nodes.find(node_index);
+ ERR_CONTINUE_MSG(node_element == nullptr, vformat("Unable to find node %d for animation", node_index));
+ node_path = root->get_path_to(node_element->get());
+
if (gltf_node->skeleton >= 0) {
- const Skeleton3D *sk = Object::cast_to<Skeleton3D>(state->scene_nodes.find(node_index)->get());
+ const Skeleton3D *sk = state->skeletons[gltf_node->skeleton]->godot_skeleton;
ERR_FAIL_COND(sk == nullptr);
const String path = ap->get_parent()->get_path_to(sk);
const String bone = gltf_node->get_name();
- node_path = path + ":" + bone;
+ transform_node_path = path + ":" + bone;
} else {
- Node *root = ap->get_parent();
- Node *godot_node = state->scene_nodes.find(node_index)->get();
- node_path = root->get_path_to(godot_node);
+ transform_node_path = node_path;
}
for (int i = 0; i < track.rotation_track.times.size(); i++) {
@@ -5672,11 +5668,13 @@ void GLTFDocument::_import_animation(Ref<GLTFState> state, AnimationPlayer *ap,
}
}
- if (track.rotation_track.values.size() || track.translation_track.values.size() || track.scale_track.values.size()) {
+ // Animated TRS properties will not affect a skinned mesh.
+ const bool transform_affects_skinned_mesh_instance = gltf_node->skeleton < 0 && gltf_node->skin >= 0;
+ if ((track.rotation_track.values.size() || track.translation_track.values.size() || track.scale_track.values.size()) && !transform_affects_skinned_mesh_instance) {
//make transform track
int track_idx = animation->get_track_count();
animation->add_track(Animation::TYPE_TRANSFORM);
- animation->track_set_path(track_idx, node_path);
+ animation->track_set_path(track_idx, transform_node_path);
//first determine animation length
const double increment = 1.0 / bake_fps;
diff --git a/modules/gltf/gltf_document.h b/modules/gltf/gltf_document.h
index bda1ce87d6..900c367010 100644
--- a/modules/gltf/gltf_document.h
+++ b/modules/gltf/gltf_document.h
@@ -260,11 +260,12 @@ private:
Error _serialize_animations(Ref<GLTFState> state);
BoneAttachment3D *_generate_bone_attachment(Ref<GLTFState> state,
Skeleton3D *skeleton,
- const GLTFNodeIndex node_index);
+ const GLTFNodeIndex node_index,
+ const GLTFNodeIndex bone_index);
EditorSceneImporterMeshNode3D *_generate_mesh_instance(Ref<GLTFState> state, Node *scene_parent, const GLTFNodeIndex node_index);
Camera3D *_generate_camera(Ref<GLTFState> state, Node *scene_parent,
const GLTFNodeIndex node_index);
- Light3D *_generate_light(Ref<GLTFState> state, Node *scene_parent, const GLTFNodeIndex node_index);
+ Node3D *_generate_light(Ref<GLTFState> state, Node *scene_parent, const GLTFNodeIndex node_index);
Node3D *_generate_spatial(Ref<GLTFState> state, Node *scene_parent,
const GLTFNodeIndex node_index);
void _assign_scene_names(Ref<GLTFState> state);
@@ -365,6 +366,7 @@ public:
void _generate_scene_node(Ref<GLTFState> state, Node *scene_parent,
Node3D *scene_root,
const GLTFNodeIndex node_index);
+ void _generate_skeleton_bone_node(Ref<GLTFState> state, Node *scene_parent, Node3D *scene_root, const GLTFNodeIndex node_index);
void _import_animation(Ref<GLTFState> state, AnimationPlayer *ap,
const GLTFAnimationIndex index, const int bake_fps);
GLTFMeshIndex _convert_mesh_instance(Ref<GLTFState> state,
diff --git a/modules/gltf/gltf_node.cpp b/modules/gltf/gltf_node.cpp
index 777c6fbd9a..f6f33ef009 100644
--- a/modules/gltf/gltf_node.cpp
+++ b/modules/gltf/gltf_node.cpp
@@ -55,8 +55,6 @@ void GLTFNode::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_scale", "scale"), &GLTFNode::set_scale);
ClassDB::bind_method(D_METHOD("get_children"), &GLTFNode::get_children);
ClassDB::bind_method(D_METHOD("set_children", "children"), &GLTFNode::set_children);
- ClassDB::bind_method(D_METHOD("get_fake_joint_parent"), &GLTFNode::get_fake_joint_parent);
- ClassDB::bind_method(D_METHOD("set_fake_joint_parent", "fake_joint_parent"), &GLTFNode::set_fake_joint_parent);
ClassDB::bind_method(D_METHOD("get_light"), &GLTFNode::get_light);
ClassDB::bind_method(D_METHOD("set_light", "light"), &GLTFNode::set_light);
@@ -72,7 +70,6 @@ void GLTFNode::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::QUAT, "rotation"), "set_rotation", "get_rotation"); // Quat
ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "scale"), "set_scale", "get_scale"); // Vector3
ADD_PROPERTY(PropertyInfo(Variant::PACKED_INT32_ARRAY, "children"), "set_children", "get_children"); // Vector<int>
- ADD_PROPERTY(PropertyInfo(Variant::INT, "fake_joint_parent"), "set_fake_joint_parent", "get_fake_joint_parent"); // GLTFNodeIndex
ADD_PROPERTY(PropertyInfo(Variant::INT, "light"), "set_light", "get_light"); // GLTFLightIndex
}
@@ -172,14 +169,6 @@ void GLTFNode::set_children(Vector<int> p_children) {
children = p_children;
}
-GLTFNodeIndex GLTFNode::get_fake_joint_parent() {
- return fake_joint_parent;
-}
-
-void GLTFNode::set_fake_joint_parent(GLTFNodeIndex p_fake_joint_parent) {
- fake_joint_parent = p_fake_joint_parent;
-}
-
GLTFLightIndex GLTFNode::get_light() {
return light;
}
diff --git a/modules/gltf/gltf_node.h b/modules/gltf/gltf_node.h
index ce8aff8944..3a5689d004 100644
--- a/modules/gltf/gltf_node.h
+++ b/modules/gltf/gltf_node.h
@@ -53,7 +53,6 @@ private:
Quat rotation;
Vector3 scale = Vector3(1, 1, 1);
Vector<int> children;
- GLTFNodeIndex fake_joint_parent = -1;
GLTFLightIndex light = -1;
protected:
@@ -96,9 +95,6 @@ public:
Vector<int> get_children();
void set_children(Vector<int> p_children);
- GLTFNodeIndex get_fake_joint_parent();
- void set_fake_joint_parent(GLTFNodeIndex p_fake_joint_parent);
-
GLTFLightIndex get_light();
void set_light(GLTFLightIndex p_light);
};
diff --git a/modules/jpg/image_loader_jpegd.cpp b/modules/jpg/image_loader_jpegd.cpp
index 18beff41d8..d237544d66 100644
--- a/modules/jpg/image_loader_jpegd.cpp
+++ b/modules/jpg/image_loader_jpegd.cpp
@@ -105,7 +105,7 @@ Error jpeg_load_image_from_buffer(Image *p_image, const uint8_t *p_buffer, int p
Error ImageLoaderJPG::load_image(Ref<Image> p_image, FileAccess *f, bool p_force_linear, float p_scale) {
Vector<uint8_t> src_image;
- uint64_t src_image_len = f->get_len();
+ uint64_t src_image_len = f->get_length();
ERR_FAIL_COND_V(src_image_len == 0, ERR_FILE_CORRUPT);
src_image.resize(src_image_len);
diff --git a/modules/mbedtls/crypto_mbedtls.cpp b/modules/mbedtls/crypto_mbedtls.cpp
index 4a42e9af0d..c389609984 100644
--- a/modules/mbedtls/crypto_mbedtls.cpp
+++ b/modules/mbedtls/crypto_mbedtls.cpp
@@ -58,7 +58,7 @@ Error CryptoKeyMbedTLS::load(String p_path, bool p_public_only) {
FileAccess *f = FileAccess::open(p_path, FileAccess::READ);
ERR_FAIL_COND_V_MSG(!f, ERR_INVALID_PARAMETER, "Cannot open CryptoKeyMbedTLS file '" + p_path + "'.");
- uint64_t flen = f->get_len();
+ uint64_t flen = f->get_length();
out.resize(flen + 1);
f->get_buffer(out.ptrw(), flen);
out.write[flen] = 0; // string terminator
@@ -146,7 +146,7 @@ Error X509CertificateMbedTLS::load(String p_path) {
FileAccess *f = FileAccess::open(p_path, FileAccess::READ);
ERR_FAIL_COND_V_MSG(!f, ERR_INVALID_PARAMETER, "Cannot open X509CertificateMbedTLS file '" + p_path + "'.");
- uint64_t flen = f->get_len();
+ uint64_t flen = f->get_length();
out.resize(flen + 1);
f->get_buffer(out.ptrw(), flen);
out.write[flen] = 0; // string terminator
diff --git a/modules/minimp3/resource_importer_mp3.cpp b/modules/minimp3/resource_importer_mp3.cpp
index 96cce49c86..f5137965da 100644
--- a/modules/minimp3/resource_importer_mp3.cpp
+++ b/modules/minimp3/resource_importer_mp3.cpp
@@ -79,7 +79,7 @@ Error ResourceImporterMP3::import(const String &p_source_file, const String &p_s
ERR_FAIL_COND_V(!f, ERR_CANT_OPEN);
- uint64_t len = f->get_len();
+ uint64_t len = f->get_length();
Vector<uint8_t> data;
data.resize(len);
diff --git a/modules/mono/godotsharp_dirs.cpp b/modules/mono/godotsharp_dirs.cpp
index 020a40575c..68134b9b20 100644
--- a/modules/mono/godotsharp_dirs.cpp
+++ b/modules/mono/godotsharp_dirs.cpp
@@ -63,8 +63,8 @@ String _get_expected_build_config() {
String _get_mono_user_dir() {
#ifdef TOOLS_ENABLED
- if (EditorSettings::get_singleton()) {
- return EditorSettings::get_singleton()->get_data_dir().plus_file("mono");
+ if (EditorPaths::get_singleton()) {
+ return EditorPaths::get_singleton()->get_data_dir().plus_file("mono");
} else {
String settings_path;
diff --git a/modules/mono/utils/string_utils.cpp b/modules/mono/utils/string_utils.cpp
index 5d53394658..ee68458268 100644
--- a/modules/mono/utils/string_utils.cpp
+++ b/modules/mono/utils/string_utils.cpp
@@ -170,7 +170,7 @@ Error read_all_file_utf8(const String &p_path, String &r_content) {
FileAccess *f = FileAccess::open(p_path, FileAccess::READ, &err);
ERR_FAIL_COND_V_MSG(err != OK, err, "Cannot open file '" + p_path + "'.");
- uint64_t len = f->get_len();
+ uint64_t len = f->get_length();
sourcef.resize(len + 1);
uint8_t *w = sourcef.ptrw();
uint64_t r = f->get_buffer(w, len);
diff --git a/modules/stb_vorbis/resource_importer_ogg_vorbis.cpp b/modules/stb_vorbis/resource_importer_ogg_vorbis.cpp
index 17380001f5..e3aa630cef 100644
--- a/modules/stb_vorbis/resource_importer_ogg_vorbis.cpp
+++ b/modules/stb_vorbis/resource_importer_ogg_vorbis.cpp
@@ -79,7 +79,7 @@ Error ResourceImporterOGGVorbis::import(const String &p_source_file, const Strin
ERR_FAIL_COND_V_MSG(!f, ERR_CANT_OPEN, "Cannot open file '" + p_source_file + "'.");
- uint64_t len = f->get_len();
+ uint64_t len = f->get_length();
Vector<uint8_t> data;
data.resize(len);
diff --git a/modules/svg/image_loader_svg.cpp b/modules/svg/image_loader_svg.cpp
index 24fe654946..4911346b96 100644
--- a/modules/svg/image_loader_svg.cpp
+++ b/modules/svg/image_loader_svg.cpp
@@ -140,7 +140,7 @@ Error ImageLoaderSVG::create_image_from_string(Ref<Image> p_image, const char *p
}
Error ImageLoaderSVG::load_image(Ref<Image> p_image, FileAccess *f, bool p_force_linear, float p_scale) {
- uint64_t size = f->get_len();
+ uint64_t size = f->get_length();
Vector<uint8_t> src_image;
src_image.resize(size + 1);
uint8_t *src_w = src_image.ptrw();
diff --git a/modules/text_server_adv/dynamic_font_adv.cpp b/modules/text_server_adv/dynamic_font_adv.cpp
index faeb83f920..c29aac05bb 100644
--- a/modules/text_server_adv/dynamic_font_adv.cpp
+++ b/modules/text_server_adv/dynamic_font_adv.cpp
@@ -66,7 +66,7 @@ DynamicFontDataAdvanced::DataAtSize *DynamicFontDataAdvanced::get_data_for_size(
ERR_FAIL_V_MSG(nullptr, "Cannot open font file '" + font_path + "'.");
}
- uint64_t len = f->get_len();
+ uint64_t len = f->get_length();
font_mem_cache.resize(len);
f->get_buffer(font_mem_cache.ptrw(), len);
font_mem = font_mem_cache.ptr();
diff --git a/modules/text_server_adv/text_server_adv.cpp b/modules/text_server_adv/text_server_adv.cpp
index 361852547f..7eff3f8dee 100644
--- a/modules/text_server_adv/text_server_adv.cpp
+++ b/modules/text_server_adv/text_server_adv.cpp
@@ -181,7 +181,7 @@ bool TextServerAdvanced::load_support_data(const String &p_filename) {
UErrorCode err = U_ZERO_ERROR;
// ICU data found.
- uint64_t len = f->get_len();
+ uint64_t len = f->get_length();
icu_data = (uint8_t *)memalloc(len);
f->get_buffer(icu_data, len);
f->close();
diff --git a/modules/text_server_fb/dynamic_font_fb.cpp b/modules/text_server_fb/dynamic_font_fb.cpp
index 406f60b6f5..a261ba8f37 100644
--- a/modules/text_server_fb/dynamic_font_fb.cpp
+++ b/modules/text_server_fb/dynamic_font_fb.cpp
@@ -65,7 +65,7 @@ DynamicFontDataFallback::DataAtSize *DynamicFontDataFallback::get_data_for_size(
ERR_FAIL_V_MSG(nullptr, "Cannot open font file '" + font_path + "'.");
}
- uint64_t len = f->get_len();
+ uint64_t len = f->get_length();
font_mem_cache.resize(len);
f->get_buffer(font_mem_cache.ptrw(), len);
font_mem = font_mem_cache.ptr();
diff --git a/modules/tga/image_loader_tga.cpp b/modules/tga/image_loader_tga.cpp
index 52b92ee930..3cfd4ff36a 100644
--- a/modules/tga/image_loader_tga.cpp
+++ b/modules/tga/image_loader_tga.cpp
@@ -226,7 +226,7 @@ Error ImageLoaderTGA::convert_to_image(Ref<Image> p_image, const uint8_t *p_buff
Error ImageLoaderTGA::load_image(Ref<Image> p_image, FileAccess *f, bool p_force_linear, float p_scale) {
Vector<uint8_t> src_image;
- uint64_t src_image_len = f->get_len();
+ uint64_t src_image_len = f->get_length();
ERR_FAIL_COND_V(src_image_len == 0, ERR_FILE_CORRUPT);
ERR_FAIL_COND_V(src_image_len < (int64_t)sizeof(tga_header_s), ERR_FILE_CORRUPT);
src_image.resize(src_image_len);
diff --git a/modules/tinyexr/image_loader_tinyexr.cpp b/modules/tinyexr/image_loader_tinyexr.cpp
index cfb75fb93b..eb7a8597e6 100644
--- a/modules/tinyexr/image_loader_tinyexr.cpp
+++ b/modules/tinyexr/image_loader_tinyexr.cpp
@@ -37,7 +37,7 @@
Error ImageLoaderTinyEXR::load_image(Ref<Image> p_image, FileAccess *f, bool p_force_linear, float p_scale) {
Vector<uint8_t> src_image;
- uint64_t src_image_len = f->get_len();
+ uint64_t src_image_len = f->get_length();
ERR_FAIL_COND_V(src_image_len == 0, ERR_FILE_CORRUPT);
src_image.resize(src_image_len);
diff --git a/modules/visual_script/visual_script_builtin_funcs.cpp b/modules/visual_script/visual_script_builtin_funcs.cpp
index 3b24de433c..df34ec22ce 100644
--- a/modules/visual_script/visual_script_builtin_funcs.cpp
+++ b/modules/visual_script/visual_script_builtin_funcs.cpp
@@ -132,6 +132,7 @@ bool VisualScriptBuiltinFunc::has_input_sequence_port() const {
case TEXT_PRINT:
case TEXT_PRINTERR:
case TEXT_PRINTRAW:
+ case MATH_SEED:
return true;
default:
return false;
diff --git a/modules/webm/video_stream_webm.cpp b/modules/webm/video_stream_webm.cpp
index 15bf9dbf2d..34addb5c9f 100644
--- a/modules/webm/video_stream_webm.cpp
+++ b/modules/webm/video_stream_webm.cpp
@@ -74,7 +74,7 @@ public:
virtual int Length(long long *total, long long *available) {
if (file) {
- const uint64_t len = file->get_len();
+ const uint64_t len = file->get_length();
if (total) {
*total = len;
}
diff --git a/modules/webp/image_loader_webp.cpp b/modules/webp/image_loader_webp.cpp
index d7e88d76b1..1f2a456619 100644
--- a/modules/webp/image_loader_webp.cpp
+++ b/modules/webp/image_loader_webp.cpp
@@ -147,7 +147,7 @@ static Ref<Image> _webp_mem_loader_func(const uint8_t *p_png, int p_size) {
Error ImageLoaderWEBP::load_image(Ref<Image> p_image, FileAccess *f, bool p_force_linear, float p_scale) {
Vector<uint8_t> src_image;
- uint64_t src_image_len = f->get_len();
+ uint64_t src_image_len = f->get_length();
ERR_FAIL_COND_V(src_image_len == 0, ERR_FILE_CORRUPT);
src_image.resize(src_image_len);
diff --git a/platform/android/display_server_android.cpp b/platform/android/display_server_android.cpp
index f46dcf58dc..ff61eeaee1 100644
--- a/platform/android/display_server_android.cpp
+++ b/platform/android/display_server_android.cpp
@@ -196,7 +196,7 @@ void DisplayServerAndroid::window_set_input_text_callback(const Callable &p_call
}
void DisplayServerAndroid::window_set_rect_changed_callback(const Callable &p_callable, DisplayServer::WindowID p_window) {
- // Not supported on Android.
+ rect_changed_callback = p_callable;
}
void DisplayServerAndroid::window_set_drop_files_callback(const Callable &p_callable, DisplayServer::WindowID p_window) {
@@ -389,6 +389,19 @@ void DisplayServerAndroid::reset_window() {
#endif
}
+void DisplayServerAndroid::notify_surface_changed(int p_width, int p_height) {
+ if (rect_changed_callback.is_null()) {
+ return;
+ }
+
+ const Variant size = Rect2i(0, 0, p_width, p_height);
+ const Variant *sizep = &size;
+ Variant ret;
+ Callable::CallError ce;
+
+ rect_changed_callback.call(reinterpret_cast<const Variant **>(&sizep), 1, ret, ce);
+}
+
DisplayServerAndroid::DisplayServerAndroid(const String &p_rendering_driver, DisplayServer::WindowMode p_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error) {
rendering_driver = p_rendering_driver;
diff --git a/platform/android/display_server_android.h b/platform/android/display_server_android.h
index 53c768f406..1379baf154 100644
--- a/platform/android/display_server_android.h
+++ b/platform/android/display_server_android.h
@@ -112,6 +112,7 @@ private:
Callable window_event_callback;
Callable input_event_callback;
Callable input_text_callback;
+ Callable rect_changed_callback;
void _window_callback(const Callable &p_callable, const Variant &p_arg) const;
@@ -215,6 +216,7 @@ public:
static void register_android_driver();
void reset_window();
+ void notify_surface_changed(int p_width, int p_height);
virtual Point2i mouse_get_position() const;
virtual int mouse_get_button_state() const;
diff --git a/platform/android/export/export.cpp b/platform/android/export/export.cpp
index c94147640d..c45828e194 100644
--- a/platform/android/export/export.cpp
+++ b/platform/android/export/export.cpp
@@ -857,6 +857,8 @@ class EditorExportPlatformAndroid : public EditorExportPlatform {
int xr_mode_index = p_preset->get("xr_features/xr_mode");
+ bool backup_allowed = p_preset->get("user_data_backup/allow");
+
Vector<String> perms;
// Write permissions into the perms variable.
_get_permissions(p_preset, p_give_internet, perms);
@@ -949,6 +951,10 @@ class EditorExportPlatformAndroid : public EditorExportPlatform {
}
}
+ if (tname == "application" && attrname == "allowBackup") {
+ encode_uint32(backup_allowed, &p_manifest.write[iofs + 16]);
+ }
+
if (tname == "instrumentation" && attrname == "targetPackage") {
string_table.write[attr_value] = get_package_name(package_name);
}
@@ -1684,6 +1690,8 @@ public:
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "screen/support_large"), true));
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "screen/support_xlarge"), true));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "user_data_backup/allow"), false));
+
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "command_line/extra_args"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "apk_expansion/enable"), false));
@@ -1785,7 +1793,7 @@ public:
p_debug_flags |= DEBUG_FLAG_REMOTE_DEBUG_LOCALHOST;
}
- String tmp_export_path = EditorSettings::get_singleton()->get_cache_dir().plus_file("tmpexport." + uitos(OS::get_singleton()->get_unix_time()) + ".apk");
+ String tmp_export_path = EditorPaths::get_singleton()->get_cache_dir().plus_file("tmpexport." + uitos(OS::get_singleton()->get_unix_time()) + ".apk");
#define CLEANUP_AND_RETURN(m_err) \
{ \
@@ -2657,7 +2665,7 @@ public:
FileAccess *dst_f = nullptr;
io2.opaque = &dst_f;
- String tmp_unaligned_path = EditorSettings::get_singleton()->get_cache_dir().plus_file("tmpexport-unaligned." + uitos(OS::get_singleton()->get_unix_time()) + ".apk");
+ String tmp_unaligned_path = EditorPaths::get_singleton()->get_cache_dir().plus_file("tmpexport-unaligned." + uitos(OS::get_singleton()->get_unix_time()) + ".apk");
#define CLEANUP_AND_RETURN(m_err) \
{ \
diff --git a/platform/android/export/gradle_export_util.h b/platform/android/export/gradle_export_util.h
index 48e7fd46ac..0bb94dcc97 100644
--- a/platform/android/export/gradle_export_util.h
+++ b/platform/android/export/gradle_export_util.h
@@ -258,10 +258,11 @@ String _get_activity_tag(const Ref<EditorExportPreset> &p_preset) {
}
String _get_application_tag(const Ref<EditorExportPreset> &p_preset) {
- String manifest_application_text =
+ String manifest_application_text = vformat(
" <application android:label=\"@string/godot_project_name_string\"\n"
- " android:allowBackup=\"false\" tools:ignore=\"GoogleAppIndexingWarning\"\n"
- " android:icon=\"@mipmap/icon\">\n\n";
+ " android:allowBackup=\"%s\" tools:ignore=\"GoogleAppIndexingWarning\"\n"
+ " android:icon=\"@mipmap/icon\">\n\n",
+ bool_to_string(p_preset->get("user_data_backup/allow")));
manifest_application_text += _get_activity_tag(p_preset);
manifest_application_text += " </application>\n";
diff --git a/platform/android/file_access_android.cpp b/platform/android/file_access_android.cpp
index 900d4d9b20..90370878b7 100644
--- a/platform/android/file_access_android.cpp
+++ b/platform/android/file_access_android.cpp
@@ -94,7 +94,7 @@ uint64_t FileAccessAndroid::get_position() const {
return pos;
}
-uint64_t FileAccessAndroid::get_len() const {
+uint64_t FileAccessAndroid::get_length() const {
return len;
}
diff --git a/platform/android/file_access_android.h b/platform/android/file_access_android.h
index 9b0f85089d..8890e0f645 100644
--- a/platform/android/file_access_android.h
+++ b/platform/android/file_access_android.h
@@ -54,7 +54,7 @@ public:
virtual void seek(uint64_t p_position); ///< seek to a given position
virtual void seek_end(int64_t p_position = 0); ///< seek from the end of file
virtual uint64_t get_position() const; ///< get position in the file
- virtual uint64_t get_len() const; ///< get size of the file
+ virtual uint64_t get_length() const; ///< get size of the file
virtual bool eof_reached() const; ///< reading passed EOF
diff --git a/platform/android/java/lib/src/org/godotengine/godot/vulkan/VkThread.kt b/platform/android/java/lib/src/org/godotengine/godot/vulkan/VkThread.kt
index b967fd5f24..6e59268076 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/vulkan/VkThread.kt
+++ b/platform/android/java/lib/src/org/godotengine/godot/vulkan/VkThread.kt
@@ -61,6 +61,7 @@ internal class VkThread(private val vkSurfaceView: VkSurfaceView, private val vk
private var rendererInitialized = false
private var rendererResumed = false
private var resumed = false
+ private var surfaceChanged = false
private var hasSurface = false
private var width = 0
private var height = 0
@@ -141,8 +142,10 @@ internal class VkThread(private val vkSurfaceView: VkSurfaceView, private val vk
fun onSurfaceChanged(width: Int, height: Int) {
lock.withLock {
hasSurface = true
+ surfaceChanged = true;
this.width = width
this.height = height
+
lockCondition.signalAll()
}
}
@@ -188,8 +191,11 @@ internal class VkThread(private val vkSurfaceView: VkSurfaceView, private val vk
rendererInitialized = true
vkRenderer.onVkSurfaceCreated(vkSurfaceView.holder.surface)
}
+ }
+ if (surfaceChanged) {
vkRenderer.onVkSurfaceChanged(vkSurfaceView.holder.surface, width, height)
+ surfaceChanged = false
}
// Break out of the loop so drawing can occur without holding onto the lock.
diff --git a/platform/android/java_godot_lib_jni.cpp b/platform/android/java_godot_lib_jni.cpp
index 0c342dc280..c7ff6cb2c0 100644
--- a/platform/android/java_godot_lib_jni.cpp
+++ b/platform/android/java_godot_lib_jni.cpp
@@ -173,6 +173,7 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_resize(JNIEnv *env, j
os_android->set_native_window(native_window);
DisplayServerAndroid::get_singleton()->reset_window();
+ DisplayServerAndroid::get_singleton()->notify_surface_changed(p_width, p_height);
}
}
}
diff --git a/platform/iphone/export/export.cpp b/platform/iphone/export/export.cpp
index 36566dffbf..73723b98a0 100644
--- a/platform/iphone/export/export.cpp
+++ b/platform/iphone/export/export.cpp
@@ -353,6 +353,8 @@ void EditorExportPlatformIOS::get_export_options(List<ExportOption> *r_options)
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/code_sign_identity_release", PROPERTY_HINT_PLACEHOLDER_TEXT, "iPhone Distribution"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "application/export_method_release", PROPERTY_HINT_ENUM, "App Store,Development,Ad-Hoc,Enterprise"), 0));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "application/targeted_device_family", PROPERTY_HINT_ENUM, "iPhone,iPad,iPhone & iPad"), 2));
+
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/name", PROPERTY_HINT_PLACEHOLDER_TEXT, "Game Name"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/info"), "Made with Godot Engine"));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/bundle_identifier", PROPERTY_HINT_PLACEHOLDER_TEXT, "com.example.game"), ""));
@@ -448,6 +450,8 @@ void EditorExportPlatformIOS::_fix_config_file(const Ref<EditorExportPreset> &p_
strnew += lines[i].replace("$copyright", p_preset->get("application/copyright")) + "\n";
} else if (lines[i].find("$team_id") != -1) {
strnew += lines[i].replace("$team_id", p_preset->get("application/app_store_team_id")) + "\n";
+ } else if (lines[i].find("$default_build_config") != -1) {
+ strnew += lines[i].replace("$default_build_config", p_debug ? "Debug" : "Release") + "\n";
} else if (lines[i].find("$export_method") != -1) {
int export_method = p_preset->get(p_debug ? "application/export_method_debug" : "application/export_method_release");
strnew += lines[i].replace("$export_method", export_method_string[export_method]) + "\n";
@@ -468,6 +472,20 @@ void EditorExportPlatformIOS::_fix_config_file(const Ref<EditorExportPreset> &p_
strnew += lines[i].replace("$godot_archs", p_config.architectures) + "\n";
} else if (lines[i].find("$linker_flags") != -1) {
strnew += lines[i].replace("$linker_flags", p_config.linker_flags) + "\n";
+ } else if (lines[i].find("$targeted_device_family") != -1) {
+ String xcode_value;
+ switch ((int)p_preset->get("application/targeted_device_family")) {
+ case 0: // iPhone
+ xcode_value = "1";
+ break;
+ case 1: // iPad
+ xcode_value = "2";
+ break;
+ case 2: // iPhone & iPad
+ xcode_value = "1,2";
+ break;
+ }
+ strnew += lines[i].replace("$targeted_device_family", xcode_value) + "\n";
} else if (lines[i].find("$cpp_code") != -1) {
strnew += lines[i].replace("$cpp_code", p_config.cpp_code) + "\n";
} else if (lines[i].find("$docs_in_place") != -1) {
diff --git a/platform/javascript/api/javascript_tools_editor_plugin.cpp b/platform/javascript/api/javascript_tools_editor_plugin.cpp
index ac4e6a1256..b35ccd087f 100644
--- a/platform/javascript/api/javascript_tools_editor_plugin.cpp
+++ b/platform/javascript/api/javascript_tools_editor_plugin.cpp
@@ -72,7 +72,7 @@ void JavaScriptToolsEditorPlugin::_download_zip(Variant p_v) {
FileAccess *f = FileAccess::open("/tmp/project.zip", FileAccess::READ);
ERR_FAIL_COND_MSG(!f, "Unable to create zip file");
Vector<uint8_t> buf;
- buf.resize(f->get_len());
+ buf.resize(f->get_length());
f->get_buffer(buf.ptrw(), buf.size());
godot_js_os_download_buffer(buf.ptr(), buf.size(), "project.zip", "application/zip");
}
@@ -84,7 +84,7 @@ void JavaScriptToolsEditorPlugin::_zip_file(String p_path, String p_base_path, z
return;
}
Vector<uint8_t> data;
- uint64_t len = f->get_len();
+ uint64_t len = f->get_length();
data.resize(len);
f->get_buffer(data.ptrw(), len);
f->close();
diff --git a/platform/javascript/export/export.cpp b/platform/javascript/export/export.cpp
index e398072312..8ce294f31b 100644
--- a/platform/javascript/export/export.cpp
+++ b/platform/javascript/export/export.cpp
@@ -63,7 +63,7 @@ private:
}
void _set_internal_certs(Ref<Crypto> p_crypto) {
- const String cache_path = EditorSettings::get_singleton()->get_cache_dir();
+ const String cache_path = EditorPaths::get_singleton()->get_cache_dir();
const String key_path = cache_path.plus_file("html5_server.key");
const String crt_path = cache_path.plus_file("html5_server.crt");
bool regen = !FileAccess::exists(key_path) || !FileAccess::exists(crt_path);
@@ -138,7 +138,7 @@ public:
const String req_file = req[1].get_file();
const String req_ext = req[1].get_extension();
- const String cache_path = EditorSettings::get_singleton()->get_cache_dir().plus_file("web");
+ const String cache_path = EditorPaths::get_singleton()->get_cache_dir().plus_file("web");
const String filepath = cache_path.plus_file(req_file);
if (!mimes.has(req_ext) || !FileAccess::exists(filepath)) {
@@ -547,7 +547,7 @@ Error EditorExportPlatformJavaScript::_build_pwa(const Ref<EditorExportPreset> &
EditorNode::get_singleton()->show_warning(TTR("Could not read file:") + "\n" + sw_path);
return ERR_FILE_CANT_READ;
}
- sw.resize(f->get_len());
+ sw.resize(f->get_length());
f->get_buffer(sw.ptrw(), sw.size());
memdelete(f);
f = nullptr;
@@ -651,7 +651,7 @@ void EditorExportPlatformJavaScript::get_export_options(List<ExportOption> *r_op
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "html/experimental_virtual_keyboard"), false));
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "progressive_web_app/enabled"), false));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "progressive_web_app/offline_page", PROPERTY_HINT_FILE, "*.html"), ""));
- r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "progressive_web_app/display", PROPERTY_HINT_ENUM, "Fullscreen,Standalone,Minimal Ui,Browser"), 1));
+ r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "progressive_web_app/display", PROPERTY_HINT_ENUM, "Fullscreen,Standalone,Minimal UI,Browser"), 1));
r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "progressive_web_app/orientation", PROPERTY_HINT_ENUM, "Any,Landscape,Portrait"), 0));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "progressive_web_app/icon_144x144", PROPERTY_HINT_FILE, "*.png,*.webp,*.svg,*.svgz"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "progressive_web_app/icon_180x180", PROPERTY_HINT_FILE, "*.png,*.webp,*.svg,*.svgz"), ""));
@@ -781,13 +781,13 @@ Error EditorExportPlatformJavaScript::export_project(const Ref<EditorExportPrese
FileAccess *f = nullptr;
f = FileAccess::open(pck_path, FileAccess::READ);
if (f) {
- file_sizes[pck_path.get_file()] = (uint64_t)f->get_len();
+ file_sizes[pck_path.get_file()] = (uint64_t)f->get_length();
memdelete(f);
f = nullptr;
}
f = FileAccess::open(base_path + ".wasm", FileAccess::READ);
if (f) {
- file_sizes[base_name + ".wasm"] = (uint64_t)f->get_len();
+ file_sizes[base_name + ".wasm"] = (uint64_t)f->get_length();
memdelete(f);
f = nullptr;
}
@@ -800,7 +800,7 @@ Error EditorExportPlatformJavaScript::export_project(const Ref<EditorExportPrese
EditorNode::get_singleton()->show_warning(TTR("Could not read HTML shell:") + "\n" + html_path);
return ERR_FILE_CANT_READ;
}
- html.resize(f->get_len());
+ html.resize(f->get_length());
f->get_buffer(html.ptrw(), html.size());
memdelete(f);
f = nullptr;
@@ -888,7 +888,7 @@ Error EditorExportPlatformJavaScript::run(const Ref<EditorExportPreset> &p_prese
return OK;
}
- const String dest = EditorSettings::get_singleton()->get_cache_dir().plus_file("web");
+ const String dest = EditorPaths::get_singleton()->get_cache_dir().plus_file("web");
DirAccessRef da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
if (!da->dir_exists(dest)) {
Error err = da->make_dir_recursive(dest);
diff --git a/platform/osx/display_server_osx.mm b/platform/osx/display_server_osx.mm
index 4c761e77b5..f53b60891f 100644
--- a/platform/osx/display_server_osx.mm
+++ b/platform/osx/display_server_osx.mm
@@ -3479,7 +3479,7 @@ void DisplayServerOSX::set_native_icon(const String &p_filename) {
ERR_FAIL_COND(!f);
Vector<uint8_t> data;
- uint64_t len = f->get_len();
+ uint64_t len = f->get_length();
data.resize(len);
f->get_buffer((uint8_t *)&data.write[0], len);
memdelete(f);
diff --git a/platform/osx/export/export.cpp b/platform/osx/export/export.cpp
index b2f881a8a8..6d995412ab 100644
--- a/platform/osx/export/export.cpp
+++ b/platform/osx/export/export.cpp
@@ -301,7 +301,7 @@ void EditorExportPlatformOSX::_make_icon(const Ref<Image> &p_icon, Vector<uint8_
if (icon_infos[i].is_png) {
// Encode PNG icon.
it->create_from_image(copy);
- String path = EditorSettings::get_singleton()->get_cache_dir().plus_file("icon.png");
+ String path = EditorPaths::get_singleton()->get_cache_dir().plus_file("icon.png");
ResourceSaver::save(path, it);
FileAccess *f = FileAccess::open(path, FileAccess::READ);
@@ -312,7 +312,7 @@ void EditorExportPlatformOSX::_make_icon(const Ref<Image> &p_icon, Vector<uint8_
}
int ofs = data.size();
- uint64_t len = f->get_len();
+ uint64_t len = f->get_length();
data.resize(data.size() + len + 8);
f->get_buffer(&data.write[ofs + 8], len);
memdelete(f);
@@ -610,7 +610,7 @@ Error EditorExportPlatformOSX::export_project(const Ref<EditorExportPreset> &p_p
// Create our application bundle.
String tmp_app_dir_name = pkg_name + ".app";
- String tmp_app_path_name = EditorSettings::get_singleton()->get_cache_dir().plus_file(tmp_app_dir_name);
+ String tmp_app_path_name = EditorPaths::get_singleton()->get_cache_dir().plus_file(tmp_app_dir_name);
print_line("Exporting to " + tmp_app_path_name);
Error err = OK;
@@ -689,8 +689,8 @@ Error EditorExportPlatformOSX::export_project(const Ref<EditorExportPreset> &p_p
if (iconpath.get_extension() == "icns") {
FileAccess *icon = FileAccess::open(iconpath, FileAccess::READ);
if (icon) {
- data.resize(icon->get_len());
- icon->get_buffer(&data.write[0], icon->get_len());
+ data.resize(icon->get_length());
+ icon->get_buffer(&data.write[0], icon->get_length());
icon->close();
memdelete(icon);
}
@@ -774,7 +774,7 @@ Error EditorExportPlatformOSX::export_project(const Ref<EditorExportPreset> &p_p
String ent_path = p_preset->get("codesign/entitlements/custom_file");
if (sign_enabled && (ent_path == "")) {
- ent_path = EditorSettings::get_singleton()->get_cache_dir().plus_file(pkg_name + ".entitlements");
+ ent_path = EditorPaths::get_singleton()->get_cache_dir().plus_file(pkg_name + ".entitlements");
FileAccess *ent_f = FileAccess::open(ent_path, FileAccess::WRITE);
if (ent_f) {
@@ -959,7 +959,7 @@ Error EditorExportPlatformOSX::export_project(const Ref<EditorExportPreset> &p_p
zlib_filefunc_def io_dst = zipio_create_io_from_file(&dst_f);
zipFile zip = zipOpen2(p_path.utf8().get_data(), APPEND_STATUS_CREATE, nullptr, &io_dst);
- _zip_folder_recursive(zip, EditorSettings::get_singleton()->get_cache_dir(), pkg_name + ".app", pkg_name);
+ _zip_folder_recursive(zip, EditorPaths::get_singleton()->get_cache_dir(), pkg_name + ".app", pkg_name);
zipClose(zip, nullptr);
}
diff --git a/platform/uwp/export/export.cpp b/platform/uwp/export/export.cpp
index 217c119978..351aaa5957 100644
--- a/platform/uwp/export/export.cpp
+++ b/platform/uwp/export/export.cpp
@@ -567,12 +567,12 @@ void AppxPackager::finish() {
// Create and add block map file
EditorNode::progress_task_step("export", "Creating block map...", 4);
- const String &tmp_blockmap_file_path = EditorSettings::get_singleton()->get_cache_dir().plus_file("tmpblockmap.xml");
+ const String &tmp_blockmap_file_path = EditorPaths::get_singleton()->get_cache_dir().plus_file("tmpblockmap.xml");
make_block_map(tmp_blockmap_file_path);
FileAccess *blockmap_file = FileAccess::open(tmp_blockmap_file_path, FileAccess::READ);
Vector<uint8_t> blockmap_buffer;
- blockmap_buffer.resize(blockmap_file->get_len());
+ blockmap_buffer.resize(blockmap_file->get_length());
blockmap_file->get_buffer(blockmap_buffer.ptrw(), blockmap_buffer.size());
@@ -585,12 +585,12 @@ void AppxPackager::finish() {
EditorNode::progress_task_step("export", "Setting content types...", 5);
- const String &tmp_content_types_file_path = EditorSettings::get_singleton()->get_cache_dir().plus_file("tmpcontenttypes.xml");
+ const String &tmp_content_types_file_path = EditorPaths::get_singleton()->get_cache_dir().plus_file("tmpcontenttypes.xml");
make_content_types(tmp_content_types_file_path);
FileAccess *types_file = FileAccess::open(tmp_content_types_file_path, FileAccess::READ);
Vector<uint8_t> types_buffer;
- types_buffer.resize(types_file->get_len());
+ types_buffer.resize(types_file->get_length());
types_file->get_buffer(types_buffer.ptrw(), types_buffer.size());
@@ -879,7 +879,7 @@ class EditorExportPlatformUWP : public EditorExportPlatform {
return data;
}
- String tmp_path = EditorSettings::get_singleton()->get_cache_dir().plus_file("uwp_tmp_logo.png");
+ String tmp_path = EditorPaths::get_singleton()->get_cache_dir().plus_file("uwp_tmp_logo.png");
Error err = texture->get_image()->save_png(tmp_path);
@@ -900,7 +900,7 @@ class EditorExportPlatformUWP : public EditorExportPlatform {
ERR_FAIL_V_MSG(data, err_string);
}
- data.resize(f->get_len());
+ data.resize(f->get_length());
f->get_buffer(data.ptrw(), data.size());
f->close();
diff --git a/scene/2d/camera_2d.cpp b/scene/2d/camera_2d.cpp
index 01045502d5..ca4b8d72a1 100644
--- a/scene/2d/camera_2d.cpp
+++ b/scene/2d/camera_2d.cpp
@@ -270,11 +270,10 @@ void Camera2D::_notification(int p_what) {
}
if (screen_drawing_enabled) {
- Color area_axis_color(0.5, 0.42, 0.87, 0.63);
+ Color area_axis_color(1, 0.4, 1, 0.63);
real_t area_axis_width = 1;
if (is_current()) {
area_axis_width = 3;
- area_axis_color.a = 0.83;
}
Transform2D inv_camera_transform = get_camera_transform().affine_inverse();
@@ -295,10 +294,9 @@ void Camera2D::_notification(int p_what) {
}
if (limit_drawing_enabled) {
- Color limit_drawing_color(1, 1, 0, 0.63);
+ Color limit_drawing_color(1, 1, 0.25, 0.63);
real_t limit_drawing_width = 1;
if (is_current()) {
- limit_drawing_color.a = 0.83;
limit_drawing_width = 3;
}
@@ -317,11 +315,10 @@ void Camera2D::_notification(int p_what) {
}
if (margin_drawing_enabled) {
- Color margin_drawing_color(0, 1, 1, 0.63);
+ Color margin_drawing_color(0.25, 1, 1, 0.63);
real_t margin_drawing_width = 1;
if (is_current()) {
margin_drawing_width = 3;
- margin_drawing_color.a = 0.83;
}
Transform2D inv_camera_transform = get_camera_transform().affine_inverse();
diff --git a/scene/2d/light_2d.cpp b/scene/2d/light_2d.cpp
index 8fb765f16b..ce57895341 100644
--- a/scene/2d/light_2d.cpp
+++ b/scene/2d/light_2d.cpp
@@ -281,7 +281,7 @@ void Light2D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "editor_only"), "set_editor_only", "is_editor_only");
ADD_PROPERTY(PropertyInfo(Variant::COLOR, "color"), "set_color", "get_color");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "energy", PROPERTY_HINT_RANGE, "0,16,0.01,or_greater"), "set_energy", "get_energy");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "blend_mode", PROPERTY_HINT_ENUM, "Add,Sub,Mix"), "set_blend_mode", "get_blend_mode");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "blend_mode", PROPERTY_HINT_ENUM, "Add,Subtract,Mix"), "set_blend_mode", "get_blend_mode");
ADD_GROUP("Range", "range_");
ADD_PROPERTY(PropertyInfo(Variant::INT, "range_z_min", PROPERTY_HINT_RANGE, itos(RS::CANVAS_ITEM_Z_MIN) + "," + itos(RS::CANVAS_ITEM_Z_MAX) + ",1"), "set_z_range_min", "get_z_range_min");
ADD_PROPERTY(PropertyInfo(Variant::INT, "range_z_max", PROPERTY_HINT_RANGE, itos(RS::CANVAS_ITEM_Z_MIN) + "," + itos(RS::CANVAS_ITEM_Z_MAX) + ",1"), "set_z_range_max", "get_z_range_max");
@@ -292,7 +292,7 @@ void Light2D::_bind_methods() {
ADD_GROUP("Shadow", "shadow_");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "shadow_enabled"), "set_shadow_enabled", "is_shadow_enabled");
ADD_PROPERTY(PropertyInfo(Variant::COLOR, "shadow_color"), "set_shadow_color", "get_shadow_color");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "shadow_filter", PROPERTY_HINT_ENUM, "None,PCF5,PCF13"), "set_shadow_filter", "get_shadow_filter");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "shadow_filter", PROPERTY_HINT_ENUM, "None (Fast),PCF5 (Average),PCF13 (Slow)"), "set_shadow_filter", "get_shadow_filter");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "shadow_filter_smooth", PROPERTY_HINT_RANGE, "0,64,0.1"), "set_shadow_smooth", "get_shadow_smooth");
ADD_PROPERTY(PropertyInfo(Variant::INT, "shadow_item_cull_mask", PROPERTY_HINT_LAYERS_2D_RENDER), "set_item_shadow_cull_mask", "get_item_shadow_cull_mask");
diff --git a/scene/2d/tile_map.cpp b/scene/2d/tile_map.cpp
index 8666361e88..24b907fe6c 100644
--- a/scene/2d/tile_map.cpp
+++ b/scene/2d/tile_map.cpp
@@ -314,37 +314,24 @@ void TileMap::set_quadrant_size(int p_size) {
emit_signal("changed");
}
-void TileMap::_fix_cell_transform(Transform2D &xform, const TileMapCell &p_cell, const Vector2 &p_offset, const Size2 &p_sc) {
- Size2 s = p_sc;
- Vector2 offset = p_offset;
-
- // Flip/transpose: update the tile transform.
- TileSetSource *source = *tile_set->get_source(p_cell.source_id);
- TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source);
- if (!atlas_source) {
- return;
- }
- TileData *tile_data = Object::cast_to<TileData>(atlas_source->get_tile_data(p_cell.get_atlas_coords(), p_cell.alternative_tile));
- if (tile_data->get_transpose()) {
- SWAP(xform.elements[0].x, xform.elements[0].y);
- SWAP(xform.elements[1].x, xform.elements[1].y);
- SWAP(offset.x, offset.y);
- SWAP(s.x, s.y);
- }
+void TileMap::set_collision_visibility_mode(TileMap::VisibilityMode p_show_collision) {
+ show_collision = p_show_collision;
+ _recreate_quadrants();
+ emit_signal("changed");
+}
- if (tile_data->get_flip_h()) {
- xform.elements[0].x = -xform.elements[0].x;
- xform.elements[1].x = -xform.elements[1].x;
- offset.x = s.x - offset.x;
- }
+TileMap::VisibilityMode TileMap::get_collision_visibility_mode() {
+ return show_collision;
+}
- if (tile_data->get_flip_v()) {
- xform.elements[0].y = -xform.elements[0].y;
- xform.elements[1].y = -xform.elements[1].y;
- offset.y = s.y - offset.y;
- }
+void TileMap::set_navigation_visibility_mode(TileMap::VisibilityMode p_show_navigation) {
+ show_navigation = p_show_navigation;
+ _recreate_quadrants();
+ emit_signal("changed");
+}
- xform.elements[2] += offset;
+TileMap::VisibilityMode TileMap::get_navigation_visibility_mode() {
+ return show_navigation;
}
void TileMap::update_dirty_quadrants() {
@@ -1723,6 +1710,12 @@ void TileMap::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_quadrant_size", "size"), &TileMap::set_quadrant_size);
ClassDB::bind_method(D_METHOD("get_quadrant_size"), &TileMap::get_quadrant_size);
+ ClassDB::bind_method(D_METHOD("set_collision_visibility_mode", "show_collision"), &TileMap::set_collision_visibility_mode);
+ ClassDB::bind_method(D_METHOD("get_collision_visibility_mode"), &TileMap::get_collision_visibility_mode);
+
+ ClassDB::bind_method(D_METHOD("set_navigation_visibility_mode", "show_navigation"), &TileMap::set_navigation_visibility_mode);
+ ClassDB::bind_method(D_METHOD("get_navigation_visibility_mode"), &TileMap::get_navigation_visibility_mode);
+
ClassDB::bind_method(D_METHOD("set_cell", "coords", "source_id", "atlas_coords", "alternative_tile"), &TileMap::set_cell, DEFVAL(-1), DEFVAL(TileSetSource::INVALID_ATLAS_COORDS), DEFVAL(TileSetSource::INVALID_TILE_ALTERNATIVE));
ClassDB::bind_method(D_METHOD("get_cell_source_id", "coords"), &TileMap::get_cell_source_id);
ClassDB::bind_method(D_METHOD("get_cell_atlas_coords", "coords"), &TileMap::get_cell_atlas_coords);
@@ -1747,10 +1740,16 @@ void TileMap::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "tile_set", PROPERTY_HINT_RESOURCE_TYPE, "TileSet"), "set_tileset", "get_tileset");
ADD_PROPERTY(PropertyInfo(Variant::INT, "cell_quadrant_size", PROPERTY_HINT_RANGE, "1,128,1"), "set_quadrant_size", "get_quadrant_size");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "show_collision", PROPERTY_HINT_ENUM, "Default,Force Show,Force Hide"), "set_collision_visibility_mode", "get_collision_visibility_mode");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "show_navigation", PROPERTY_HINT_ENUM, "Default,Force Show,Force Hide"), "set_navigation_visibility_mode", "get_navigation_visibility_mode");
ADD_PROPERTY_DEFAULT("format", FORMAT_1);
ADD_SIGNAL(MethodInfo("changed"));
+
+ BIND_ENUM_CONSTANT(VISIBILITY_MODE_DEFAULT);
+ BIND_ENUM_CONSTANT(VISIBILITY_MODE_FORCE_HIDE);
+ BIND_ENUM_CONSTANT(VISIBILITY_MODE_FORCE_SHOW);
}
void TileMap::_tile_set_changed() {
@@ -1759,12 +1758,6 @@ void TileMap::_tile_set_changed() {
}
TileMap::TileMap() {
- rect_cache_dirty = true;
- used_size_cache_dirty = true;
- pending_update = false;
- quadrant_size = 16;
- format = FORMAT_1; // Assume lowest possible format if none is present
-
set_notify_transform(true);
set_notify_local_transform(false);
}
diff --git a/scene/2d/tile_map.h b/scene/2d/tile_map.h
index 2703e1980e..f02455a84b 100644
--- a/scene/2d/tile_map.h
+++ b/scene/2d/tile_map.h
@@ -179,37 +179,45 @@ class TileMap : public Node2D {
GDCLASS(TileMap, Node2D);
public:
+ enum VisibilityMode {
+ VISIBILITY_MODE_DEFAULT,
+ VISIBILITY_MODE_FORCE_SHOW,
+ VISIBILITY_MODE_FORCE_HIDE,
+ };
+
private:
friend class TileSetPlugin;
+ // A compatibility enum to specify how is the data if formatted.
enum DataFormat {
FORMAT_1 = 0,
FORMAT_2,
FORMAT_3
};
+ mutable DataFormat format = FORMAT_1; // Assume lowest possible format if none is present;
+ // Properties.
Ref<TileSet> tile_set;
- int quadrant_size;
- Transform2D custom_transform;
-
- // Map of cells
- Map<Vector2i, TileMapCell> tile_map;
-
- Vector2i _coords_to_quadrant_coords(const Vector2i &p_coords) const;
-
- Map<Vector2i, TileMapQuadrant> quadrant_map;
-
- SelfList<TileMapQuadrant>::List dirty_quadrant_list;
+ int quadrant_size = 16;
+ VisibilityMode show_collision = VISIBILITY_MODE_DEFAULT;
+ VisibilityMode show_navigation = VISIBILITY_MODE_DEFAULT;
+ // Updates.
bool pending_update = false;
+ // Rect.
Rect2 rect_cache;
bool rect_cache_dirty = true;
Rect2 used_size_cache;
- bool used_size_cache_dirty;
- mutable DataFormat format;
+ bool used_size_cache_dirty = true;
- void _fix_cell_transform(Transform2D &xform, const TileMapCell &p_cell, const Vector2 &p_offset, const Size2 &p_sc);
+ // Map of cells.
+ Map<Vector2i, TileMapCell> tile_map;
+
+ // Quadrants management.
+ Map<Vector2i, TileMapQuadrant> quadrant_map;
+ Vector2i _coords_to_quadrant_coords(const Vector2i &p_coords) const;
+ SelfList<TileMapQuadrant>::List dirty_quadrant_list;
Map<Vector2i, TileMapQuadrant>::Element *_create_quadrant(const Vector2i &p_qk);
void _erase_quadrant(Map<Vector2i, TileMapQuadrant>::Element *Q);
@@ -219,8 +227,7 @@ private:
void _clear_quadrants();
void _recompute_rect_cache();
- void _update_all_items_material_state();
-
+ // Set and get tiles from data arrays.
void _set_tile_data(const Vector<int> &p_data);
Vector<int> _get_tile_data() const;
@@ -251,6 +258,12 @@ public:
void set_quadrant_size(int p_size);
int get_quadrant_size() const;
+ void set_collision_visibility_mode(VisibilityMode p_show_collision);
+ VisibilityMode get_collision_visibility_mode();
+
+ void set_navigation_visibility_mode(VisibilityMode p_show_navigation);
+ VisibilityMode get_navigation_visibility_mode();
+
void set_cell(const Vector2i &p_coords, int p_source_id = -1, const Vector2i p_atlas_coords = TileSetSource::INVALID_ATLAS_COORDS, int p_alternative_tile = TileSetSource::INVALID_TILE_ALTERNATIVE);
int get_cell_source_id(const Vector2i &p_coords) const;
Vector2i get_cell_atlas_coords(const Vector2i &p_coords) const;
@@ -293,4 +306,7 @@ public:
TileMap();
~TileMap();
};
+
+VARIANT_ENUM_CAST(TileMap::VisibilityMode);
+
#endif // TILE_MAP_H
diff --git a/scene/3d/audio_stream_player_3d.cpp b/scene/3d/audio_stream_player_3d.cpp
index 72392be5bd..55b26bc9fb 100644
--- a/scene/3d/audio_stream_player_3d.cpp
+++ b/scene/3d/audio_stream_player_3d.cpp
@@ -962,7 +962,7 @@ void AudioStreamPlayer3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_stream_playback"), &AudioStreamPlayer3D::get_stream_playback);
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "stream", PROPERTY_HINT_RESOURCE_TYPE, "AudioStream"), "set_stream", "get_stream");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "attenuation_model", PROPERTY_HINT_ENUM, "Inverse,InverseSquare,Log,Disabled"), "set_attenuation_model", "get_attenuation_model");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "attenuation_model", PROPERTY_HINT_ENUM, "Inverse,Inverse Square,Log,Disabled"), "set_attenuation_model", "get_attenuation_model");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "unit_db", PROPERTY_HINT_RANGE, "-80,80"), "set_unit_db", "get_unit_db");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "unit_size", PROPERTY_HINT_RANGE, "0.1,100,0.1"), "set_unit_size", "get_unit_size");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "max_db", PROPERTY_HINT_RANGE, "-24,6"), "set_max_db", "get_max_db");
diff --git a/scene/3d/reflection_probe.cpp b/scene/3d/reflection_probe.cpp
index ad24f39bce..7762156b4a 100644
--- a/scene/3d/reflection_probe.cpp
+++ b/scene/3d/reflection_probe.cpp
@@ -230,7 +230,7 @@ void ReflectionProbe::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_update_mode", "mode"), &ReflectionProbe::set_update_mode);
ClassDB::bind_method(D_METHOD("get_update_mode"), &ReflectionProbe::get_update_mode);
- ADD_PROPERTY(PropertyInfo(Variant::INT, "update_mode", PROPERTY_HINT_ENUM, "Once,Always"), "set_update_mode", "get_update_mode");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "update_mode", PROPERTY_HINT_ENUM, "Once (Fast),Always (Slow)"), "set_update_mode", "get_update_mode");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "intensity", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_intensity", "get_intensity");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "max_distance", PROPERTY_HINT_EXP_RANGE, "0,16384,0.1,or_greater"), "set_max_distance", "get_max_distance");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "extents"), "set_extents", "get_extents");
@@ -242,7 +242,7 @@ void ReflectionProbe::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "lod_threshold", PROPERTY_HINT_RANGE, "0,1024,0.1"), "set_lod_threshold", "get_lod_threshold");
ADD_GROUP("Ambient", "ambient_");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "ambient_mode", PROPERTY_HINT_ENUM, "Disabled,Environment,ConstantColor"), "set_ambient_mode", "get_ambient_mode");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "ambient_mode", PROPERTY_HINT_ENUM, "Disabled,Environment,Constant Color"), "set_ambient_mode", "get_ambient_mode");
ADD_PROPERTY(PropertyInfo(Variant::COLOR, "ambient_color", PROPERTY_HINT_COLOR_NO_ALPHA), "set_ambient_color", "get_ambient_color");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "ambient_color_energy", PROPERTY_HINT_RANGE, "0,16,0.01"), "set_ambient_color_energy", "get_ambient_color_energy");
diff --git a/scene/3d/skeleton_3d.cpp b/scene/3d/skeleton_3d.cpp
index 93f7dbcd91..82927df5f1 100644
--- a/scene/3d/skeleton_3d.cpp
+++ b/scene/3d/skeleton_3d.cpp
@@ -698,7 +698,7 @@ void Skeleton3D::_rebuild_physical_bones_cache() {
const int b_size = bones.size();
for (int i = 0; i < b_size; ++i) {
PhysicalBone3D *parent_pb = _get_physical_bone_parent(i);
- if (parent_pb != bones[i].physical_bone) {
+ if (parent_pb != bones[i].cache_parent_physical_bone) {
bones.write[i].cache_parent_physical_bone = parent_pb;
if (bones[i].physical_bone) {
bones[i].physical_bone->_on_bone_parent_changed();
diff --git a/scene/3d/skeleton_ik_3d.cpp b/scene/3d/skeleton_ik_3d.cpp
index bd1c202205..294e313300 100644
--- a/scene/3d/skeleton_ik_3d.cpp
+++ b/scene/3d/skeleton_ik_3d.cpp
@@ -129,7 +129,7 @@ bool FabrikInverseKinematic::build_chain(Task *p_task, bool p_force_simple_chain
return true;
}
-void FabrikInverseKinematic::solve_simple(Task *p_task, bool p_solve_magnet) {
+void FabrikInverseKinematic::solve_simple(Task *p_task, bool p_solve_magnet, Vector3 p_origin_pos) {
real_t distance_to_goal(1e4);
real_t previous_distance_to_goal(0);
int can_solve(p_task->max_iterations);
@@ -138,7 +138,7 @@ void FabrikInverseKinematic::solve_simple(Task *p_task, bool p_solve_magnet) {
--can_solve;
solve_simple_backwards(p_task->chain, p_solve_magnet);
- solve_simple_forwards(p_task->chain, p_solve_magnet);
+ solve_simple_forwards(p_task->chain, p_solve_magnet, p_origin_pos);
distance_to_goal = (p_task->chain.tips[0].chain_item->current_pos - p_task->chain.tips[0].end_effector->goal_transform.origin).length();
}
@@ -176,13 +176,13 @@ void FabrikInverseKinematic::solve_simple_backwards(Chain &r_chain, bool p_solve
}
}
-void FabrikInverseKinematic::solve_simple_forwards(Chain &r_chain, bool p_solve_magnet) {
+void FabrikInverseKinematic::solve_simple_forwards(Chain &r_chain, bool p_solve_magnet, Vector3 p_origin_pos) {
if (p_solve_magnet && !r_chain.middle_chain_item) {
return;
}
ChainItem *sub_chain_root(&r_chain.chain_root);
- Vector3 origin(r_chain.chain_root.initial_transform.origin);
+ Vector3 origin = p_origin_pos;
while (sub_chain_root) { // Reach the tip
sub_chain_root->current_pos = origin;
@@ -273,13 +273,16 @@ void FabrikInverseKinematic::solve(Task *p_task, real_t blending_delta, bool ove
// Update the initial root transform so its synced with any animation changes
_update_chain(p_task->skeleton, &p_task->chain.chain_root);
+ p_task->skeleton->set_bone_global_pose_override(p_task->chain.chain_root.bone, Transform(), 0.0, false);
+ Vector3 origin_pos = p_task->skeleton->get_bone_global_pose(p_task->chain.chain_root.bone).origin;
+
make_goal(p_task, p_task->skeleton->get_global_transform().affine_inverse(), blending_delta);
if (p_use_magnet && p_task->chain.middle_chain_item) {
p_task->chain.magnet_position = p_task->chain.middle_chain_item->initial_transform.origin.lerp(p_magnet_position, blending_delta);
- solve_simple(p_task, true);
+ solve_simple(p_task, true, origin_pos);
}
- solve_simple(p_task, false);
+ solve_simple(p_task, false, origin_pos);
// Assign new bone position.
ChainItem *ci(&p_task->chain.chain_root);
diff --git a/scene/3d/skeleton_ik_3d.h b/scene/3d/skeleton_ik_3d.h
index 9255e18b72..9b5ae240f6 100644
--- a/scene/3d/skeleton_ik_3d.h
+++ b/scene/3d/skeleton_ik_3d.h
@@ -106,10 +106,10 @@ private:
/// Init a chain that starts from the root to tip
static bool build_chain(Task *p_task, bool p_force_simple_chain = true);
- static void solve_simple(Task *p_task, bool p_solve_magnet);
+ static void solve_simple(Task *p_task, bool p_solve_magnet, Vector3 p_origin_pos);
/// Special solvers that solve only chains with one end effector
static void solve_simple_backwards(Chain &r_chain, bool p_solve_magnet);
- static void solve_simple_forwards(Chain &r_chain, bool p_solve_magnet);
+ static void solve_simple_forwards(Chain &r_chain, bool p_solve_magnet, Vector3 p_origin_pos);
public:
static Task *create_simple_task(Skeleton3D *p_sk, BoneId root_bone, BoneId tip_bone, const Transform &goal_transform);
diff --git a/scene/animation/animation_node_state_machine.cpp b/scene/animation/animation_node_state_machine.cpp
index 246fff6d57..65918a2989 100644
--- a/scene/animation/animation_node_state_machine.cpp
+++ b/scene/animation/animation_node_state_machine.cpp
@@ -115,7 +115,7 @@ void AnimationNodeStateMachineTransition::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_priority", "priority"), &AnimationNodeStateMachineTransition::set_priority);
ClassDB::bind_method(D_METHOD("get_priority"), &AnimationNodeStateMachineTransition::get_priority);
- ADD_PROPERTY(PropertyInfo(Variant::INT, "switch_mode", PROPERTY_HINT_ENUM, "Immediate,Sync,AtEnd"), "set_switch_mode", "get_switch_mode");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "switch_mode", PROPERTY_HINT_ENUM, "Immediate,Sync,At End"), "set_switch_mode", "get_switch_mode");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "auto_advance"), "set_auto_advance", "has_auto_advance");
ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "advance_condition"), "set_advance_condition", "get_advance_condition");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "xfade_time", PROPERTY_HINT_RANGE, "0,240,0.01"), "set_xfade_time", "get_xfade_time");
diff --git a/scene/gui/aspect_ratio_container.cpp b/scene/gui/aspect_ratio_container.cpp
index c7f6c0e2da..fb6fa9dec9 100644
--- a/scene/gui/aspect_ratio_container.cpp
+++ b/scene/gui/aspect_ratio_container.cpp
@@ -155,7 +155,7 @@ void AspectRatioContainer::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_alignment_vertical"), &AspectRatioContainer::get_alignment_vertical);
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "ratio"), "set_ratio", "get_ratio");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "stretch_mode", PROPERTY_HINT_ENUM, "Width controls height,Height controls width,Fit,Cover"), "set_stretch_mode", "get_stretch_mode");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "stretch_mode", PROPERTY_HINT_ENUM, "Width Controls Height,Height Controls Width,Fit,Cover"), "set_stretch_mode", "get_stretch_mode");
ADD_GROUP("Alignment", "alignment_");
ADD_PROPERTY(PropertyInfo(Variant::INT, "alignment_horizontal", PROPERTY_HINT_ENUM, "Begin,Center,End"), "set_alignment_horizontal", "get_alignment_horizontal");
diff --git a/scene/gui/button.cpp b/scene/gui/button.cpp
index b0bcde8865..c0df5271b4 100644
--- a/scene/gui/button.cpp
+++ b/scene/gui/button.cpp
@@ -533,7 +533,7 @@ void Button::_bind_methods() {
BIND_ENUM_CONSTANT(ALIGN_RIGHT);
ADD_PROPERTY(PropertyInfo(Variant::STRING, "text", PROPERTY_HINT_MULTILINE_TEXT, "", PROPERTY_USAGE_DEFAULT_INTL), "set_text", "get_text");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "text_direction", PROPERTY_HINT_ENUM, "Auto,LTR,RTL,Inherited"), "set_text_direction", "get_text_direction");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "text_direction", PROPERTY_HINT_ENUM, "Auto,Left-to-Right,Right-to-Left,Inherited"), "set_text_direction", "get_text_direction");
ADD_PROPERTY(PropertyInfo(Variant::STRING, "language"), "set_language", "get_language");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "icon", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_button_icon", "get_button_icon");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "flat"), "set_flat", "is_flat");
diff --git a/scene/gui/color_picker.cpp b/scene/gui/color_picker.cpp
index 9ccb2886e1..c0b4563615 100644
--- a/scene/gui/color_picker.cpp
+++ b/scene/gui/color_picker.cpp
@@ -91,7 +91,6 @@ void ColorPicker::init_shaders() {
wheel_shader.instance();
wheel_shader->set_code(
"shader_type canvas_item;"
- "const float TAU = 6.28318530718;"
"void fragment() {"
" float x = UV.x - 0.5;"
" float y = UV.y - 0.5;"
@@ -111,7 +110,6 @@ void ColorPicker::init_shaders() {
circle_shader.instance();
circle_shader->set_code(
"shader_type canvas_item;"
- "const float TAU = 6.28318530718;"
"uniform float v = 1.0;"
"void fragment() {"
" float x = UV.x - 0.5;"
diff --git a/scene/gui/control.cpp b/scene/gui/control.cpp
index 83abd02b40..0ae1dec46d 100644
--- a/scene/gui/control.cpp
+++ b/scene/gui/control.cpp
@@ -2801,7 +2801,7 @@ void Control::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::INT, "grow_vertical", PROPERTY_HINT_ENUM, "Begin,End,Both"), "set_v_grow_direction", "get_v_grow_direction");
ADD_GROUP("Layout Direction", "layout_");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "layout_direction", PROPERTY_HINT_ENUM, "Inherited,Locale,Left-to-right,Right-to-left"), "set_layout_direction", "get_layout_direction");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "layout_direction", PROPERTY_HINT_ENUM, "Inherited,Locale,Left-to-Right,Right-to-Left"), "set_layout_direction", "get_layout_direction");
ADD_GROUP("Rect", "rect_");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "rect_position", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "_set_position", "get_position");
@@ -2828,7 +2828,7 @@ void Control::_bind_methods() {
ADD_GROUP("Mouse", "mouse_");
ADD_PROPERTY(PropertyInfo(Variant::INT, "mouse_filter", PROPERTY_HINT_ENUM, "Stop,Pass,Ignore"), "set_mouse_filter", "get_mouse_filter");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "mouse_default_cursor_shape", PROPERTY_HINT_ENUM, "Arrow,Ibeam,Pointing hand,Cross,Wait,Busy,Drag,Can drop,Forbidden,Vertical resize,Horizontal resize,Secondary diagonal resize,Main diagonal resize,Move,Vertical split,Horizontal split,Help"), "set_default_cursor_shape", "get_default_cursor_shape");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "mouse_default_cursor_shape", PROPERTY_HINT_ENUM, "Arrow,I-Beam,Pointing Hand,Cross,Wait,Busy,Drag,Can Drop,Forbidden,Vertical Resize,Horizontal Resize,Secondary Diagonal Resize,Main Diagonal Resize,Move,Vertical Split,Horizontal Split,Help"), "set_default_cursor_shape", "get_default_cursor_shape");
ADD_GROUP("Size Flags", "size_flags_");
ADD_PROPERTY(PropertyInfo(Variant::INT, "size_flags_horizontal", PROPERTY_HINT_FLAGS, "Fill,Expand,Shrink Center,Shrink End"), "set_h_size_flags", "get_h_size_flags");
diff --git a/scene/gui/graph_node.cpp b/scene/gui/graph_node.cpp
index 1ca54a16ae..77c502cf8d 100644
--- a/scene/gui/graph_node.cpp
+++ b/scene/gui/graph_node.cpp
@@ -999,7 +999,7 @@ void GraphNode::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_overlay"), &GraphNode::get_overlay);
ADD_PROPERTY(PropertyInfo(Variant::STRING, "title"), "set_title", "get_title");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "text_direction", PROPERTY_HINT_ENUM, "Auto,LTR,RTL,Inherited"), "set_text_direction", "get_text_direction");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "text_direction", PROPERTY_HINT_ENUM, "Auto,Left-to-Right,Right-to-Left,Inherited"), "set_text_direction", "get_text_direction");
ADD_PROPERTY(PropertyInfo(Variant::STRING, "language"), "set_language", "get_language");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "position_offset"), "set_position_offset", "get_position_offset");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "show_close"), "set_show_close_button", "is_close_button_visible");
diff --git a/scene/gui/label.cpp b/scene/gui/label.cpp
index be73fd8f51..de0ee626b9 100644
--- a/scene/gui/label.cpp
+++ b/scene/gui/label.cpp
@@ -695,7 +695,7 @@ void Label::_bind_methods() {
BIND_ENUM_CONSTANT(VALIGN_FILL);
ADD_PROPERTY(PropertyInfo(Variant::STRING, "text", PROPERTY_HINT_MULTILINE_TEXT, "", PROPERTY_USAGE_DEFAULT_INTL), "set_text", "get_text");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "text_direction", PROPERTY_HINT_ENUM, "Auto,LTR,RTL,Inherited"), "set_text_direction", "get_text_direction");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "text_direction", PROPERTY_HINT_ENUM, "Auto,Left-to-Right,Right-to-Left,Inherited"), "set_text_direction", "get_text_direction");
ADD_PROPERTY(PropertyInfo(Variant::STRING, "language"), "set_language", "get_language");
ADD_PROPERTY(PropertyInfo(Variant::INT, "align", PROPERTY_HINT_ENUM, "Left,Center,Right,Fill"), "set_align", "get_align");
ADD_PROPERTY(PropertyInfo(Variant::INT, "valign", PROPERTY_HINT_ENUM, "Top,Center,Bottom,Fill"), "set_valign", "get_valign");
diff --git a/scene/gui/line_edit.cpp b/scene/gui/line_edit.cpp
index 98dd9f624b..c2ed9c1a3c 100644
--- a/scene/gui/line_edit.cpp
+++ b/scene/gui/line_edit.cpp
@@ -2209,7 +2209,7 @@ void LineEdit::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "shortcut_keys_enabled"), "set_shortcut_keys_enabled", "is_shortcut_keys_enabled");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "selecting_enabled"), "set_selecting_enabled", "is_selecting_enabled");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "right_icon", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_right_icon", "get_right_icon");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "text_direction", PROPERTY_HINT_ENUM, "Auto,LTR,RTL,Inherited"), "set_text_direction", "get_text_direction");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "text_direction", PROPERTY_HINT_ENUM, "Auto,Left-to-Right,Right-to-Left,Inherited"), "set_text_direction", "get_text_direction");
ADD_PROPERTY(PropertyInfo(Variant::STRING, "language"), "set_language", "get_language");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "draw_control_chars"), "set_draw_control_chars", "get_draw_control_chars");
ADD_GROUP("Structured Text", "structured_text_");
diff --git a/scene/gui/link_button.cpp b/scene/gui/link_button.cpp
index 1f7b61e3d1..d45ffde715 100644
--- a/scene/gui/link_button.cpp
+++ b/scene/gui/link_button.cpp
@@ -292,7 +292,7 @@ void LinkButton::_bind_methods() {
BIND_ENUM_CONSTANT(UNDERLINE_MODE_NEVER);
ADD_PROPERTY(PropertyInfo(Variant::STRING, "text"), "set_text", "get_text");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "text_direction", PROPERTY_HINT_ENUM, "Auto,LTR,RTL,Inherited"), "set_text_direction", "get_text_direction");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "text_direction", PROPERTY_HINT_ENUM, "Auto,Left-to-Right,Right-to-Left,Inherited"), "set_text_direction", "get_text_direction");
ADD_PROPERTY(PropertyInfo(Variant::STRING, "language"), "set_language", "get_language");
ADD_PROPERTY(PropertyInfo(Variant::INT, "underline", PROPERTY_HINT_ENUM, "Always,On Hover,Never"), "set_underline_mode", "get_underline_mode");
ADD_GROUP("Structured Text", "structured_text_");
diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp
index 2800ab0442..a2c3b4ed8a 100644
--- a/scene/gui/rich_text_label.cpp
+++ b/scene/gui/rich_text_label.cpp
@@ -2232,18 +2232,22 @@ void RichTextLabel::_remove_item(Item *p_item, const int p_line, const int p_sub
int size = p_item->subitems.size();
if (size == 0) {
p_item->parent->subitems.erase(p_item);
+ // If a newline was erased, all lines AFTER the newline need to be decremented.
if (p_item->type == ITEM_NEWLINE) {
current_frame->lines.remove(p_line);
- for (int i = p_subitem_line; i < current->subitems.size(); i++) {
- if (current->subitems[i]->line > 0) {
+ for (int i = 0; i < current->subitems.size(); i++) {
+ if (current->subitems[i]->line > p_subitem_line) {
current->subitems[i]->line--;
}
}
}
} else {
+ // First, remove all child items for the provided item.
for (int i = 0; i < size; i++) {
_remove_item(p_item->subitems.front()->get(), p_line, p_subitem_line);
}
+ // Then remove the provided item itself.
+ p_item->parent->subitems.erase(p_item);
}
}
@@ -2303,21 +2307,23 @@ bool RichTextLabel::remove_line(const int p_line) {
return false;
}
- int i = 0;
- while (i < current->subitems.size() && current->subitems[i]->line < p_line) {
- i++;
+ // Remove all subitems with the same line as that provided.
+ Vector<int> subitem_indices_to_remove;
+ for (int i = 0; i < current->subitems.size(); i++) {
+ if (current->subitems[i]->line == p_line) {
+ subitem_indices_to_remove.push_back(i);
+ }
}
- bool was_newline = false;
- while (i < current->subitems.size()) {
- was_newline = current->subitems[i]->type == ITEM_NEWLINE;
- _remove_item(current->subitems[i], current->subitems[i]->line, p_line);
- if (was_newline) {
- break;
- }
+ bool had_newline = false;
+ // Reverse for loop to remove items from the end first.
+ for (int i = subitem_indices_to_remove.size() - 1; i >= 0; i--) {
+ int subitem_idx = subitem_indices_to_remove[i];
+ had_newline = had_newline || current->subitems[subitem_idx]->type == ITEM_NEWLINE;
+ _remove_item(current->subitems[subitem_idx], current->subitems[subitem_idx]->line, p_line);
}
- if (!was_newline) {
+ if (!had_newline) {
current_frame->lines.remove(p_line);
if (current_frame->lines.size() == 0) {
current_frame->lines.resize(1);
@@ -2329,6 +2335,7 @@ bool RichTextLabel::remove_line(const int p_line) {
}
main->first_invalid_line = 0; // p_line ???
+ update();
return true;
}
@@ -2613,14 +2620,6 @@ void RichTextLabel::pop() {
current = current->parent;
}
-// Creates a new line without adding an ItemNewline to the previous line.
-// Useful when wanting to calling remove_line and add a new line immediately after.
-void RichTextLabel::increment_line_count() {
- _validate_line_caches(main);
- current_frame->lines.resize(current_frame->lines.size() + 1);
- _invalidate_current_line(current_frame);
-}
-
void RichTextLabel::clear() {
main->_clear_children();
current = main;
@@ -3935,7 +3934,7 @@ void RichTextLabel::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "custom_effects", PROPERTY_HINT_ARRAY_TYPE, "RichTextEffect", (PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SCRIPT_VARIABLE)), "set_effects", "get_effects");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "text_direction", PROPERTY_HINT_ENUM, "Auto,LTR,RTL,Inherited"), "set_text_direction", "get_text_direction");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "text_direction", PROPERTY_HINT_ENUM, "Auto,Left-to-Right,Right-to-Left,Inherited"), "set_text_direction", "get_text_direction");
ADD_PROPERTY(PropertyInfo(Variant::STRING, "language"), "set_language", "get_language");
ADD_GROUP("Structured Text", "structured_text_");
diff --git a/scene/gui/rich_text_label.h b/scene/gui/rich_text_label.h
index afc88e070a..e3e457d1f2 100644
--- a/scene/gui/rich_text_label.h
+++ b/scene/gui/rich_text_label.h
@@ -483,8 +483,6 @@ public:
void push_cell();
void pop();
- void increment_line_count();
-
void clear();
void set_offset(int p_pixel);
diff --git a/scene/gui/scroll_container.cpp b/scene/gui/scroll_container.cpp
index 46db4a3c2f..5f872644ab 100644
--- a/scene/gui/scroll_container.cpp
+++ b/scene/gui/scroll_container.cpp
@@ -238,32 +238,25 @@ void ScrollContainer::_update_scrollbar_position() {
_updating_scrollbars = false;
}
-void ScrollContainer::_ensure_focused_visible(Control *p_control) {
- if (!follow_focus) {
- return;
+void ScrollContainer::_gui_focus_changed(Control *p_control) {
+ if (follow_focus && is_a_parent_of(p_control)) {
+ ensure_control_visible(p_control);
}
+}
- if (is_a_parent_of(p_control)) {
- Rect2 global_rect = get_global_rect();
- Rect2 other_rect = p_control->get_global_rect();
- float right_margin = 0.0;
- if (v_scroll->is_visible()) {
- right_margin += v_scroll->get_size().x;
- }
- float bottom_margin = 0.0;
- if (h_scroll->is_visible()) {
- bottom_margin += h_scroll->get_size().y;
- }
+void ScrollContainer::ensure_control_visible(Control *p_control) {
+ ERR_FAIL_COND_MSG(!is_a_parent_of(p_control), "Must be a parent of the control.");
- float diff = MAX(MIN(other_rect.position.y, global_rect.position.y), other_rect.position.y + other_rect.size.y - global_rect.size.y + bottom_margin);
- set_v_scroll(get_v_scroll() + (diff - global_rect.position.y));
- if (is_layout_rtl()) {
- diff = MAX(MIN(other_rect.position.x, global_rect.position.x), other_rect.position.x + other_rect.size.x - global_rect.size.x);
- } else {
- diff = MAX(MIN(other_rect.position.x, global_rect.position.x), other_rect.position.x + other_rect.size.x - global_rect.size.x + right_margin);
- }
- set_h_scroll(get_h_scroll() + (diff - global_rect.position.x));
- }
+ Rect2 global_rect = get_global_rect();
+ Rect2 other_rect = p_control->get_global_rect();
+ float right_margin = v_scroll->is_visible() ? v_scroll->get_size().x : 0.0f;
+ float bottom_margin = h_scroll->is_visible() ? h_scroll->get_size().y : 0.0f;
+
+ Vector2 diff = Vector2(MAX(MIN(other_rect.position.x, global_rect.position.x), other_rect.position.x + other_rect.size.x - global_rect.size.x + (!is_layout_rtl() ? right_margin : 0.0f)),
+ MAX(MIN(other_rect.position.y, global_rect.position.y), other_rect.position.y + other_rect.size.y - global_rect.size.y + bottom_margin));
+
+ set_h_scroll(get_h_scroll() + (diff.x - global_rect.position.x));
+ set_v_scroll(get_v_scroll() + (diff.y - global_rect.position.y));
}
void ScrollContainer::_update_dimensions() {
@@ -334,7 +327,7 @@ void ScrollContainer::_notification(int p_what) {
};
if (p_what == NOTIFICATION_READY) {
- get_viewport()->connect("gui_focus_changed", callable_mp(this, &ScrollContainer::_ensure_focused_visible));
+ get_viewport()->connect("gui_focus_changed", callable_mp(this, &ScrollContainer::_gui_focus_changed));
_update_dimensions();
}
@@ -608,6 +601,7 @@ void ScrollContainer::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_h_scrollbar"), &ScrollContainer::get_h_scrollbar);
ClassDB::bind_method(D_METHOD("get_v_scrollbar"), &ScrollContainer::get_v_scrollbar);
+ ClassDB::bind_method(D_METHOD("ensure_control_visible", "control"), &ScrollContainer::ensure_control_visible);
ADD_SIGNAL(MethodInfo("scroll_started"));
ADD_SIGNAL(MethodInfo("scroll_ended"));
diff --git a/scene/gui/scroll_container.h b/scene/gui/scroll_container.h
index f61df70b85..c77a0d62f5 100644
--- a/scene/gui/scroll_container.h
+++ b/scene/gui/scroll_container.h
@@ -69,6 +69,7 @@ protected:
Size2 get_minimum_size() const override;
void _gui_input(const Ref<InputEvent> &p_gui_input);
+ void _gui_focus_changed(Control *p_control);
void _update_dimensions();
void _notification(int p_what);
@@ -77,7 +78,6 @@ protected:
bool _updating_scrollbars = false;
void _update_scrollbar_position();
- void _ensure_focused_visible(Control *p_node);
public:
void set_h_scroll(int p_pos);
@@ -106,6 +106,7 @@ public:
HScrollBar *get_h_scrollbar();
VScrollBar *get_v_scrollbar();
+ void ensure_control_visible(Control *p_control);
virtual bool clips_input() const override;
diff --git a/scene/gui/split_container.cpp b/scene/gui/split_container.cpp
index 13ff2c5b86..df4cf9a740 100644
--- a/scene/gui/split_container.cpp
+++ b/scene/gui/split_container.cpp
@@ -353,7 +353,7 @@ void SplitContainer::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::INT, "split_offset"), "set_split_offset", "get_split_offset");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "collapsed"), "set_collapsed", "is_collapsed");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "dragger_visibility", PROPERTY_HINT_ENUM, "Visible,Hidden,Hidden & Collapsed"), "set_dragger_visibility", "get_dragger_visibility");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "dragger_visibility", PROPERTY_HINT_ENUM, "Visible,Hidden,Hidden and Collapsed"), "set_dragger_visibility", "get_dragger_visibility");
BIND_ENUM_CONSTANT(DRAGGER_VISIBLE);
BIND_ENUM_CONSTANT(DRAGGER_HIDDEN);
diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp
index d19d95f6f4..65d4433f4e 100644
--- a/scene/gui/text_edit.cpp
+++ b/scene/gui/text_edit.cpp
@@ -1187,7 +1187,8 @@ void TextEdit::_notification(int p_what) {
if (rect.position.x < xmargin_beg) {
rect.size.x -= (xmargin_beg - rect.position.x);
rect.position.x = xmargin_beg;
- } else if (rect.position.x + rect.size.x > xmargin_end) {
+ }
+ if (rect.position.x + rect.size.x > xmargin_end) {
rect.size.x = xmargin_end - rect.position.x;
}
draw_rect(rect, cache.selection_color, true);
@@ -7055,7 +7056,7 @@ void TextEdit::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_minimap_width"), &TextEdit::get_minimap_width);
ADD_PROPERTY(PropertyInfo(Variant::STRING, "text", PROPERTY_HINT_MULTILINE_TEXT), "set_text", "get_text");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "text_direction", PROPERTY_HINT_ENUM, "Auto,LTR,RTL,Inherited"), "set_text_direction", "get_text_direction");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "text_direction", PROPERTY_HINT_ENUM, "Auto,Left-to-Right,Right-to-Left,Inherited"), "set_text_direction", "get_text_direction");
ADD_PROPERTY(PropertyInfo(Variant::STRING, "language"), "set_language", "get_language");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "draw_control_chars"), "set_draw_control_chars", "get_draw_control_chars");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "readonly"), "set_readonly", "is_readonly");
diff --git a/scene/gui/tree.cpp b/scene/gui/tree.cpp
index 2f78736a12..d2b98eb0fa 100644
--- a/scene/gui/tree.cpp
+++ b/scene/gui/tree.cpp
@@ -1184,15 +1184,23 @@ void Tree::update_cache() {
cache.font_color = get_theme_color("font_color");
cache.font_selected_color = get_theme_color("font_selected_color");
- cache.guide_color = get_theme_color("guide_color");
cache.drop_position_color = get_theme_color("drop_position_color");
cache.hseparation = get_theme_constant("hseparation");
cache.vseparation = get_theme_constant("vseparation");
cache.item_margin = get_theme_constant("item_margin");
cache.button_margin = get_theme_constant("button_margin");
+
cache.draw_guides = get_theme_constant("draw_guides");
+ cache.guide_color = get_theme_color("guide_color");
cache.draw_relationship_lines = get_theme_constant("draw_relationship_lines");
+ cache.relationship_line_width = get_theme_constant("relationship_line_width");
+ cache.parent_hl_line_width = get_theme_constant("parent_hl_line_width");
+ cache.children_hl_line_width = get_theme_constant("children_hl_line_width");
+ cache.parent_hl_line_margin = get_theme_constant("parent_hl_line_margin");
cache.relationship_line_color = get_theme_color("relationship_line_color");
+ cache.parent_hl_line_color = get_theme_color("parent_hl_line_color");
+ cache.children_hl_line_color = get_theme_color("children_hl_line_color");
+
cache.scroll_border = get_theme_constant("scroll_border");
cache.scroll_speed = get_theme_constant("scroll_speed");
@@ -1813,38 +1821,79 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2
TreeItem *c = p_item->first_child;
- int prev_ofs = children_pos.y - cache.offset.y + p_draw_ofs.y;
+ int base_ofs = children_pos.y - cache.offset.y + p_draw_ofs.y;
+ int prev_ofs = base_ofs;
+ int prev_hl_ofs = base_ofs;
while (c) {
if (cache.draw_relationship_lines > 0 && (!hide_root || c->parent != root)) {
int root_ofs = children_pos.x + ((p_item->disable_folding || hide_folding) ? cache.hseparation : cache.item_margin);
- int parent_ofs = p_pos.x + ((p_item->disable_folding || hide_folding) ? cache.hseparation : cache.item_margin);
+ int parent_ofs = p_pos.x + cache.item_margin;
Point2i root_pos = Point2i(root_ofs, children_pos.y + label_h / 2) - cache.offset + p_draw_ofs;
if (c->get_first_child() != nullptr) {
root_pos -= Point2i(cache.arrow->get_width(), 0);
}
- float line_width = 1.0;
+ float line_width = cache.relationship_line_width;
+ float parent_line_width = cache.parent_hl_line_width;
+ float children_line_width = cache.children_hl_line_width;
+
#ifdef TOOLS_ENABLED
line_width *= Math::round(EDSCALE);
+ parent_line_width *= Math::round(EDSCALE);
+ children_line_width *= Math::round(EDSCALE);
#endif
Point2i parent_pos = Point2i(parent_ofs - cache.arrow->get_width() / 2, p_pos.y + label_h / 2 + cache.arrow->get_height() / 2) - cache.offset + p_draw_ofs;
+ int more_prev_ofs = 0;
+
if (root_pos.y + line_width >= 0) {
if (rtl) {
root_pos.x = get_size().width - root_pos.x;
parent_pos.x = get_size().width - parent_pos.x;
}
- RenderingServer::get_singleton()->canvas_item_add_line(ci, root_pos, Point2i(parent_pos.x - Math::floor(line_width / 2), root_pos.y), cache.relationship_line_color, line_width);
- RenderingServer::get_singleton()->canvas_item_add_line(ci, Point2i(parent_pos.x, root_pos.y), Point2i(parent_pos.x, prev_ofs), cache.relationship_line_color, line_width);
+
+ // Order of parts on this bend: the horizontal line first, then the vertical line.
+ if (_is_branch_selected(c)) {
+ // If this item or one of its children is selected, we draw the line using parent highlight style.
+ RenderingServer::get_singleton()->canvas_item_add_line(ci, root_pos, Point2i(parent_pos.x + Math::floor(parent_line_width / 2), root_pos.y), cache.parent_hl_line_color, parent_line_width);
+ RenderingServer::get_singleton()->canvas_item_add_line(ci, Point2i(parent_pos.x, root_pos.y + Math::floor(parent_line_width / 2)), Point2i(parent_pos.x, prev_hl_ofs), cache.parent_hl_line_color, parent_line_width);
+
+ more_prev_ofs = cache.parent_hl_line_margin;
+ prev_hl_ofs = root_pos.y + Math::floor(parent_line_width / 2);
+ } else if (p_item->is_selected(0)) {
+ // If parent item is selected (but this item is not), we draw the line using children highlight style.
+ // Siblings of the selected branch can be drawn with a slight offset and their vertical line must appear as highlighted.
+ if (_is_sibling_branch_selected(c)) {
+ RenderingServer::get_singleton()->canvas_item_add_line(ci, root_pos, Point2i(parent_pos.x + Math::floor(parent_line_width / 2), root_pos.y), cache.children_hl_line_color, children_line_width);
+ RenderingServer::get_singleton()->canvas_item_add_line(ci, Point2i(parent_pos.x, root_pos.y + Math::floor(parent_line_width / 2)), Point2i(parent_pos.x, prev_hl_ofs), cache.parent_hl_line_color, parent_line_width);
+
+ prev_hl_ofs = root_pos.y + Math::floor(parent_line_width / 2);
+ } else {
+ RenderingServer::get_singleton()->canvas_item_add_line(ci, root_pos, Point2i(parent_pos.x + Math::floor(children_line_width / 2), root_pos.y), cache.children_hl_line_color, children_line_width);
+ RenderingServer::get_singleton()->canvas_item_add_line(ci, Point2i(parent_pos.x, root_pos.y + Math::floor(children_line_width / 2)), Point2i(parent_pos.x, prev_ofs + Math::floor(children_line_width / 2)), cache.children_hl_line_color, children_line_width);
+ }
+ } else {
+ // If nothing of the above is true, we draw the line using normal style.
+ // Siblings of the selected branch can be drawn with a slight offset and their vertical line must appear as highlighted.
+ if (_is_sibling_branch_selected(c)) {
+ RenderingServer::get_singleton()->canvas_item_add_line(ci, root_pos, Point2i(parent_pos.x + cache.parent_hl_line_margin, root_pos.y), cache.relationship_line_color, line_width);
+ RenderingServer::get_singleton()->canvas_item_add_line(ci, Point2i(parent_pos.x, root_pos.y + Math::floor(parent_line_width / 2)), Point2i(parent_pos.x, prev_hl_ofs), cache.parent_hl_line_color, parent_line_width);
+
+ prev_hl_ofs = root_pos.y + Math::floor(parent_line_width / 2);
+ } else {
+ RenderingServer::get_singleton()->canvas_item_add_line(ci, root_pos, Point2i(parent_pos.x + Math::floor(line_width / 2), root_pos.y), cache.relationship_line_color, line_width);
+ RenderingServer::get_singleton()->canvas_item_add_line(ci, Point2i(parent_pos.x, root_pos.y + Math::floor(line_width / 2)), Point2i(parent_pos.x, prev_ofs + Math::floor(line_width / 2)), cache.relationship_line_color, line_width);
+ }
+ }
}
if (htotal < 0) {
return -1;
}
- prev_ofs = root_pos.y;
+ prev_ofs = root_pos.y + more_prev_ofs;
}
if (htotal >= 0) {
@@ -1853,10 +1902,10 @@ int Tree::draw_item(const Point2i &p_pos, const Point2 &p_draw_ofs, const Size2
if (child_h < 0) {
if (cache.draw_relationship_lines == 0) {
return -1; // break, stop drawing, no need to anymore
- } else {
- htotal = -1;
- children_pos.y = cache.offset.y + p_draw_size.height;
}
+
+ htotal = -1;
+ children_pos.y = cache.offset.y + p_draw_size.height;
} else {
htotal += child_h;
children_pos.y += child_h;
@@ -1889,6 +1938,36 @@ int Tree::_count_selected_items(TreeItem *p_from) const {
return count;
}
+bool Tree::_is_branch_selected(TreeItem *p_from) const {
+ for (int i = 0; i < columns.size(); i++) {
+ if (p_from->is_selected(i)) {
+ return true;
+ }
+ }
+
+ TreeItem *child_item = p_from->get_first_child();
+ while (child_item) {
+ if (_is_branch_selected(child_item)) {
+ return true;
+ }
+ child_item = child_item->get_next();
+ }
+
+ return false;
+}
+
+bool Tree::_is_sibling_branch_selected(TreeItem *p_from) const {
+ TreeItem *sibling_item = p_from->get_next();
+ while (sibling_item) {
+ if (_is_branch_selected(sibling_item)) {
+ return true;
+ }
+ sibling_item = sibling_item->get_next();
+ }
+
+ return false;
+}
+
void Tree::select_single_item(TreeItem *p_selected, TreeItem *p_current, int p_col, TreeItem *p_prev, bool *r_in_range, bool p_force_deselect) {
TreeItem::Cell &selected_cell = p_selected->cells.write[p_col];
@@ -4386,7 +4465,7 @@ void Tree::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "allow_rmb_select"), "set_allow_rmb_select", "get_allow_rmb_select");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "hide_folding"), "set_hide_folding", "is_folding_hidden");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "hide_root"), "set_hide_root", "is_root_hidden");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "drop_mode_flags", PROPERTY_HINT_FLAGS, "On Item,In between"), "set_drop_mode_flags", "get_drop_mode_flags");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "drop_mode_flags", PROPERTY_HINT_FLAGS, "On Item,In Between"), "set_drop_mode_flags", "get_drop_mode_flags");
ADD_PROPERTY(PropertyInfo(Variant::INT, "select_mode", PROPERTY_HINT_ENUM, "Single,Row,Multi"), "set_select_mode", "get_select_mode");
ADD_SIGNAL(MethodInfo("item_selected"));
diff --git a/scene/gui/tree.h b/scene/gui/tree.h
index 9dbfd93082..c0948f1b80 100644
--- a/scene/gui/tree.h
+++ b/scene/gui/tree.h
@@ -494,6 +494,8 @@ private:
Color guide_color;
Color drop_position_color;
Color relationship_line_color;
+ Color parent_hl_line_color;
+ Color children_hl_line_color;
Color custom_button_font_highlight;
int hseparation = 0;
@@ -502,6 +504,10 @@ private:
int button_margin = 0;
Point2 offset;
int draw_relationship_lines = 0;
+ int relationship_line_width = 0;
+ int parent_hl_line_width = 0;
+ int children_hl_line_width = 0;
+ int parent_hl_line_margin = 0;
int draw_guides = 0;
int scroll_border = 0;
int scroll_speed = 0;
@@ -574,6 +580,8 @@ private:
bool hide_folding = false;
int _count_selected_items(TreeItem *p_from) const;
+ bool _is_branch_selected(TreeItem *p_from) const;
+ bool _is_sibling_branch_selected(TreeItem *p_from) const;
void _go_left();
void _go_right();
void _go_down();
diff --git a/scene/main/canvas_item.cpp b/scene/main/canvas_item.cpp
index d594c6c8b6..181fe606c8 100644
--- a/scene/main/canvas_item.cpp
+++ b/scene/main/canvas_item.cpp
@@ -271,7 +271,7 @@ void CanvasItemMaterial::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_particles_anim_loop", "loop"), &CanvasItemMaterial::set_particles_anim_loop);
ClassDB::bind_method(D_METHOD("get_particles_anim_loop"), &CanvasItemMaterial::get_particles_anim_loop);
- ADD_PROPERTY(PropertyInfo(Variant::INT, "blend_mode", PROPERTY_HINT_ENUM, "Mix,Add,Sub,Mul,Premult Alpha"), "set_blend_mode", "get_blend_mode");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "blend_mode", PROPERTY_HINT_ENUM, "Mix,Add,Subtract,Multiply,Premultiplied Alpha"), "set_blend_mode", "get_blend_mode");
ADD_PROPERTY(PropertyInfo(Variant::INT, "light_mode", PROPERTY_HINT_ENUM, "Normal,Unshaded,Light Only"), "set_light_mode", "get_light_mode");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "particles_animation"), "set_particles_animation", "get_particles_animation");
@@ -1554,7 +1554,7 @@ void CanvasTexture::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::COLOR, "specular_color", PROPERTY_HINT_COLOR_NO_ALPHA), "set_specular_color", "get_specular_color");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "specular_shininess", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_specular_shininess", "get_specular_shininess");
ADD_GROUP("Texture", "texture_");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "texture_filter", PROPERTY_HINT_ENUM, "Inherit,Nearest,Linear,MipmapNearest,MipmapLinear,MipmapNearestAniso,MipmapLinearAniso"), "set_texture_filter", "get_texture_filter");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "texture_filter", PROPERTY_HINT_ENUM, "Inherit,Nearest,Linear,Nearest Mipmap,Linear Mipmap,Nearest Mipmap Aniso.,Linear Mipmap Aniso."), "set_texture_filter", "get_texture_filter");
ADD_PROPERTY(PropertyInfo(Variant::INT, "texture_repeat", PROPERTY_HINT_ENUM, "Inherit,Disabled,Enabled,Mirror"), "set_texture_repeat", "get_texture_repeat");
}
diff --git a/scene/main/http_request.cpp b/scene/main/http_request.cpp
index 884696d58d..927b114fbc 100644
--- a/scene/main/http_request.cpp
+++ b/scene/main/http_request.cpp
@@ -375,17 +375,19 @@ bool HTTPRequest::_update_connection() {
}
PackedByteArray chunk = client->read_response_body_chunk();
- downloaded.add(chunk.size());
- if (file) {
- const uint8_t *r = chunk.ptr();
- file->store_buffer(r, chunk.size());
- if (file->get_error() != OK) {
- call_deferred("_request_done", RESULT_DOWNLOAD_FILE_WRITE_ERROR, response_code, response_headers, PackedByteArray());
- return true;
+ if (chunk.size()) {
+ downloaded.add(chunk.size());
+ if (file) {
+ const uint8_t *r = chunk.ptr();
+ file->store_buffer(r, chunk.size());
+ if (file->get_error() != OK) {
+ call_deferred("_request_done", RESULT_DOWNLOAD_FILE_WRITE_ERROR, response_code, response_headers, PackedByteArray());
+ return true;
+ }
+ } else {
+ body.append_array(chunk);
}
- } else {
- body.append_array(chunk);
}
if (body_size_limit >= 0 && downloaded.get() > body_size_limit) {
diff --git a/scene/main/node.cpp b/scene/main/node.cpp
index c90d3e4a32..dc7057f339 100644
--- a/scene/main/node.cpp
+++ b/scene/main/node.cpp
@@ -2872,7 +2872,7 @@ void Node::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "custom_multiplayer", PROPERTY_HINT_RESOURCE_TYPE, "MultiplayerAPI", 0), "set_custom_multiplayer", "get_custom_multiplayer");
ADD_GROUP("Process", "process_");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "process_mode", PROPERTY_HINT_ENUM, "Inherit,Pausable,WhenPaused,Always,Disabled"), "set_process_mode", "get_process_mode");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "process_mode", PROPERTY_HINT_ENUM, "Inherit,Pausable,When Paused,Always,Disabled"), "set_process_mode", "get_process_mode");
ADD_PROPERTY(PropertyInfo(Variant::INT, "process_priority"), "set_process_priority", "get_process_priority");
ADD_GROUP("Editor Description", "editor_");
diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp
index 62e1223fb0..77667cf188 100644
--- a/scene/main/viewport.cpp
+++ b/scene/main/viewport.cpp
@@ -3601,8 +3601,8 @@ void Viewport::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "snap_2d_transforms_to_pixel"), "set_snap_2d_transforms_to_pixel", "is_snap_2d_transforms_to_pixel_enabled");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "snap_2d_vertices_to_pixel"), "set_snap_2d_vertices_to_pixel", "is_snap_2d_vertices_to_pixel_enabled");
ADD_GROUP("Rendering", "");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "msaa", PROPERTY_HINT_ENUM, "Disabled,2x,4x,8x,16x,AndroidVR 2x,AndroidVR 4x"), "set_msaa", "get_msaa");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "screen_space_aa", PROPERTY_HINT_ENUM, "Disabled,FXAA"), "set_screen_space_aa", "get_screen_space_aa");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "msaa", PROPERTY_HINT_ENUM, "Disabled (Fastest),2x (Fast),4x (Average),8x (Slow),16x (Slower)"), "set_msaa", "get_msaa");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "screen_space_aa", PROPERTY_HINT_ENUM, "Disabled (Fastest),FXAA (Fast)"), "set_screen_space_aa", "get_screen_space_aa");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_debanding"), "set_use_debanding", "is_using_debanding");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_occlusion_culling"), "set_use_occlusion_culling", "is_using_occlusion_culling");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "lod_threshold", PROPERTY_HINT_RANGE, "0,1024,0.1"), "set_lod_threshold", "get_lod_threshold");
diff --git a/scene/main/window.cpp b/scene/main/window.cpp
index b7bc2a83c5..d793be1869 100644
--- a/scene/main/window.cpp
+++ b/scene/main/window.cpp
@@ -1444,7 +1444,7 @@ void Window::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::STRING, "title"), "set_title", "get_title");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2I, "position"), "set_position", "get_position");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2I, "size"), "set_size", "get_size");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "mode", PROPERTY_HINT_ENUM, "Windowed,Minimized,Maximized,FullScreen"), "set_mode", "get_mode");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "mode", PROPERTY_HINT_ENUM, "Windowed,Minimized,Maximized,Fullscreen"), "set_mode", "get_mode");
ADD_PROPERTY(PropertyInfo(Variant::STRING, "current_screen"), "set_current_screen", "get_current_screen");
ADD_GROUP("Flags", "");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "visible"), "set_visible", "is_visible");
@@ -1461,8 +1461,8 @@ void Window::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2I, "max_size"), "set_max_size", "get_max_size");
ADD_GROUP("Content Scale", "content_scale_");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2I, "content_scale_size"), "set_content_scale_size", "get_content_scale_size");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "content_scale_mode", PROPERTY_HINT_ENUM, "Disabled,CanvasItems,Viewport"), "set_content_scale_mode", "get_content_scale_mode");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "content_scale_aspect", PROPERTY_HINT_ENUM, "Ignore,Keep,KeepWidth,KeepHeight,Expand"), "set_content_scale_aspect", "get_content_scale_aspect");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "content_scale_mode", PROPERTY_HINT_ENUM, "Disabled,Canvas Items,Viewport"), "set_content_scale_mode", "get_content_scale_mode");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "content_scale_aspect", PROPERTY_HINT_ENUM, "Ignore,Keep,Keep Width,Keep Height,Expand"), "set_content_scale_aspect", "get_content_scale_aspect");
ADD_GROUP("Theme", "theme_");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "theme", PROPERTY_HINT_RESOURCE_TYPE, "Theme"), "set_theme", "get_theme");
ADD_PROPERTY(PropertyInfo(Variant::STRING, "theme_custom_type"), "set_theme_custom_type", "get_theme_custom_type");
diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp
index b9c1659fd1..e8ee4cb9fc 100644
--- a/scene/register_scene_types.cpp
+++ b/scene/register_scene_types.cpp
@@ -597,6 +597,7 @@ void register_scene_types() {
ClassDB::register_class<VisualShaderNodeIs>();
ClassDB::register_class<VisualShaderNodeCompare>();
ClassDB::register_class<VisualShaderNodeMultiplyAdd>();
+ ClassDB::register_class<VisualShaderNodeBillboard>();
ClassDB::register_class<VisualShaderNodeSDFToScreenUV>();
ClassDB::register_class<VisualShaderNodeScreenUVToSDF>();
diff --git a/scene/resources/default_theme/default_theme.cpp b/scene/resources/default_theme/default_theme.cpp
index 6e67daf15f..3a5b3534c6 100644
--- a/scene/resources/default_theme/default_theme.cpp
+++ b/scene/resources/default_theme/default_theme.cpp
@@ -719,6 +719,8 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_color("guide_color", "Tree", Color(0, 0, 0, 0.1));
theme->set_color("drop_position_color", "Tree", Color(1, 0.3, 0.2));
theme->set_color("relationship_line_color", "Tree", Color(0.27, 0.27, 0.27));
+ theme->set_color("parent_hl_line_color", "Tree", Color(0.27, 0.27, 0.27));
+ theme->set_color("children_hl_line_color", "Tree", Color(0.27, 0.27, 0.27));
theme->set_color("custom_button_font_highlight", "Tree", control_font_hover_color);
theme->set_constant("hseparation", "Tree", 4 * scale);
@@ -726,6 +728,10 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
theme->set_constant("item_margin", "Tree", 12 * scale);
theme->set_constant("button_margin", "Tree", 4 * scale);
theme->set_constant("draw_relationship_lines", "Tree", 0);
+ theme->set_constant("relationship_line_width", "Tree", 1);
+ theme->set_constant("parent_hl_line_width", "Tree", 1);
+ theme->set_constant("children_hl_line_width", "Tree", 1);
+ theme->set_constant("parent_hl_line_margin", "Tree", 0);
theme->set_constant("draw_guides", "Tree", 1);
theme->set_constant("scroll_border", "Tree", 4);
theme->set_constant("scroll_speed", "Tree", 12);
diff --git a/scene/resources/material.cpp b/scene/resources/material.cpp
index 75dd417f7f..100fccc783 100644
--- a/scene/resources/material.cpp
+++ b/scene/resources/material.cpp
@@ -1760,7 +1760,8 @@ void BaseMaterial3D::_validate_property(PropertyInfo &property) const {
if (orm) {
if (property.name == "shading_mode") {
- property.hint_string = "Unshaded,PerPixel"; //vertex not supported in ORM mode, since no individual roughness.
+ // Vertex not supported in ORM mode, since no individual roughness.
+ property.hint_string = "Unshaded,Per-Pixel";
}
if (property.name.begins_with("roughness") || property.name.begins_with("metallic") || property.name.begins_with("ao_texture")) {
property.usage = 0;
@@ -2387,18 +2388,18 @@ void BaseMaterial3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_distance_fade_min_distance"), &BaseMaterial3D::get_distance_fade_min_distance);
ADD_GROUP("Transparency", "");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "transparency", PROPERTY_HINT_ENUM, "Disabled,Alpha,Alpha Scissor,Alpha Hash,Depth PrePass"), "set_transparency", "get_transparency");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "transparency", PROPERTY_HINT_ENUM, "Disabled,Alpha,Alpha Scissor,Alpha Hash,Depth Pre-Pass"), "set_transparency", "get_transparency");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "alpha_scissor_threshold", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_alpha_scissor_threshold", "get_alpha_scissor_threshold");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "alpha_hash_scale", PROPERTY_HINT_RANGE, "0,2,0.01"), "set_alpha_hash_scale", "get_alpha_hash_scale");
ADD_PROPERTY(PropertyInfo(Variant::INT, "alpha_antialiasing_mode", PROPERTY_HINT_ENUM, "Disabled,Alpha Edge Blend,Alpha Edge Clip"), "set_alpha_antialiasing", "get_alpha_antialiasing");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "alpha_antialiasing_edge", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_alpha_antialiasing_edge", "get_alpha_antialiasing_edge");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "blend_mode", PROPERTY_HINT_ENUM, "Mix,Add,Sub,Mul"), "set_blend_mode", "get_blend_mode");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "blend_mode", PROPERTY_HINT_ENUM, "Mix,Add,Subtract,Multiply"), "set_blend_mode", "get_blend_mode");
ADD_PROPERTY(PropertyInfo(Variant::INT, "cull_mode", PROPERTY_HINT_ENUM, "Back,Front,Disabled"), "set_cull_mode", "get_cull_mode");
ADD_PROPERTY(PropertyInfo(Variant::INT, "depth_draw_mode", PROPERTY_HINT_ENUM, "Opaque Only,Always,Never"), "set_depth_draw_mode", "get_depth_draw_mode");
ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "no_depth_test"), "set_flag", "get_flag", FLAG_DISABLE_DEPTH_TEST);
ADD_GROUP("Shading", "");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "shading_mode", PROPERTY_HINT_ENUM, "Unshaded,PerPixel,PerVertex"), "set_shading_mode", "get_shading_mode");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "shading_mode", PROPERTY_HINT_ENUM, "Unshaded,Per-Pixel,Per-Vertex"), "set_shading_mode", "get_shading_mode");
ADD_PROPERTY(PropertyInfo(Variant::INT, "diffuse_mode", PROPERTY_HINT_ENUM, "Burley,Lambert,Lambert Wrap,Oren Nayar,Toon"), "set_diffuse_mode", "get_diffuse_mode");
ADD_PROPERTY(PropertyInfo(Variant::INT, "specular_mode", PROPERTY_HINT_ENUM, "SchlickGGX,Blinn,Phong,Toon,Disabled"), "set_specular_mode", "get_specular_mode");
ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "disable_ambient_light"), "set_flag", "get_flag", FLAG_DISABLE_AMBIENT_LIGHT);
@@ -2502,7 +2503,7 @@ void BaseMaterial3D::_bind_methods() {
ADD_GROUP("Detail", "detail_");
ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "detail_enabled"), "set_feature", "get_feature", FEATURE_DETAIL);
ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "detail_mask", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_texture", "get_texture", TEXTURE_DETAIL_MASK);
- ADD_PROPERTY(PropertyInfo(Variant::INT, "detail_blend_mode", PROPERTY_HINT_ENUM, "Mix,Add,Sub,Mul"), "set_detail_blend_mode", "get_detail_blend_mode");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "detail_blend_mode", PROPERTY_HINT_ENUM, "Mix,Add,Subtract,Multiply"), "set_detail_blend_mode", "get_detail_blend_mode");
ADD_PROPERTY(PropertyInfo(Variant::INT, "detail_uv_layer", PROPERTY_HINT_ENUM, "UV1,UV2"), "set_detail_uv", "get_detail_uv");
ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "detail_albedo", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_texture", "get_texture", TEXTURE_DETAIL_ALBEDO);
ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "detail_normal", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_texture", "get_texture", TEXTURE_DETAIL_NORMAL);
diff --git a/scene/resources/particles_material.cpp b/scene/resources/particles_material.cpp
index a965317c81..60d5566f08 100644
--- a/scene/resources/particles_material.cpp
+++ b/scene/resources/particles_material.cpp
@@ -1316,7 +1316,7 @@ void ParticlesMaterial::_bind_methods() {
ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "anim_offset_curve", PROPERTY_HINT_RESOURCE_TYPE, "CurveTexture"), "set_param_texture", "get_param_texture", PARAM_ANIM_OFFSET);
ADD_GROUP("Sub Emitter", "sub_emitter_");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "sub_emitter_mode", PROPERTY_HINT_ENUM, "Disabled,Constant,AtEnd,AtCollision"), "set_sub_emitter_mode", "get_sub_emitter_mode");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "sub_emitter_mode", PROPERTY_HINT_ENUM, "Disabled,Constant,At End,At Collision"), "set_sub_emitter_mode", "get_sub_emitter_mode");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "sub_emitter_frequency", PROPERTY_HINT_RANGE, "0.01,100,0.01"), "set_sub_emitter_frequency", "get_sub_emitter_frequency");
ADD_PROPERTY(PropertyInfo(Variant::INT, "sub_emitter_amount_at_end", PROPERTY_HINT_RANGE, "1,32,1"), "set_sub_emitter_amount_at_end", "get_sub_emitter_amount_at_end");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "sub_emitter_keep_velocity"), "set_sub_emitter_keep_velocity", "get_sub_emitter_keep_velocity");
diff --git a/scene/resources/sky_material.cpp b/scene/resources/sky_material.cpp
index f50ee9c4c8..9d79c22159 100644
--- a/scene/resources/sky_material.cpp
+++ b/scene/resources/sky_material.cpp
@@ -193,7 +193,6 @@ ProceduralSkyMaterial::ProceduralSkyMaterial() {
code += "uniform float ground_energy = 1.0;\n\n";
code += "uniform float sun_angle_max = 1.74;\n";
code += "uniform float sun_curve : hint_range(0, 1) = 0.05;\n\n";
- code += "const float PI = 3.1415926535897932384626433833;\n\n";
code += "void sky() {\n";
code += "\tfloat v_angle = acos(clamp(EYEDIR.y, -1.0, 1.0));\n";
code += "\tfloat c = (1.0 - v_angle / (PI * 0.5));\n";
@@ -499,7 +498,6 @@ PhysicalSkyMaterial::PhysicalSkyMaterial() {
code += "uniform sampler2D night_sky : hint_black;";
- code += "const float PI = 3.141592653589793238462643383279502884197169;\n";
code += "const vec3 UP = vec3( 0.0, 1.0, 0.0 );\n\n";
code += "// Sun constants\n";
diff --git a/scene/resources/text_file.cpp b/scene/resources/text_file.cpp
index b71909b6bb..564c65adb9 100644
--- a/scene/resources/text_file.cpp
+++ b/scene/resources/text_file.cpp
@@ -55,7 +55,7 @@ Error TextFile::load_text(const String &p_path) {
ERR_FAIL_COND_V_MSG(err, err, "Cannot open TextFile '" + p_path + "'.");
- uint64_t len = f->get_len();
+ uint64_t len = f->get_length();
sourcef.resize(len + 1);
uint8_t *w = sourcef.ptrw();
uint64_t r = f->get_buffer(w, len);
diff --git a/scene/resources/text_line.cpp b/scene/resources/text_line.cpp
index 925867a1f2..f1eff6e84f 100644
--- a/scene/resources/text_line.cpp
+++ b/scene/resources/text_line.cpp
@@ -74,7 +74,7 @@ void TextLine::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_flags", "flags"), &TextLine::set_flags);
ClassDB::bind_method(D_METHOD("get_flags"), &TextLine::get_flags);
- ADD_PROPERTY(PropertyInfo(Variant::INT, "flags", PROPERTY_HINT_FLAGS, "Kashida justification,Word justification,Trim edge spaces after justification,Justification only after last tab"), "set_flags", "get_flags");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "flags", PROPERTY_HINT_FLAGS, "Kashida Justify,Word Justify,Trim Edge Spaces After Justify,Justify Only After Last Tab"), "set_flags", "get_flags");
ClassDB::bind_method(D_METHOD("get_objects"), &TextLine::get_objects);
ClassDB::bind_method(D_METHOD("get_object_rect", "key"), &TextLine::get_object_rect);
diff --git a/scene/resources/text_paragraph.cpp b/scene/resources/text_paragraph.cpp
index 341f5abd80..958c94fe31 100644
--- a/scene/resources/text_paragraph.cpp
+++ b/scene/resources/text_paragraph.cpp
@@ -72,7 +72,7 @@ void TextParagraph::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_flags", "flags"), &TextParagraph::set_flags);
ClassDB::bind_method(D_METHOD("get_flags"), &TextParagraph::get_flags);
- ADD_PROPERTY(PropertyInfo(Variant::INT, "flags", PROPERTY_HINT_FLAGS, "Kashida justification,Word justification,Trim edge spaces after justification,Justification only after last tab,Break mandatory,Break words,Break graphemes"), "set_flags", "get_flags");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "flags", PROPERTY_HINT_FLAGS, "Kashida Justify,Word Justify,Trim Edge Spaces After Justify,Justify Only After Last Tab,Break Mandatory,Break Words,Break Graphemes"), "set_flags", "get_flags");
ClassDB::bind_method(D_METHOD("set_width", "width"), &TextParagraph::set_width);
ClassDB::bind_method(D_METHOD("get_width"), &TextParagraph::get_width);
diff --git a/scene/resources/texture.cpp b/scene/resources/texture.cpp
index 1b2176d30a..4475179431 100644
--- a/scene/resources/texture.cpp
+++ b/scene/resources/texture.cpp
@@ -490,7 +490,7 @@ Image::Format StreamTexture2D::get_format() const {
return format;
}
-Error StreamTexture2D::_load_data(const String &p_path, int &tw, int &th, int &tw_custom, int &th_custom, Ref<Image> &image, bool &r_request_3d, bool &r_request_normal, bool &r_request_roughness, int &mipmap_limit, int p_size_limit) {
+Error StreamTexture2D::_load_data(const String &p_path, int &r_width, int &r_height, Ref<Image> &image, bool &r_request_3d, bool &r_request_normal, bool &r_request_roughness, int &mipmap_limit, int p_size_limit) {
alpha_cache.unref();
ERR_FAIL_COND_V(image.is_null(), ERR_INVALID_PARAMETER);
@@ -511,8 +511,8 @@ Error StreamTexture2D::_load_data(const String &p_path, int &tw, int &th, int &t
memdelete(f);
ERR_FAIL_V_MSG(ERR_FILE_CORRUPT, "Stream texture file is too new.");
}
- tw_custom = f->get_32();
- th_custom = f->get_32();
+ r_width = f->get_32();
+ r_height = f->get_32();
uint32_t df = f->get_32(); //data format
//skip reserved
@@ -551,7 +551,7 @@ Error StreamTexture2D::_load_data(const String &p_path, int &tw, int &th, int &t
}
Error StreamTexture2D::load(const String &p_path) {
- int lw, lh, lwc, lhc;
+ int lw, lh;
Ref<Image> image;
image.instance();
@@ -560,7 +560,7 @@ Error StreamTexture2D::load(const String &p_path) {
bool request_roughness;
int mipmap_limit;
- Error err = _load_data(p_path, lw, lh, lwc, lhc, image, request_3d, request_normal, request_roughness, mipmap_limit);
+ Error err = _load_data(p_path, lw, lh, image, request_3d, request_normal, request_roughness, mipmap_limit);
if (err) {
return err;
}
@@ -571,12 +571,12 @@ Error StreamTexture2D::load(const String &p_path) {
} else {
texture = RS::get_singleton()->texture_2d_create(image);
}
- if (lwc || lhc) {
- RS::get_singleton()->texture_set_size_override(texture, lwc, lhc);
+ if (lw || lh) {
+ RS::get_singleton()->texture_set_size_override(texture, lw, lh);
}
- w = lwc ? lwc : lw;
- h = lhc ? lhc : lh;
+ w = lw;
+ h = lh;
path_to_file = p_path;
format = image->get_format();
diff --git a/scene/resources/texture.h b/scene/resources/texture.h
index 264d85d187..df8c00f8ff 100644
--- a/scene/resources/texture.h
+++ b/scene/resources/texture.h
@@ -158,7 +158,7 @@ public:
};
private:
- Error _load_data(const String &p_path, int &tw, int &th, int &tw_custom, int &th_custom, Ref<Image> &image, bool &r_request_3d, bool &r_request_normal, bool &r_request_roughness, int &mipmap_limit, int p_size_limit = 0);
+ Error _load_data(const String &p_path, int &r_width, int &r_height, Ref<Image> &image, bool &r_request_3d, bool &r_request_normal, bool &r_request_roughness, int &mipmap_limit, int p_size_limit = 0);
String path_to_file;
mutable RID texture;
Image::Format format = Image::FORMAT_MAX;
diff --git a/scene/resources/tile_set.cpp b/scene/resources/tile_set.cpp
index 9db7acb263..2c2c8ea0e8 100644
--- a/scene/resources/tile_set.cpp
+++ b/scene/resources/tile_set.cpp
@@ -1290,7 +1290,7 @@ void TileSet::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_tile_skew", "skew"), &TileSet::set_tile_skew);
ClassDB::bind_method(D_METHOD("get_tile_skew"), &TileSet::get_tile_skew);
- ADD_PROPERTY(PropertyInfo(Variant::INT, "tile_shape", PROPERTY_HINT_ENUM, "Square,Isometric,Half-offset square,Hexagon"), "set_tile_shape", "get_tile_shape");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "tile_shape", PROPERTY_HINT_ENUM, "Square,Isometric,Half-Offset Square,Hexagon"), "set_tile_shape", "get_tile_shape");
ADD_PROPERTY(PropertyInfo(Variant::INT, "tile_layout", PROPERTY_HINT_ENUM, "Stacked,Stacked Offset,Stairs Right,Stairs Down,Diamond Right,Diamond Down"), "set_tile_layout", "get_tile_layout");
ADD_PROPERTY(PropertyInfo(Variant::INT, "tile_offset_axis", PROPERTY_HINT_ENUM, "Horizontal Offset,Vertical Offset"), "set_tile_offset_axis", "get_tile_offset_axis");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2I, "tile_size"), "set_tile_size", "get_tile_size");
@@ -1407,9 +1407,11 @@ TileSet::TileSet() {
}
TileSet::~TileSet() {
+#ifndef DISABLE_DEPRECATED
for (Map<int, CompatibilityTileData *>::Element *E = compatibility_data.front(); E; E = E->next()) {
memdelete(E->get());
}
+#endif // DISABLE_DEPRECATED
while (!source_ids.is_empty()) {
remove_source(source_ids[0]);
}
@@ -4295,7 +4297,23 @@ void TileSetPluginAtlasPhysics::draw_quadrant_debug(TileMap *p_tile_map, TileMap
Ref<TileSet> tile_set = p_tile_map->get_tileset();
ERR_FAIL_COND(!tile_set.is_valid());
- if (!p_tile_map->get_tree() || !(Engine::get_singleton()->is_editor_hint() || p_tile_map->get_tree()->is_debugging_collisions_hint())) {
+ if (!p_tile_map->get_tree()) {
+ return;
+ }
+
+ bool show_collision = false;
+ switch (p_tile_map->get_collision_visibility_mode()) {
+ case TileMap::VISIBILITY_MODE_DEFAULT:
+ show_collision = !Engine::get_singleton()->is_editor_hint() && (p_tile_map->get_tree() && p_tile_map->get_tree()->is_debugging_navigation_hint());
+ break;
+ case TileMap::VISIBILITY_MODE_FORCE_HIDE:
+ show_collision = false;
+ break;
+ case TileMap::VISIBILITY_MODE_FORCE_SHOW:
+ show_collision = true;
+ break;
+ }
+ if (!show_collision) {
return;
}
@@ -4454,7 +4472,23 @@ void TileSetPluginAtlasNavigation::draw_quadrant_debug(TileMap *p_tile_map, Tile
Ref<TileSet> tile_set = p_tile_map->get_tileset();
ERR_FAIL_COND(!tile_set.is_valid());
- if (!p_tile_map->get_tree() || !(Engine::get_singleton()->is_editor_hint() || p_tile_map->get_tree()->is_debugging_navigation_hint())) {
+ if (!p_tile_map->get_tree()) {
+ return;
+ }
+
+ bool show_navigation = false;
+ switch (p_tile_map->get_navigation_visibility_mode()) {
+ case TileMap::VISIBILITY_MODE_DEFAULT:
+ show_navigation = !Engine::get_singleton()->is_editor_hint() && (p_tile_map->get_tree() && p_tile_map->get_tree()->is_debugging_navigation_hint());
+ break;
+ case TileMap::VISIBILITY_MODE_FORCE_HIDE:
+ show_navigation = false;
+ break;
+ case TileMap::VISIBILITY_MODE_FORCE_SHOW:
+ show_navigation = true;
+ break;
+ }
+ if (!show_navigation) {
return;
}
diff --git a/scene/resources/visual_shader.cpp b/scene/resources/visual_shader.cpp
index da29f2ebd8..f37b1a3e8e 100644
--- a/scene/resources/visual_shader.cpp
+++ b/scene/resources/visual_shader.cpp
@@ -94,6 +94,52 @@ bool VisualShaderNode::is_generate_input_var(int p_port) const {
return true;
}
+bool VisualShaderNode::is_output_port_expandable(int p_port) const {
+ return false;
+}
+
+void VisualShaderNode::_set_output_ports_expanded(const Array &p_values) {
+ for (int i = 0; i < p_values.size(); i++) {
+ expanded_output_ports[p_values[i]] = true;
+ }
+ emit_changed();
+}
+
+Array VisualShaderNode::_get_output_ports_expanded() const {
+ Array arr;
+ for (int i = 0; i < get_output_port_count(); i++) {
+ if (_is_output_port_expanded(i)) {
+ arr.push_back(i);
+ }
+ }
+ return arr;
+}
+
+void VisualShaderNode::_set_output_port_expanded(int p_port, bool p_expanded) {
+ expanded_output_ports[p_port] = p_expanded;
+ emit_changed();
+}
+
+bool VisualShaderNode::_is_output_port_expanded(int p_port) const {
+ if (expanded_output_ports.has(p_port)) {
+ return expanded_output_ports[p_port];
+ }
+ return false;
+}
+
+int VisualShaderNode::get_expanded_output_port_count() const {
+ int count = get_output_port_count();
+ int count2 = count;
+ for (int i = 0; i < count; i++) {
+ if (is_output_port_expandable(i) && _is_output_port_expanded(i)) {
+ if (get_output_port_type(i) == PORT_TYPE_VECTOR) {
+ count2 += 3;
+ }
+ }
+ }
+ return count2;
+}
+
bool VisualShaderNode::is_code_generated() const {
return true;
}
@@ -106,6 +152,14 @@ bool VisualShaderNode::is_use_prop_slots() const {
return false;
}
+bool VisualShaderNode::is_disabled() const {
+ return disabled;
+}
+
+void VisualShaderNode::set_disabled(bool p_disabled) {
+ disabled = p_disabled;
+}
+
Vector<VisualShader::DefaultTextureParam> VisualShaderNode::get_default_texture_parameters(VisualShader::Type p_type, int p_id) const {
return Vector<VisualShader::DefaultTextureParam>();
}
@@ -157,6 +211,12 @@ void VisualShaderNode::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_output_port_for_preview", "port"), &VisualShaderNode::set_output_port_for_preview);
ClassDB::bind_method(D_METHOD("get_output_port_for_preview"), &VisualShaderNode::get_output_port_for_preview);
+ ClassDB::bind_method(D_METHOD("_set_output_port_expanded", "port"), &VisualShaderNode::_set_output_port_expanded);
+ ClassDB::bind_method(D_METHOD("_is_output_port_expanded"), &VisualShaderNode::_is_output_port_expanded);
+
+ ClassDB::bind_method(D_METHOD("_set_output_ports_expanded", "values"), &VisualShaderNode::_set_output_ports_expanded);
+ ClassDB::bind_method(D_METHOD("_get_output_ports_expanded"), &VisualShaderNode::_get_output_ports_expanded);
+
ClassDB::bind_method(D_METHOD("set_input_port_default_value", "port", "value"), &VisualShaderNode::set_input_port_default_value);
ClassDB::bind_method(D_METHOD("get_input_port_default_value", "port"), &VisualShaderNode::get_input_port_default_value);
@@ -165,6 +225,7 @@ void VisualShaderNode::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::INT, "output_port_for_preview"), "set_output_port_for_preview", "get_output_port_for_preview");
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "default_input_values", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "set_default_input_values", "get_default_input_values");
+ ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "expanded_output_ports", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_output_ports_expanded", "_get_output_ports_expanded");
ADD_SIGNAL(MethodInfo("editor_refresh_request"));
BIND_ENUM_CONSTANT(PORT_TYPE_SCALAR);
@@ -576,7 +637,7 @@ bool VisualShader::can_connect_nodes(Type p_type, int p_from_node, int p_from_po
return false;
}
- if (p_from_port < 0 || p_from_port >= g->nodes[p_from_node].node->get_output_port_count()) {
+ if (p_from_port < 0 || p_from_port >= g->nodes[p_from_node].node->get_expanded_output_port_count()) {
return false;
}
@@ -617,7 +678,7 @@ void VisualShader::connect_nodes_forced(Type p_type, int p_from_node, int p_from
Graph *g = &graph[p_type];
ERR_FAIL_COND(!g->nodes.has(p_from_node));
- ERR_FAIL_INDEX(p_from_port, g->nodes[p_from_node].node->get_output_port_count());
+ ERR_FAIL_INDEX(p_from_port, g->nodes[p_from_node].node->get_expanded_output_port_count());
ERR_FAIL_COND(!g->nodes.has(p_to_node));
ERR_FAIL_INDEX(p_to_port, g->nodes[p_to_node].node->get_input_port_count());
@@ -639,7 +700,7 @@ Error VisualShader::connect_nodes(Type p_type, int p_from_node, int p_from_port,
Graph *g = &graph[p_type];
ERR_FAIL_COND_V(!g->nodes.has(p_from_node), ERR_INVALID_PARAMETER);
- ERR_FAIL_INDEX_V(p_from_port, g->nodes[p_from_node].node->get_output_port_count(), ERR_INVALID_PARAMETER);
+ ERR_FAIL_INDEX_V(p_from_port, g->nodes[p_from_node].node->get_expanded_output_port_count(), ERR_INVALID_PARAMETER);
ERR_FAIL_COND_V(!g->nodes.has(p_to_node), ERR_INVALID_PARAMETER);
ERR_FAIL_INDEX_V(p_to_port, g->nodes[p_to_node].node->get_input_port_count(), ERR_INVALID_PARAMETER);
@@ -790,7 +851,7 @@ bool VisualShader::is_text_shader() const {
String VisualShader::generate_preview_shader(Type p_type, int p_node, int p_port, Vector<DefaultTextureParam> &default_tex_params) const {
Ref<VisualShaderNode> node = get_node(p_type, p_node);
ERR_FAIL_COND_V(!node.is_valid(), String());
- ERR_FAIL_COND_V(p_port < 0 || p_port >= node->get_output_port_count(), String());
+ ERR_FAIL_COND_V(p_port < 0 || p_port >= node->get_expanded_output_port_count(), String());
ERR_FAIL_COND_V(node->get_output_port_type(p_port) == VisualShaderNode::PORT_TYPE_TRANSFORM, String());
StringBuilder global_code;
@@ -1207,6 +1268,12 @@ void VisualShader::_get_property_list(List<PropertyInfo> *p_list) const {
Error VisualShader::_write_node(Type type, StringBuilder &global_code, StringBuilder &global_code_per_node, Map<Type, StringBuilder> &global_code_per_func, StringBuilder &code, Vector<VisualShader::DefaultTextureParam> &def_tex_params, const VMap<ConnectionKey, const List<Connection>::Element *> &input_connections, const VMap<ConnectionKey, const List<Connection>::Element *> &output_connections, int node, Set<int> &processed, bool for_preview, Set<StringName> &r_classes) const {
const Ref<VisualShaderNode> vsnode = graph[type].nodes[node].node;
+ if (vsnode->is_disabled()) {
+ code += "// " + vsnode->get_caption() + ":" + itos(node) + "\n";
+ code += "\t// Node is disabled and code is not generated.\n";
+ return OK;
+ }
+
//check inputs recursively first
int input_count = vsnode->get_input_port_count();
for (int i = 0; i < input_count; i++) {
@@ -1275,6 +1342,11 @@ Error VisualShader::_write_node(Type type, StringBuilder &global_code, StringBui
if (input_connections.has(ck)) {
//connected to something, use that output
int from_node = input_connections[ck]->get().from_node;
+
+ if (graph[type].nodes[from_node].node->is_disabled()) {
+ continue;
+ }
+
int from_port = input_connections[ck]->get().from_port;
VisualShaderNode::PortType in_type = vsnode->get_input_port_type(i);
@@ -1362,13 +1434,30 @@ Error VisualShader::_write_node(Type type, StringBuilder &global_code, StringBui
}
int output_count = vsnode->get_output_port_count();
+ int initial_output_count = output_count;
+
+ Map<int, bool> expanded_output_ports;
+
+ for (int i = 0; i < initial_output_count; i++) {
+ bool expanded = false;
+
+ if (vsnode->is_output_port_expandable(i) && vsnode->_is_output_port_expanded(i)) {
+ expanded = true;
+
+ if (vsnode->get_output_port_type(i) == VisualShaderNode::PORT_TYPE_VECTOR) {
+ output_count += 3;
+ }
+ }
+ expanded_output_ports.insert(i, expanded);
+ }
+
Vector<String> output_vars;
- output_vars.resize(vsnode->get_output_port_count());
+ output_vars.resize(output_count);
String *outputs = output_vars.ptrw();
if (vsnode->is_simple_decl()) { // less code to generate for some simple_decl nodes
- for (int i = 0; i < output_count; i++) {
- String var_name = "n_out" + itos(node) + "p" + itos(i);
+ for (int i = 0, j = 0; i < initial_output_count; i++, j++) {
+ String var_name = "n_out" + itos(node) + "p" + itos(j);
switch (vsnode->get_output_port_type(i)) {
case VisualShaderNode::PORT_TYPE_SCALAR:
outputs[i] = "float " + var_name;
@@ -1388,35 +1477,84 @@ Error VisualShader::_write_node(Type type, StringBuilder &global_code, StringBui
default: {
}
}
+ if (expanded_output_ports[i]) {
+ if (vsnode->get_output_port_type(i) == VisualShaderNode::PORT_TYPE_VECTOR) {
+ j += 3;
+ }
+ }
}
} else {
- for (int i = 0; i < output_count; i++) {
- outputs[i] = "n_out" + itos(node) + "p" + itos(i);
+ for (int i = 0, j = 0; i < initial_output_count; i++, j++) {
+ outputs[i] = "n_out" + itos(node) + "p" + itos(j);
switch (vsnode->get_output_port_type(i)) {
case VisualShaderNode::PORT_TYPE_SCALAR:
- code += String() + "\tfloat " + outputs[i] + ";\n";
+ code += "\tfloat " + outputs[i] + ";\n";
break;
case VisualShaderNode::PORT_TYPE_SCALAR_INT:
- code += String() + "\tint " + outputs[i] + ";\n";
+ code += "\tint " + outputs[i] + ";\n";
break;
case VisualShaderNode::PORT_TYPE_VECTOR:
- code += String() + "\tvec3 " + outputs[i] + ";\n";
+ code += "\tvec3 " + outputs[i] + ";\n";
break;
case VisualShaderNode::PORT_TYPE_BOOLEAN:
- code += String() + "\tbool " + outputs[i] + ";\n";
+ code += "\tbool " + outputs[i] + ";\n";
break;
case VisualShaderNode::PORT_TYPE_TRANSFORM:
- code += String() + "\tmat4 " + outputs[i] + ";\n";
+ code += "\tmat4 " + outputs[i] + ";\n";
break;
default: {
}
}
+ if (expanded_output_ports[i]) {
+ if (vsnode->get_output_port_type(i) == VisualShaderNode::PORT_TYPE_VECTOR) {
+ j += 3;
+ }
+ }
}
}
code += vsnode->generate_code(get_mode(), type, node, inputs, outputs, for_preview);
+ for (int i = 0; i < output_count; i++) {
+ bool new_line_inserted = false;
+ if (expanded_output_ports[i]) {
+ if (vsnode->get_output_port_type(i) == VisualShaderNode::PORT_TYPE_VECTOR) {
+ if (vsnode->is_output_port_connected(i + 1) || (for_preview && vsnode->get_output_port_for_preview() == (i + 1))) { // red-component
+ if (!new_line_inserted) {
+ code += "\n";
+ new_line_inserted = true;
+ }
+ String r = "n_out" + itos(node) + "p" + itos(i + 1);
+ code += "\tfloat " + r + " = n_out" + itos(node) + "p" + itos(i) + ".r;\n";
+ outputs[i + 1] = r;
+ }
+
+ if (vsnode->is_output_port_connected(i + 2) || (for_preview && vsnode->get_output_port_for_preview() == (i + 2))) { // green-component
+ if (!new_line_inserted) {
+ code += "\n";
+ new_line_inserted = true;
+ }
+ String g = "n_out" + itos(node) + "p" + itos(i + 2);
+ code += "\tfloat " + g + " = n_out" + itos(node) + "p" + itos(i) + ".g;\n";
+ outputs[i + 2] = g;
+ }
+
+ if (vsnode->is_output_port_connected(i + 3) || (for_preview && vsnode->get_output_port_for_preview() == (i + 3))) { // blue-component
+ if (!new_line_inserted) {
+ code += "\n";
+ new_line_inserted = true;
+ }
+ String b = "n_out" + itos(node) + "p" + itos(i + 3);
+ code += "\tfloat " + b + " = n_out" + itos(node) + "p" + itos(i) + ".b;\n";
+ outputs[i + 3] = b;
+ }
+
+ i += 3;
+ }
+ }
+ }
+
code += "\n"; //
processed.insert(node);
@@ -2412,6 +2550,8 @@ const VisualShaderNodeOutput::Port VisualShaderNodeOutput::ports[] = {
{ Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_VECTOR, "color", "COLOR.rgb" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "alpha", "COLOR.a" },
{ Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_SCALAR, "roughness", "ROUGHNESS" },
+ { Shader::MODE_SPATIAL, VisualShader::TYPE_VERTEX, VisualShaderNode::PORT_TYPE_TRANSFORM, "model_view_matrix", "MODELVIEW_MATRIX" },
+
// Spatial, Fragment
{ Shader::MODE_SPATIAL, VisualShader::TYPE_FRAGMENT, VisualShaderNode::PORT_TYPE_VECTOR, "albedo", "ALBEDO" },
@@ -2551,9 +2691,13 @@ String VisualShaderNodeOutput::get_output_port_name(int p_port) const {
}
bool VisualShaderNodeOutput::is_port_separator(int p_index) const {
+ if (shader_mode == Shader::MODE_SPATIAL && shader_type == VisualShader::TYPE_VERTEX) {
+ String name = get_input_port_name(p_index);
+ return bool(name == "Model View Matrix");
+ }
if (shader_mode == Shader::MODE_SPATIAL && shader_type == VisualShader::TYPE_FRAGMENT) {
String name = get_input_port_name(p_index);
- return (name == "Normal" || name == "Rim" || name == "Alpha Scissor Threshold");
+ return bool(name == "Normal" || name == "Rim" || name == "Alpha Scissor Threshold");
}
return false;
}
diff --git a/scene/resources/visual_shader.h b/scene/resources/visual_shader.h
index 841672294e..aa7768751e 100644
--- a/scene/resources/visual_shader.h
+++ b/scene/resources/visual_shader.h
@@ -199,9 +199,12 @@ class VisualShaderNode : public Resource {
Map<int, Variant> default_input_values;
Map<int, bool> connected_input_ports;
Map<int, int> connected_output_ports;
+ Map<int, bool> expanded_output_ports;
protected:
bool simple_decl = true;
+ bool disabled = false;
+
static void _bind_methods();
public:
@@ -245,10 +248,20 @@ public:
void set_input_port_connected(int p_port, bool p_connected);
virtual bool is_generate_input_var(int p_port) const;
+ virtual bool is_output_port_expandable(int p_port) const;
+ void _set_output_ports_expanded(const Array &p_data);
+ Array _get_output_ports_expanded() const;
+ void _set_output_port_expanded(int p_port, bool p_expanded);
+ bool _is_output_port_expanded(int p_port) const;
+ int get_expanded_output_port_count() const;
+
virtual bool is_code_generated() const;
virtual bool is_show_prop_names() const;
virtual bool is_use_prop_slots() const;
+ bool is_disabled() const;
+ void set_disabled(bool p_disabled = true);
+
virtual Vector<StringName> get_editable_properties() const;
virtual Vector<VisualShader::DefaultTextureParam> get_default_texture_parameters(VisualShader::Type p_type, int p_id) const;
diff --git a/scene/resources/visual_shader_nodes.cpp b/scene/resources/visual_shader_nodes.cpp
index 7943b95177..e7cc78cb3a 100644
--- a/scene/resources/visual_shader_nodes.cpp
+++ b/scene/resources/visual_shader_nodes.cpp
@@ -242,6 +242,13 @@ String VisualShaderNodeColorConstant::get_output_port_name(int p_port) const {
return p_port == 0 ? "" : "alpha"; //no output port means the editor will be used as port
}
+bool VisualShaderNodeColorConstant::is_output_port_expandable(int p_port) const {
+ if (p_port == 0) {
+ return true;
+ }
+ return false;
+}
+
String VisualShaderNodeColorConstant::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const {
String code;
code += "\t" + p_output_vars[0] + " = " + vformat("vec3(%.6f, %.6f, %.6f)", constant.r, constant.g, constant.b) + ";\n";
@@ -455,6 +462,13 @@ String VisualShaderNodeTexture::get_output_port_name(int p_port) const {
return p_port == 0 ? "rgb" : "alpha";
}
+bool VisualShaderNodeTexture::is_output_port_expandable(int p_port) const {
+ if (p_port == 0) {
+ return true;
+ }
+ return false;
+}
+
String VisualShaderNodeTexture::get_input_port_default_hint(int p_port) const {
if (p_port == 0) {
return "default";
@@ -775,7 +789,7 @@ void VisualShaderNodeTexture::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::INT, "source", PROPERTY_HINT_ENUM, "Texture,Screen,Texture2D,NormalMap2D,Depth,SamplerPort"), "set_source", "get_source");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_texture", "get_texture");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "texture_type", PROPERTY_HINT_ENUM, "Data,Color,Normalmap"), "set_texture_type", "get_texture_type");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "texture_type", PROPERTY_HINT_ENUM, "Data,Color,Normal Map"), "set_texture_type", "get_texture_type");
BIND_ENUM_CONSTANT(SOURCE_TEXTURE);
BIND_ENUM_CONSTANT(SOURCE_SCREEN);
@@ -917,6 +931,13 @@ String VisualShaderNodeSample3D::get_output_port_name(int p_port) const {
return p_port == 0 ? "rgb" : "alpha";
}
+bool VisualShaderNodeSample3D::is_output_port_expandable(int p_port) const {
+ if (p_port == 0) {
+ return true;
+ }
+ return false;
+}
+
String VisualShaderNodeSample3D::get_input_port_default_hint(int p_port) const {
if (p_port == 0) {
return "default";
@@ -1168,6 +1189,13 @@ String VisualShaderNodeCubemap::get_output_port_name(int p_port) const {
return p_port == 0 ? "rgb" : "alpha";
}
+bool VisualShaderNodeCubemap::is_output_port_expandable(int p_port) const {
+ if (p_port == 0) {
+ return true;
+ }
+ return false;
+}
+
Vector<VisualShader::DefaultTextureParam> VisualShaderNodeCubemap::get_default_texture_parameters(VisualShader::Type p_type, int p_id) const {
VisualShader::DefaultTextureParam dtp;
dtp.name = make_unique_id(p_type, p_id, "cube");
@@ -1308,7 +1336,7 @@ void VisualShaderNodeCubemap::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::INT, "source", PROPERTY_HINT_ENUM, "Texture,SamplerPort"), "set_source", "get_source");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "cube_map", PROPERTY_HINT_RESOURCE_TYPE, "Cubemap"), "set_cube_map", "get_cube_map");
- ADD_PROPERTY(PropertyInfo(Variant::INT, "texture_type", PROPERTY_HINT_ENUM, "Data,Color,Normalmap"), "set_texture_type", "get_texture_type");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "texture_type", PROPERTY_HINT_ENUM, "Data,Color,Normal Map"), "set_texture_type", "get_texture_type");
BIND_ENUM_CONSTANT(SOURCE_TEXTURE);
BIND_ENUM_CONSTANT(SOURCE_PORT);
@@ -1409,7 +1437,7 @@ void VisualShaderNodeFloatOp::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_operator", "op"), &VisualShaderNodeFloatOp::set_operator);
ClassDB::bind_method(D_METHOD("get_operator"), &VisualShaderNodeFloatOp::get_operator);
- ADD_PROPERTY(PropertyInfo(Variant::INT, "operator", PROPERTY_HINT_ENUM, "Add,Sub,Multiply,Divide,Remainder,Power,Max,Min,Atan2,Step"), "set_operator", "get_operator");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "operator", PROPERTY_HINT_ENUM, "Add,Subtract,Multiply,Divide,Remainder,Power,Max,Min,ATan2,Step"), "set_operator", "get_operator");
BIND_ENUM_CONSTANT(OP_ADD);
BIND_ENUM_CONSTANT(OP_SUB);
@@ -1506,7 +1534,7 @@ void VisualShaderNodeIntOp::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_operator", "op"), &VisualShaderNodeIntOp::set_operator);
ClassDB::bind_method(D_METHOD("get_operator"), &VisualShaderNodeIntOp::get_operator);
- ADD_PROPERTY(PropertyInfo(Variant::INT, "operator", PROPERTY_HINT_ENUM, "Add,Sub,Multiply,Divide,Remainder,Max,Min"), "set_operator", "get_operator");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "operator", PROPERTY_HINT_ENUM, "Add,Subtract,Multiply,Divide,Remainder,Max,Min"), "set_operator", "get_operator");
BIND_ENUM_CONSTANT(OP_ADD);
BIND_ENUM_CONSTANT(OP_SUB);
@@ -1615,7 +1643,7 @@ void VisualShaderNodeVectorOp::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_operator", "op"), &VisualShaderNodeVectorOp::set_operator);
ClassDB::bind_method(D_METHOD("get_operator"), &VisualShaderNodeVectorOp::get_operator);
- ADD_PROPERTY(PropertyInfo(Variant::INT, "operator", PROPERTY_HINT_ENUM, "Add,Sub,Multiply,Divide,Remainder,Power,Max,Min,Cross,Atan2,Reflect,Step"), "set_operator", "get_operator");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "operator", PROPERTY_HINT_ENUM, "Add,Subtract,Multiply,Divide,Remainder,Power,Max,Min,Cross,ATan2,Reflect,Step"), "set_operator", "get_operator");
BIND_ENUM_CONSTANT(OP_ADD);
BIND_ENUM_CONSTANT(OP_SUB);
@@ -1785,7 +1813,7 @@ void VisualShaderNodeColorOp::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_operator", "op"), &VisualShaderNodeColorOp::set_operator);
ClassDB::bind_method(D_METHOD("get_operator"), &VisualShaderNodeColorOp::get_operator);
- ADD_PROPERTY(PropertyInfo(Variant::INT, "operator", PROPERTY_HINT_ENUM, "Screen,Difference,Darken,Lighten,Overlay,Dodge,Burn,SoftLight,HardLight"), "set_operator", "get_operator");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "operator", PROPERTY_HINT_ENUM, "Screen,Difference,Darken,Lighten,Overlay,Dodge,Burn,Soft Light,Hard Light"), "set_operator", "get_operator");
BIND_ENUM_CONSTANT(OP_SCREEN);
BIND_ENUM_CONSTANT(OP_DIFFERENCE);
@@ -3899,7 +3927,7 @@ void VisualShaderNodeIntUniform::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_default_value", "value"), &VisualShaderNodeIntUniform::set_default_value);
ClassDB::bind_method(D_METHOD("get_default_value"), &VisualShaderNodeIntUniform::get_default_value);
- ADD_PROPERTY(PropertyInfo(Variant::INT, "hint", PROPERTY_HINT_ENUM, "None,Range,Range+Step"), "set_hint", "get_hint");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "hint", PROPERTY_HINT_ENUM, "None,Range,Range + Step"), "set_hint", "get_hint");
ADD_PROPERTY(PropertyInfo(Variant::INT, "min"), "set_min", "get_min");
ADD_PROPERTY(PropertyInfo(Variant::INT, "max"), "set_max", "get_max");
ADD_PROPERTY(PropertyInfo(Variant::INT, "step"), "set_step", "get_step");
@@ -4487,7 +4515,7 @@ void VisualShaderNodeTextureUniform::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_color_default", "type"), &VisualShaderNodeTextureUniform::set_color_default);
ClassDB::bind_method(D_METHOD("get_color_default"), &VisualShaderNodeTextureUniform::get_color_default);
- ADD_PROPERTY(PropertyInfo(Variant::INT, "texture_type", PROPERTY_HINT_ENUM, "Data,Color,Normalmap,Aniso"), "set_texture_type", "get_texture_type");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "texture_type", PROPERTY_HINT_ENUM, "Data,Color,Normal Map,Anisotropic"), "set_texture_type", "get_texture_type");
ADD_PROPERTY(PropertyInfo(Variant::INT, "color_default", PROPERTY_HINT_ENUM, "White Default,Black Default"), "set_color_default", "get_color_default");
BIND_ENUM_CONSTANT(TYPE_DATA);
@@ -5551,3 +5579,127 @@ VisualShaderNodeMultiplyAdd::VisualShaderNodeMultiplyAdd() {
set_input_port_default_value(1, 0.0);
set_input_port_default_value(2, 0.0);
}
+
+////////////// Billboard
+
+String VisualShaderNodeBillboard::get_caption() const {
+ return "GetBillboardMatrix";
+}
+
+int VisualShaderNodeBillboard::get_input_port_count() const {
+ return 0;
+}
+
+VisualShaderNodeBillboard::PortType VisualShaderNodeBillboard::get_input_port_type(int p_port) const {
+ return PORT_TYPE_SCALAR;
+}
+
+String VisualShaderNodeBillboard::get_input_port_name(int p_port) const {
+ return "";
+}
+
+int VisualShaderNodeBillboard::get_output_port_count() const {
+ return 1;
+}
+
+VisualShaderNodeBillboard::PortType VisualShaderNodeBillboard::get_output_port_type(int p_port) const {
+ return PORT_TYPE_TRANSFORM;
+}
+
+String VisualShaderNodeBillboard::get_output_port_name(int p_port) const {
+ return "model_view_matrix";
+}
+
+String VisualShaderNodeBillboard::generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview) const {
+ String code;
+
+ switch (billboard_type) {
+ case BILLBOARD_TYPE_ENABLED:
+ code += "\t{\n";
+ code += "\t\tmat4 __mvm = INV_CAMERA_MATRIX * mat4(CAMERA_MATRIX[0], CAMERA_MATRIX[1], CAMERA_MATRIX[2], WORLD_MATRIX[3]);\n";
+ if (keep_scale) {
+ code += "\t\t__mvm = __mvm * mat4(vec4(length(WORLD_MATRIX[0].xyz), 0.0, 0.0, 0.0), vec4(0.0, length(WORLD_MATRIX[1].xyz), 0.0, 0.0), vec4(0.0, 0.0, length(WORLD_MATRIX[2].xyz), 0.0), vec4(0.0, 0.0, 0.0, 1.0));\n";
+ }
+ code += "\t\t" + p_output_vars[0] + " = __mvm;\n";
+ code += "\t}\n";
+ break;
+ case BILLBOARD_TYPE_FIXED_Y:
+ code += "\t{\n";
+ code += "\t\tmat4 __mvm = INV_CAMERA_MATRIX * mat4(CAMERA_MATRIX[0], WORLD_MATRIX[1], vec4(normalize(cross(CAMERA_MATRIX[0].xyz, WORLD_MATRIX[1].xyz)), 0.0), WORLD_MATRIX[3]);\n";
+ if (keep_scale) {
+ code += "\t\t__mvm = __mvm * mat4(vec4(length(WORLD_MATRIX[0].xyz), 0.0, 0.0, 0.0), vec4(0.0, 1.0, 0.0, 0.0), vec4(0.0, 0.0, length(WORLD_MATRIX[2].xyz), 0.0), vec4(0.0, 0.0, 0.0, 1.0));\n";
+ } else {
+ code += "\t\t__mvm = __mvm * mat4(vec4(1.0, 0.0, 0.0, 0.0), vec4(0.0, 1.0 / length(WORLD_MATRIX[1].xyz), 0.0, 0.0), vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0));\n";
+ }
+ code += "\t\t" + p_output_vars[0] + " = __mvm;\n";
+ code += "\t}\n";
+ break;
+ case BILLBOARD_TYPE_PARTICLES:
+ code += "\t{\n";
+ code += "\t\tmat4 __wm = mat4(normalize(CAMERA_MATRIX[0]) * length(WORLD_MATRIX[0]), normalize(CAMERA_MATRIX[1]) * length(WORLD_MATRIX[0]), normalize(CAMERA_MATRIX[2]) * length(WORLD_MATRIX[2]), WORLD_MATRIX[3]);\n";
+ code += "\t\t__wm = __wm * mat4(vec4(cos(INSTANCE_CUSTOM.x), -sin(INSTANCE_CUSTOM.x), 0.0, 0.0), vec4(sin(INSTANCE_CUSTOM.x), cos(INSTANCE_CUSTOM.x), 0.0, 0.0), vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0));\n";
+ code += "\t\t" + p_output_vars[0] + " = INV_CAMERA_MATRIX * __wm;\n";
+ code += "\t}\n";
+ break;
+ default:
+ code += "\t" + p_output_vars[0] + " = mat4(1.0);\n";
+ break;
+ }
+
+ return code;
+}
+
+bool VisualShaderNodeBillboard::is_show_prop_names() const {
+ return true;
+}
+
+void VisualShaderNodeBillboard::set_billboard_type(BillboardType p_billboard_type) {
+ ERR_FAIL_INDEX((int)p_billboard_type, BILLBOARD_TYPE_MAX);
+ billboard_type = p_billboard_type;
+ simple_decl = bool(billboard_type == BILLBOARD_TYPE_DISABLED);
+ set_disabled(simple_decl);
+ emit_changed();
+}
+
+VisualShaderNodeBillboard::BillboardType VisualShaderNodeBillboard::get_billboard_type() const {
+ return billboard_type;
+}
+
+void VisualShaderNodeBillboard::set_keep_scale_enabled(bool p_enabled) {
+ keep_scale = p_enabled;
+ emit_changed();
+}
+
+bool VisualShaderNodeBillboard::is_keep_scale_enabled() const {
+ return keep_scale;
+}
+
+Vector<StringName> VisualShaderNodeBillboard::get_editable_properties() const {
+ Vector<StringName> props;
+ props.push_back("billboard_type");
+ if (billboard_type == BILLBOARD_TYPE_ENABLED || billboard_type == BILLBOARD_TYPE_FIXED_Y) {
+ props.push_back("keep_scale");
+ }
+ return props;
+}
+
+void VisualShaderNodeBillboard::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_billboard_type", "billboard_type"), &VisualShaderNodeBillboard::set_billboard_type);
+ ClassDB::bind_method(D_METHOD("get_billboard_type"), &VisualShaderNodeBillboard::get_billboard_type);
+
+ ClassDB::bind_method(D_METHOD("set_keep_scale_enabled", "enabled"), &VisualShaderNodeBillboard::set_keep_scale_enabled);
+ ClassDB::bind_method(D_METHOD("is_keep_scale_enabled"), &VisualShaderNodeBillboard::is_keep_scale_enabled);
+
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "billboard_type", PROPERTY_HINT_ENUM, "Disabled,Enabled,Y-Billboard,Particles"), "set_billboard_type", "get_billboard_type");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "keep_scale"), "set_keep_scale_enabled", "is_keep_scale_enabled");
+
+ BIND_ENUM_CONSTANT(BILLBOARD_TYPE_DISABLED);
+ BIND_ENUM_CONSTANT(BILLBOARD_TYPE_ENABLED);
+ BIND_ENUM_CONSTANT(BILLBOARD_TYPE_FIXED_Y);
+ BIND_ENUM_CONSTANT(BILLBOARD_TYPE_PARTICLES);
+ BIND_ENUM_CONSTANT(BILLBOARD_TYPE_MAX);
+}
+
+VisualShaderNodeBillboard::VisualShaderNodeBillboard() {
+ simple_decl = false;
+}
diff --git a/scene/resources/visual_shader_nodes.h b/scene/resources/visual_shader_nodes.h
index d3397fad6f..1c70459e3b 100644
--- a/scene/resources/visual_shader_nodes.h
+++ b/scene/resources/visual_shader_nodes.h
@@ -163,6 +163,7 @@ public:
virtual int get_output_port_count() const override;
virtual PortType get_output_port_type(int p_port) const override;
virtual String get_output_port_name(int p_port) const override;
+ virtual bool is_output_port_expandable(int p_port) const override;
virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const override;
@@ -275,6 +276,7 @@ public:
virtual int get_output_port_count() const override;
virtual PortType get_output_port_type(int p_port) const override;
virtual String get_output_port_name(int p_port) const override;
+ virtual bool is_output_port_expandable(int p_port) const override;
virtual String get_input_port_default_hint(int p_port) const override;
@@ -359,6 +361,7 @@ public:
virtual int get_output_port_count() const override;
virtual PortType get_output_port_type(int p_port) const override;
virtual String get_output_port_name(int p_port) const override;
+ virtual bool is_output_port_expandable(int p_port) const override;
virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const override;
@@ -452,6 +455,7 @@ public:
virtual int get_output_port_count() const override;
virtual PortType get_output_port_type(int p_port) const override;
virtual String get_output_port_name(int p_port) const override;
+ virtual bool is_output_port_expandable(int p_port) const override;
virtual Vector<VisualShader::DefaultTextureParam> get_default_texture_parameters(VisualShader::Type p_type, int p_id) const override;
virtual String generate_global(Shader::Mode p_mode, VisualShader::Type p_type, int p_id) const override;
@@ -2213,4 +2217,51 @@ public:
VARIANT_ENUM_CAST(VisualShaderNodeMultiplyAdd::OpType)
+class VisualShaderNodeBillboard : public VisualShaderNode {
+ GDCLASS(VisualShaderNodeBillboard, VisualShaderNode);
+
+public:
+ enum BillboardType {
+ BILLBOARD_TYPE_DISABLED,
+ BILLBOARD_TYPE_ENABLED,
+ BILLBOARD_TYPE_FIXED_Y,
+ BILLBOARD_TYPE_PARTICLES,
+ BILLBOARD_TYPE_MAX,
+ };
+
+protected:
+ BillboardType billboard_type = BILLBOARD_TYPE_ENABLED;
+ bool keep_scale = false;
+
+protected:
+ static void _bind_methods();
+
+public:
+ virtual String get_caption() const override;
+
+ virtual int get_input_port_count() const override;
+ virtual PortType get_input_port_type(int p_port) const override;
+ virtual String get_input_port_name(int p_port) const override;
+
+ virtual int get_output_port_count() const override;
+ virtual PortType get_output_port_type(int p_port) const override;
+ virtual String get_output_port_name(int p_port) const override;
+
+ virtual String generate_code(Shader::Mode p_mode, VisualShader::Type p_type, int p_id, const String *p_input_vars, const String *p_output_vars, bool p_for_preview = false) const override;
+
+ virtual bool is_show_prop_names() const override;
+
+ void set_billboard_type(BillboardType p_billboard_type);
+ BillboardType get_billboard_type() const;
+
+ void set_keep_scale_enabled(bool p_enabled);
+ bool is_keep_scale_enabled() const;
+
+ virtual Vector<StringName> get_editable_properties() const override;
+
+ VisualShaderNodeBillboard();
+};
+
+VARIANT_ENUM_CAST(VisualShaderNodeBillboard::BillboardType)
+
#endif // VISUAL_SHADER_NODES_H
diff --git a/servers/audio/effects/audio_effect_distortion.cpp b/servers/audio/effects/audio_effect_distortion.cpp
index 06d51776a3..5c076ca3fe 100644
--- a/servers/audio/effects/audio_effect_distortion.cpp
+++ b/servers/audio/effects/audio_effect_distortion.cpp
@@ -159,7 +159,7 @@ void AudioEffectDistortion::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_post_gain", "post_gain"), &AudioEffectDistortion::set_post_gain);
ClassDB::bind_method(D_METHOD("get_post_gain"), &AudioEffectDistortion::get_post_gain);
- ADD_PROPERTY(PropertyInfo(Variant::INT, "mode", PROPERTY_HINT_ENUM, "Clip,ATan,LoFi,Overdrive,WaveShape"), "set_mode", "get_mode");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "mode", PROPERTY_HINT_ENUM, "Clip,ATan,LoFi,Overdrive,Wave Shape"), "set_mode", "get_mode");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "pre_gain", PROPERTY_HINT_RANGE, "-60,60,0.01"), "set_pre_gain", "get_pre_gain");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "keep_hf_hz", PROPERTY_HINT_RANGE, "1,20500,1"), "set_keep_hf_hz", "get_keep_hf_hz");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "drive", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_drive", "get_drive");
diff --git a/servers/audio_server.cpp b/servers/audio_server.cpp
index 08c482553b..0e816fd4f8 100644
--- a/servers/audio_server.cpp
+++ b/servers/audio_server.cpp
@@ -1164,6 +1164,9 @@ void AudioServer::set_bus_layout(const Ref<AudioBusLayout> &p_bus_layout) {
Bus::Effect bfx;
bfx.effect = fx;
bfx.enabled = p_bus_layout->buses[i].effects[j].enabled;
+#if DEBUG_ENABLED
+ bfx.prof_time = 0;
+#endif
bus->effects.push_back(bfx);
}
}
diff --git a/servers/physics_3d/body_3d_sw.cpp b/servers/physics_3d/body_3d_sw.cpp
index 4357c474e4..e4f9548a61 100644
--- a/servers/physics_3d/body_3d_sw.cpp
+++ b/servers/physics_3d/body_3d_sw.cpp
@@ -65,16 +65,18 @@ void Body3DSW::update_inertias() {
// We have to recompute the center of mass.
center_of_mass_local.zero();
- for (int i = 0; i < get_shape_count(); i++) {
- real_t area = get_shape_area(i);
+ if (total_area != 0.0) {
+ for (int i = 0; i < get_shape_count(); i++) {
+ real_t area = get_shape_area(i);
- real_t mass = area * this->mass / total_area;
+ real_t mass = area * this->mass / total_area;
- // NOTE: we assume that the shape origin is also its center of mass.
- center_of_mass_local += mass * get_shape_transform(i).origin;
- }
+ // NOTE: we assume that the shape origin is also its center of mass.
+ center_of_mass_local += mass * get_shape_transform(i).origin;
+ }
- center_of_mass_local /= mass;
+ center_of_mass_local /= mass;
+ }
// Recompute the inertia tensor.
Basis inertia_tensor;
@@ -86,12 +88,15 @@ void Body3DSW::update_inertias() {
continue;
}
+ real_t area = get_shape_area(i);
+ if (area == 0.0) {
+ continue;
+ }
+
inertia_set = true;
const Shape3DSW *shape = get_shape(i);
- real_t area = get_shape_area(i);
-
real_t mass = area * this->mass / total_area;
Basis shape_inertia_tensor = shape->get_moment_of_inertia(mass).to_diagonal_matrix();
diff --git a/servers/rendering/renderer_canvas_render.h b/servers/rendering/renderer_canvas_render.h
index 0e9ef616cb..0266e137c0 100644
--- a/servers/rendering/renderer_canvas_render.h
+++ b/servers/rendering/renderer_canvas_render.h
@@ -420,7 +420,6 @@ public:
if (found_xform) {
r = xf.xform(r);
- found_xform = false;
}
if (first) {
diff --git a/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.cpp b/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.cpp
index c96c541461..5a26b5abbb 100644
--- a/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.cpp
+++ b/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.cpp
@@ -30,6 +30,7 @@
#include "scene_shader_forward_clustered.h"
#include "core/config/project_settings.h"
+#include "core/math/math_defs.h"
#include "render_forward_clustered.h"
using namespace RendererSceneRenderImplementation;
@@ -608,6 +609,9 @@ void SceneShaderForwardClustered::init(RendererStorageRD *p_storage, const Strin
//builtins
actions.renames["TIME"] = "scene_data.time";
+ actions.renames["PI"] = _MKSTR(Math_PI);
+ actions.renames["TAU"] = _MKSTR(Math_TAU);
+ actions.renames["E"] = _MKSTR(Math_E);
actions.renames["VIEWPORT_SIZE"] = "scene_data.viewport_size";
actions.renames["FRAGCOORD"] = "gl_FragCoord";
diff --git a/servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.cpp b/servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.cpp
index b9220cc514..883273c8a5 100644
--- a/servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.cpp
+++ b/servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.cpp
@@ -30,6 +30,7 @@
#include "scene_shader_forward_mobile.h"
#include "core/config/project_settings.h"
+#include "core/math/math_defs.h"
#include "render_forward_mobile.h"
using namespace RendererSceneRenderImplementation;
@@ -625,6 +626,9 @@ void SceneShaderForwardMobile::init(RendererStorageRD *p_storage, const String p
//builtins
actions.renames["TIME"] = "scene_data.time";
+ actions.renames["PI"] = _MKSTR(Math_PI);
+ actions.renames["TAU"] = _MKSTR(Math_TAU);
+ actions.renames["E"] = _MKSTR(Math_E);
actions.renames["VIEWPORT_SIZE"] = "scene_data.viewport_size";
actions.renames["FRAGCOORD"] = "gl_FragCoord";
diff --git a/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp b/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp
index 1f27dd54ec..70c1705bff 100644
--- a/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp
+++ b/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp
@@ -31,6 +31,7 @@
#include "renderer_canvas_render_rd.h"
#include "core/config/project_settings.h"
#include "core/math/geometry_2d.h"
+#include "core/math/math_defs.h"
#include "core/math/math_funcs.h"
#include "renderer_compositor_rd.h"
#include "servers/rendering/rendering_server_default.h"
@@ -2408,6 +2409,9 @@ RendererCanvasRenderRD::RendererCanvasRenderRD(RendererStorageRD *p_storage) {
actions.renames["CANVAS_MATRIX"] = "canvas_data.canvas_transform";
actions.renames["SCREEN_MATRIX"] = "canvas_data.screen_transform";
actions.renames["TIME"] = "canvas_data.time";
+ actions.renames["PI"] = _MKSTR(Math_PI);
+ actions.renames["TAU"] = _MKSTR(Math_TAU);
+ actions.renames["E"] = _MKSTR(Math_E);
actions.renames["AT_LIGHT_PASS"] = "false";
actions.renames["INSTANCE_CUSTOM"] = "instance_custom";
diff --git a/servers/rendering/renderer_rd/renderer_compositor_rd.cpp b/servers/rendering/renderer_rd/renderer_compositor_rd.cpp
index 0012ba9c27..1337d36762 100644
--- a/servers/rendering/renderer_rd/renderer_compositor_rd.cpp
+++ b/servers/rendering/renderer_rd/renderer_compositor_rd.cpp
@@ -31,6 +31,7 @@
#include "renderer_compositor_rd.h"
#include "core/config/project_settings.h"
+#include "core/os/dir_access.h"
void RendererCompositorRD::prepare_for_blitting_render_targets() {
RD::get_singleton()->prepare_screen_for_drawing();
@@ -155,6 +156,43 @@ void RendererCompositorRD::finalize() {
RendererCompositorRD *RendererCompositorRD::singleton = nullptr;
RendererCompositorRD::RendererCompositorRD() {
+ {
+ String shader_cache_dir = Engine::get_singleton()->get_shader_cache_path();
+ if (shader_cache_dir == String()) {
+ shader_cache_dir = "user://";
+ }
+ DirAccessRef da = DirAccess::open(shader_cache_dir);
+ if (!da) {
+ ERR_PRINT("Can't create shader cache folder, no shader caching will happen: " + shader_cache_dir);
+ } else {
+ Error err = da->change_dir("shader_cache");
+ if (err != OK) {
+ err = da->make_dir("shader_cache");
+ }
+ if (err != OK) {
+ ERR_PRINT("Can't create shader cache folder, no shader caching will happen: " + shader_cache_dir);
+ } else {
+ shader_cache_dir = shader_cache_dir.plus_file("shader_cache");
+
+ bool shader_cache_enabled = GLOBAL_GET("rendering/shader_compiler/shader_cache/enabled");
+ if (!Engine::get_singleton()->is_editor_hint() && !shader_cache_enabled) {
+ shader_cache_dir = String(); //disable only if not editor
+ }
+
+ if (shader_cache_dir != String()) {
+ bool compress = GLOBAL_GET("rendering/shader_compiler/shader_cache/compress");
+ bool use_zstd = GLOBAL_GET("rendering/shader_compiler/shader_cache/use_zstd_compression");
+ bool strip_debug = GLOBAL_GET("rendering/shader_compiler/shader_cache/strip_debug");
+
+ ShaderRD::set_shader_cache_dir(shader_cache_dir);
+ ShaderRD::set_shader_cache_save_compressed(compress);
+ ShaderRD::set_shader_cache_save_compressed_zstd(use_zstd);
+ ShaderRD::set_shader_cache_save_debug(!strip_debug);
+ }
+ }
+ }
+ }
+
singleton = this;
time = 0;
@@ -171,3 +209,7 @@ RendererCompositorRD::RendererCompositorRD() {
scene = memnew(RendererSceneRenderImplementation::RenderForwardClustered(storage));
}
}
+
+RendererCompositorRD::~RendererCompositorRD() {
+ ShaderRD::set_shader_cache_dir(String());
+}
diff --git a/servers/rendering/renderer_rd/renderer_compositor_rd.h b/servers/rendering/renderer_rd/renderer_compositor_rd.h
index 52552f7ee3..7a78322051 100644
--- a/servers/rendering/renderer_rd/renderer_compositor_rd.h
+++ b/servers/rendering/renderer_rd/renderer_compositor_rd.h
@@ -118,6 +118,6 @@ public:
static RendererCompositorRD *singleton;
RendererCompositorRD();
- ~RendererCompositorRD() {}
+ ~RendererCompositorRD();
};
#endif // RASTERIZER_RD_H
diff --git a/servers/rendering/renderer_rd/renderer_scene_sky_rd.cpp b/servers/rendering/renderer_rd/renderer_scene_sky_rd.cpp
index 54c6e81110..966a1c6815 100644
--- a/servers/rendering/renderer_rd/renderer_scene_sky_rd.cpp
+++ b/servers/rendering/renderer_rd/renderer_scene_sky_rd.cpp
@@ -30,6 +30,7 @@
#include "renderer_scene_sky_rd.h"
#include "core/config/project_settings.h"
+#include "core/math/math_defs.h"
#include "renderer_scene_render_rd.h"
#include "servers/rendering/rendering_server_default.h"
@@ -710,6 +711,9 @@ void RendererSceneSkyRD::init(RendererStorageRD *p_storage) {
actions.renames["SKY_COORDS"] = "panorama_coords";
actions.renames["SCREEN_UV"] = "uv";
actions.renames["TIME"] = "params.time";
+ actions.renames["PI"] = _MKSTR(Math_PI);
+ actions.renames["TAU"] = _MKSTR(Math_TAU);
+ actions.renames["E"] = _MKSTR(Math_E);
actions.renames["HALF_RES_COLOR"] = "half_res_color";
actions.renames["QUARTER_RES_COLOR"] = "quarter_res_color";
actions.renames["RADIANCE"] = "radiance";
diff --git a/servers/rendering/renderer_rd/renderer_storage_rd.cpp b/servers/rendering/renderer_rd/renderer_storage_rd.cpp
index 26ddd74a80..7a26e40c0a 100644
--- a/servers/rendering/renderer_rd/renderer_storage_rd.cpp
+++ b/servers/rendering/renderer_rd/renderer_storage_rd.cpp
@@ -33,6 +33,7 @@
#include "core/config/engine.h"
#include "core/config/project_settings.h"
#include "core/io/resource_loader.h"
+#include "core/math/math_defs.h"
#include "renderer_compositor_rd.h"
#include "servers/rendering/shader_language.h"
@@ -9225,6 +9226,9 @@ RendererStorageRD::RendererStorageRD() {
actions.renames["CUSTOM"] = "PARTICLE.custom";
actions.renames["TRANSFORM"] = "PARTICLE.xform";
actions.renames["TIME"] = "FRAME.time";
+ actions.renames["PI"] = _MKSTR(Math_PI);
+ actions.renames["TAU"] = _MKSTR(Math_TAU);
+ actions.renames["E"] = _MKSTR(Math_E);
actions.renames["LIFETIME"] = "params.lifetime";
actions.renames["DELTA"] = "local_delta";
actions.renames["NUMBER"] = "particle_number";
diff --git a/servers/rendering/renderer_rd/shader_compiler_rd.cpp b/servers/rendering/renderer_rd/shader_compiler_rd.cpp
index 3a000bd06e..7deedb80c3 100644
--- a/servers/rendering/renderer_rd/shader_compiler_rd.cpp
+++ b/servers/rendering/renderer_rd/shader_compiler_rd.cpp
@@ -369,17 +369,24 @@ void ShaderCompilerRD::_dump_function_deps(const SL::ShaderNode *p_node, const S
ERR_FAIL_COND(fidx == -1);
+ Vector<StringName> uses_functions;
+
for (Set<StringName>::Element *E = p_node->functions[fidx].uses_function.front(); E; E = E->next()) {
- if (added.has(E->get())) {
+ uses_functions.push_back(E->get());
+ }
+ uses_functions.sort_custom<StringName::AlphCompare>(); //ensure order is deterministic so the same shader is always produced
+
+ for (int k = 0; k < uses_functions.size(); k++) {
+ if (added.has(uses_functions[k])) {
continue; //was added already
}
- _dump_function_deps(p_node, E->get(), p_func_code, r_to_add, added);
+ _dump_function_deps(p_node, uses_functions[k], p_func_code, r_to_add, added);
SL::FunctionNode *fnode = nullptr;
for (int i = 0; i < p_node->functions.size(); i++) {
- if (p_node->functions[i].name == E->get()) {
+ if (p_node->functions[i].name == uses_functions[k]) {
fnode = p_node->functions[i].function;
break;
}
@@ -391,10 +398,21 @@ void ShaderCompilerRD::_dump_function_deps(const SL::ShaderNode *p_node, const S
String header;
if (fnode->return_type == SL::TYPE_STRUCT) {
- header = _mkid(fnode->return_struct_name) + " " + _mkid(fnode->name) + "(";
+ header = _mkid(fnode->return_struct_name);
} else {
- header = _typestr(fnode->return_type) + " " + _mkid(fnode->name) + "(";
+ header = _typestr(fnode->return_type);
}
+
+ if (fnode->return_array_size > 0) {
+ header += "[";
+ header += itos(fnode->return_array_size);
+ header += "]";
+ }
+
+ header += " ";
+ header += _mkid(fnode->name);
+ header += "(";
+
for (int i = 0; i < fnode->arguments.size(); i++) {
if (i > 0) {
header += ", ";
@@ -407,13 +425,18 @@ void ShaderCompilerRD::_dump_function_deps(const SL::ShaderNode *p_node, const S
} else {
header += _qualstr(fnode->arguments[i].qualifier) + _prestr(fnode->arguments[i].precision) + _typestr(fnode->arguments[i].type) + " " + _mkid(fnode->arguments[i].name);
}
+ if (fnode->arguments[i].array_size > 0) {
+ header += "[";
+ header += itos(fnode->arguments[i].array_size);
+ header += "]";
+ }
}
header += ")\n";
r_to_add += header;
- r_to_add += p_func_code[E->get()];
+ r_to_add += p_func_code[uses_functions[k]];
- added.insert(E->get());
+ added.insert(uses_functions[k]);
}
}
@@ -565,63 +588,74 @@ String ShaderCompilerRD::_dump_node_code(const SL::Node *p_node, int p_level, Ge
uniform_defines.resize(max_uniforms);
bool uses_uniforms = false;
+ Vector<StringName> uniform_names;
+
for (Map<StringName, SL::ShaderNode::Uniform>::Element *E = pnode->uniforms.front(); E; E = E->next()) {
+ uniform_names.push_back(E->key());
+ }
+
+ uniform_names.sort_custom<StringName::AlphCompare>(); //ensure order is deterministic so the same shader is always produced
+
+ for (int k = 0; k < uniform_names.size(); k++) {
+ StringName uniform_name = uniform_names[k];
+ const SL::ShaderNode::Uniform &uniform = pnode->uniforms[uniform_name];
+
String ucode;
- if (E->get().scope == SL::ShaderNode::Uniform::SCOPE_INSTANCE) {
+ if (uniform.scope == SL::ShaderNode::Uniform::SCOPE_INSTANCE) {
//insert, but don't generate any code.
- p_actions.uniforms->insert(E->key(), E->get());
+ p_actions.uniforms->insert(uniform_name, uniform);
continue; //instances are indexed directly, dont need index uniforms
}
- if (SL::is_sampler_type(E->get().type)) {
- ucode = "layout(set = " + itos(actions.texture_layout_set) + ", binding = " + itos(actions.base_texture_binding_index + E->get().texture_order) + ") uniform ";
+ if (SL::is_sampler_type(uniform.type)) {
+ ucode = "layout(set = " + itos(actions.texture_layout_set) + ", binding = " + itos(actions.base_texture_binding_index + uniform.texture_order) + ") uniform ";
}
- bool is_buffer_global = !SL::is_sampler_type(E->get().type) && E->get().scope == SL::ShaderNode::Uniform::SCOPE_GLOBAL;
+ bool is_buffer_global = !SL::is_sampler_type(uniform.type) && uniform.scope == SL::ShaderNode::Uniform::SCOPE_GLOBAL;
if (is_buffer_global) {
//this is an integer to index the global table
ucode += _typestr(ShaderLanguage::TYPE_UINT);
} else {
- ucode += _prestr(E->get().precision);
- ucode += _typestr(E->get().type);
+ ucode += _prestr(uniform.precision);
+ ucode += _typestr(uniform.type);
}
- ucode += " " + _mkid(E->key());
+ ucode += " " + _mkid(uniform_name);
ucode += ";\n";
- if (SL::is_sampler_type(E->get().type)) {
+ if (SL::is_sampler_type(uniform.type)) {
for (int j = 0; j < STAGE_MAX; j++) {
r_gen_code.stage_globals[j] += ucode;
}
GeneratedCode::Texture texture;
- texture.name = E->key();
- texture.hint = E->get().hint;
- texture.type = E->get().type;
- texture.filter = E->get().filter;
- texture.repeat = E->get().repeat;
- texture.global = E->get().scope == ShaderLanguage::ShaderNode::Uniform::SCOPE_GLOBAL;
+ texture.name = uniform_name;
+ texture.hint = uniform.hint;
+ texture.type = uniform.type;
+ texture.filter = uniform.filter;
+ texture.repeat = uniform.repeat;
+ texture.global = uniform.scope == ShaderLanguage::ShaderNode::Uniform::SCOPE_GLOBAL;
if (texture.global) {
r_gen_code.uses_global_textures = true;
}
- r_gen_code.texture_uniforms.write[E->get().texture_order] = texture;
+ r_gen_code.texture_uniforms.write[uniform.texture_order] = texture;
} else {
if (!uses_uniforms) {
uses_uniforms = true;
}
- uniform_defines.write[E->get().order] = ucode;
+ uniform_defines.write[uniform.order] = ucode;
if (is_buffer_global) {
//globals are indices into the global table
- uniform_sizes.write[E->get().order] = _get_datatype_size(ShaderLanguage::TYPE_UINT);
- uniform_alignments.write[E->get().order] = _get_datatype_alignment(ShaderLanguage::TYPE_UINT);
+ uniform_sizes.write[uniform.order] = _get_datatype_size(ShaderLanguage::TYPE_UINT);
+ uniform_alignments.write[uniform.order] = _get_datatype_alignment(ShaderLanguage::TYPE_UINT);
} else {
- uniform_sizes.write[E->get().order] = _get_datatype_size(E->get().type);
- uniform_alignments.write[E->get().order] = _get_datatype_alignment(E->get().type);
+ uniform_sizes.write[uniform.order] = _get_datatype_size(uniform.type);
+ uniform_alignments.write[uniform.order] = _get_datatype_alignment(uniform.type);
}
}
- p_actions.uniforms->insert(E->key(), E->get());
+ p_actions.uniforms->insert(uniform_name, uniform);
}
for (int i = 0; i < max_uniforms; i++) {
@@ -688,21 +722,32 @@ String ShaderCompilerRD::_dump_node_code(const SL::Node *p_node, int p_level, Ge
List<Pair<StringName, SL::ShaderNode::Varying>> var_frag_to_light;
+ Vector<StringName> varying_names;
+
for (Map<StringName, SL::ShaderNode::Varying>::Element *E = pnode->varyings.front(); E; E = E->next()) {
- if (E->get().stage == SL::ShaderNode::Varying::STAGE_FRAGMENT_TO_LIGHT || E->get().stage == SL::ShaderNode::Varying::STAGE_FRAGMENT) {
- var_frag_to_light.push_back(Pair<StringName, SL::ShaderNode::Varying>(E->key(), E->get()));
- fragment_varyings.insert(E->key());
+ varying_names.push_back(E->key());
+ }
+
+ varying_names.sort_custom<StringName::AlphCompare>(); //ensure order is deterministic so the same shader is always produced
+
+ for (int k = 0; k < varying_names.size(); k++) {
+ StringName varying_name = varying_names[k];
+ const SL::ShaderNode::Varying &varying = pnode->varyings[varying_name];
+
+ if (varying.stage == SL::ShaderNode::Varying::STAGE_FRAGMENT_TO_LIGHT || varying.stage == SL::ShaderNode::Varying::STAGE_FRAGMENT) {
+ var_frag_to_light.push_back(Pair<StringName, SL::ShaderNode::Varying>(varying_name, varying));
+ fragment_varyings.insert(varying_name);
continue;
}
String vcode;
- String interp_mode = _interpstr(E->get().interpolation);
- vcode += _prestr(E->get().precision);
- vcode += _typestr(E->get().type);
- vcode += " " + _mkid(E->key());
- if (E->get().array_size > 0) {
+ String interp_mode = _interpstr(varying.interpolation);
+ vcode += _prestr(varying.precision);
+ vcode += _typestr(varying.type);
+ vcode += " " + _mkid(varying_name);
+ if (varying.array_size > 0) {
vcode += "[";
- vcode += itos(E->get().array_size);
+ vcode += itos(varying.array_size);
vcode += "]";
}
vcode += ";\n";
@@ -959,25 +1004,30 @@ String ShaderCompilerRD::_dump_node_code(const SL::Node *p_node, int p_level, Ge
declaration += itos(adnode->declarations[i].size);
}
declaration += "]";
- int sz = adnode->declarations[i].initializer.size();
- if (sz > 0) {
+ if (adnode->declarations[i].single_expression) {
declaration += "=";
- if (adnode->datatype == SL::TYPE_STRUCT) {
- declaration += _mkid(adnode->struct_name);
- } else {
- declaration += _typestr(adnode->datatype);
- }
- declaration += "[";
- declaration += itos(sz);
- declaration += "]";
- declaration += "(";
- for (int j = 0; j < sz; j++) {
- declaration += _dump_node_code(adnode->declarations[i].initializer[j], p_level, r_gen_code, p_actions, p_default_actions, p_assigning);
- if (j != sz - 1) {
- declaration += ", ";
+ declaration += _dump_node_code(adnode->declarations[i].initializer[0], p_level, r_gen_code, p_actions, p_default_actions, p_assigning);
+ } else {
+ int sz = adnode->declarations[i].initializer.size();
+ if (sz > 0) {
+ declaration += "=";
+ if (adnode->datatype == SL::TYPE_STRUCT) {
+ declaration += _mkid(adnode->struct_name);
+ } else {
+ declaration += _typestr(adnode->datatype);
+ }
+ declaration += "[";
+ declaration += itos(sz);
+ declaration += "]";
+ declaration += "(";
+ for (int j = 0; j < sz; j++) {
+ declaration += _dump_node_code(adnode->declarations[i].initializer[j], p_level, r_gen_code, p_actions, p_default_actions, p_assigning);
+ if (j != sz - 1) {
+ declaration += ", ";
+ }
}
+ declaration += ")";
}
- declaration += ")";
}
}
@@ -988,7 +1038,7 @@ String ShaderCompilerRD::_dump_node_code(const SL::Node *p_node, int p_level, Ge
bool use_fragment_varying = false;
if (!(p_actions.entry_point_stages.has(current_func_name) && p_actions.entry_point_stages[current_func_name] == STAGE_VERTEX)) {
- if (anode->assign_expression != nullptr) {
+ if (anode->assign_expression != nullptr && shader->varyings.has(anode->name)) {
use_fragment_varying = true;
} else {
if (p_assigning) {
diff --git a/servers/rendering/renderer_rd/shader_rd.cpp b/servers/rendering/renderer_rd/shader_rd.cpp
index f7242a2b17..6f29ff42bc 100644
--- a/servers/rendering/renderer_rd/shader_rd.cpp
+++ b/servers/rendering/renderer_rd/shader_rd.cpp
@@ -30,8 +30,12 @@
#include "shader_rd.h"
+#include "core/io/compression.h"
+#include "core/os/dir_access.h"
+#include "core/os/file_access.h"
#include "renderer_compositor_rd.h"
#include "servers/rendering/rendering_device.h"
+#include "thirdparty/misc/smolv.h"
void ShaderRD::_add_stage(const char *p_code, StageType p_stage_type) {
Vector<String> lines = String(p_code).split("\n");
@@ -97,6 +101,7 @@ void ShaderRD::_add_stage(const char *p_code, StageType p_stage_type) {
void ShaderRD::setup(const char *p_vertex_code, const char *p_fragment_code, const char *p_compute_code, const char *p_name) {
name = p_name;
+
if (p_compute_code) {
_add_stage(p_compute_code, STAGE_TYPE_COMPUTE);
is_compute = true;
@@ -109,6 +114,18 @@ void ShaderRD::setup(const char *p_vertex_code, const char *p_fragment_code, con
_add_stage(p_fragment_code, STAGE_TYPE_FRAGMENT);
}
}
+
+ StringBuilder tohash;
+ tohash.append("[VersionKey]");
+ tohash.append(RenderingDevice::get_singleton()->shader_get_cache_key());
+ tohash.append("[Vertex]");
+ tohash.append(p_vertex_code ? p_vertex_code : "");
+ tohash.append("[Fragment]");
+ tohash.append(p_fragment_code ? p_fragment_code : "");
+ tohash.append("[Compute]");
+ tohash.append(p_compute_code ? p_compute_code : "");
+
+ base_sha256 = tohash.as_string().sha256_text();
}
RID ShaderRD::version_create() {
@@ -131,6 +148,9 @@ void ShaderRD::_clear_version(Version *p_version) {
}
memdelete_arr(p_version->variants);
+ if (p_version->variant_stages) {
+ memdelete_arr(p_version->variant_stages);
+ }
p_version->variants = nullptr;
}
}
@@ -183,7 +203,7 @@ void ShaderRD::_compile_variant(uint32_t p_variant, Version *p_version) {
return; //variant is disabled, return
}
- Vector<RD::ShaderStageData> stages;
+ Vector<RD::ShaderStageData> &stages = p_version->variant_stages[p_variant];
String error;
String current_source;
@@ -313,6 +333,197 @@ RS::ShaderNativeSourceCode ShaderRD::version_get_native_source_code(RID p_versio
return source_code;
}
+String ShaderRD::_version_get_sha1(Version *p_version) const {
+ StringBuilder hash_build;
+
+ hash_build.append("[uniforms]");
+ hash_build.append(p_version->uniforms.get_data());
+ hash_build.append("[vertex_globals]");
+ hash_build.append(p_version->vertex_globals.get_data());
+ hash_build.append("[fragment_globals]");
+ hash_build.append(p_version->fragment_globals.get_data());
+ hash_build.append("[compute_globals]");
+ hash_build.append(p_version->compute_globals.get_data());
+
+ Vector<StringName> code_sections;
+ for (Map<StringName, CharString>::Element *E = p_version->code_sections.front(); E; E = E->next()) {
+ code_sections.push_back(E->key());
+ }
+ code_sections.sort_custom<StringName::AlphCompare>();
+
+ for (int i = 0; i < code_sections.size(); i++) {
+ hash_build.append(String("[code:") + String(code_sections[i]) + "]");
+ hash_build.append(p_version->code_sections[code_sections[i]].get_data());
+ }
+ for (int i = 0; i < p_version->custom_defines.size(); i++) {
+ hash_build.append("[custom_defines:" + itos(i) + "]");
+ hash_build.append(p_version->custom_defines[i].get_data());
+ }
+
+ return hash_build.as_string().sha1_text();
+}
+
+static const char *shader_file_header = "GDSC";
+static const uint32_t cache_file_version = 1;
+
+bool ShaderRD::_load_from_cache(Version *p_version) {
+ String sha1 = _version_get_sha1(p_version);
+ String path = shader_cache_dir.plus_file(name).plus_file(base_sha256).plus_file(sha1) + ".cache";
+
+ uint64_t time_from = OS::get_singleton()->get_ticks_usec();
+
+ FileAccessRef f = FileAccess::open(path, FileAccess::READ);
+ if (!f) {
+ return false;
+ }
+
+ char header[5] = { 0, 0, 0, 0, 0 };
+ f->get_buffer((uint8_t *)header, 4);
+ ERR_FAIL_COND_V(header != String(shader_file_header), false);
+
+ uint32_t file_version = f->get_32();
+ if (file_version != cache_file_version) {
+ return false; // wrong version
+ }
+
+ uint32_t variant_count = f->get_32();
+
+ ERR_FAIL_COND_V(variant_count != (uint32_t)variant_defines.size(), false); //should not happen but check
+
+ bool success = true;
+ for (uint32_t i = 0; i < variant_count; i++) {
+ uint32_t stage_count = f->get_32();
+ p_version->variant_stages[i].resize(stage_count);
+ for (uint32_t j = 0; j < stage_count; j++) {
+ p_version->variant_stages[i].write[j].shader_stage = RD::ShaderStage(f->get_32());
+
+ int compression = f->get_32();
+ uint32_t length = f->get_32();
+
+ if (compression == 0) {
+ Vector<uint8_t> data;
+ data.resize(length);
+
+ f->get_buffer(data.ptrw(), length);
+
+ p_version->variant_stages[i].write[j].spir_v = data;
+ } else {
+ Vector<uint8_t> data;
+
+ if (compression == 2) {
+ //zstd
+ int smol_length = f->get_32();
+ Vector<uint8_t> zstd_data;
+
+ zstd_data.resize(smol_length);
+ f->get_buffer(zstd_data.ptrw(), smol_length);
+
+ data.resize(length);
+ Compression::decompress(data.ptrw(), data.size(), zstd_data.ptr(), zstd_data.size(), Compression::MODE_ZSTD);
+
+ } else {
+ data.resize(length);
+ f->get_buffer(data.ptrw(), length);
+ }
+
+ Vector<uint8_t> spirv;
+ uint32_t spirv_size = smolv::GetDecodedBufferSize(data.ptr(), data.size());
+ spirv.resize(spirv_size);
+ if (!smolv::Decode(data.ptr(), data.size(), spirv.ptrw(), spirv_size)) {
+ ERR_PRINT("Malformed smolv input uncompressing shader " + name + ", variant #" + itos(i) + " stage :" + itos(j));
+ success = false;
+ break;
+ }
+ p_version->variant_stages[i].write[j].spir_v = spirv;
+ }
+ }
+ }
+
+ if (!success) {
+ for (uint32_t i = 0; i < variant_count; i++) {
+ p_version->variant_stages[i].resize(0);
+ }
+ return false;
+ }
+
+ float time_ms = double(OS::get_singleton()->get_ticks_usec() - time_from) / 1000.0;
+
+ print_verbose("Shader cache load success '" + path + "' " + rtos(time_ms) + "ms.");
+
+ for (uint32_t i = 0; i < variant_count; i++) {
+ RID shader = RD::get_singleton()->shader_create(p_version->variant_stages[i]);
+ {
+ MutexLock lock(variant_set_mutex);
+ p_version->variants[i] = shader;
+ }
+ }
+
+ memdelete_arr(p_version->variant_stages); //clear stages
+ p_version->variant_stages = nullptr;
+ p_version->valid = true;
+ return true;
+}
+
+void ShaderRD::_save_to_cache(Version *p_version) {
+ String sha1 = _version_get_sha1(p_version);
+ String path = shader_cache_dir.plus_file(name).plus_file(base_sha256).plus_file(sha1) + ".cache";
+
+ FileAccessRef f = FileAccess::open(path, FileAccess::WRITE);
+ ERR_FAIL_COND(!f);
+ f->store_buffer((const uint8_t *)shader_file_header, 4);
+ f->store_32(cache_file_version); //file version
+ uint32_t variant_count = variant_defines.size();
+ f->store_32(variant_count); //variant count
+
+ for (uint32_t i = 0; i < variant_count; i++) {
+ f->store_32(p_version->variant_stages[i].size()); //stage count
+ for (int j = 0; j < p_version->variant_stages[i].size(); j++) {
+ f->store_32(p_version->variant_stages[i][j].shader_stage); //stage count
+ Vector<uint8_t> spirv = p_version->variant_stages[i][j].spir_v;
+
+ bool save_uncompressed = true;
+ if (shader_cache_save_compressed) {
+ smolv::ByteArray smolv;
+ bool strip_debug = !shader_cache_save_debug;
+ if (!smolv::Encode(spirv.ptr(), spirv.size(), smolv, strip_debug ? smolv::kEncodeFlagStripDebugInfo : 0)) {
+ ERR_PRINT("Error compressing shader " + name + ", variant #" + itos(i) + " stage :" + itos(i));
+ } else {
+ bool compress_zstd = shader_cache_save_compressed_zstd;
+
+ if (compress_zstd) {
+ Vector<uint8_t> zstd;
+ zstd.resize(Compression::get_max_compressed_buffer_size(smolv.size(), Compression::MODE_ZSTD));
+ int dst_size = Compression::compress(zstd.ptrw(), &smolv[0], smolv.size(), Compression::MODE_ZSTD);
+ if (dst_size >= 0 && (uint32_t)dst_size < smolv.size()) {
+ f->store_32(2); //compressed zstd
+ f->store_32(smolv.size()); //size of smolv buffer
+ f->store_32(dst_size); //size of smolv buffer
+ f->store_buffer(zstd.ptr(), dst_size); //smolv buffer
+ } else {
+ compress_zstd = false;
+ }
+ }
+
+ if (!compress_zstd) {
+ f->store_32(1); //compressed
+ f->store_32(smolv.size()); //size of smolv buffer
+ f->store_buffer(&smolv[0], smolv.size()); //smolv buffer
+ }
+ save_uncompressed = false;
+ }
+ }
+
+ if (save_uncompressed) {
+ f->store_32(0); //uncompressed
+ f->store_32(spirv.size()); //stage count
+ f->store_buffer(spirv.ptr(), spirv.size()); //stage count
+ }
+ }
+ }
+
+ f->close();
+}
+
void ShaderRD::_compile_version(Version *p_version) {
_clear_version(p_version);
@@ -320,6 +531,15 @@ void ShaderRD::_compile_version(Version *p_version) {
p_version->dirty = false;
p_version->variants = memnew_arr(RID, variant_defines.size());
+ typedef Vector<RD::ShaderStageData> ShaderStageArray;
+ p_version->variant_stages = memnew_arr(ShaderStageArray, variant_defines.size());
+
+ if (shader_cache_dir_valid) {
+ if (_load_from_cache(p_version)) {
+ return;
+ }
+ }
+
#if 1
RendererThreadPool::singleton->thread_work_pool.do_work(variant_defines.size(), this, &ShaderRD::_compile_variant, p_version);
@@ -351,10 +571,20 @@ void ShaderRD::_compile_version(Version *p_version) {
}
}
memdelete_arr(p_version->variants);
+ if (p_version->variant_stages) {
+ memdelete_arr(p_version->variant_stages);
+ }
p_version->variants = nullptr;
+ p_version->variant_stages = nullptr;
return;
+ } else if (shader_cache_dir_valid) {
+ //save shader cache
+ _save_to_cache(p_version);
}
+ memdelete_arr(p_version->variant_stages); //clear stages
+ p_version->variant_stages = nullptr;
+
p_version->valid = true;
}
@@ -443,6 +673,8 @@ bool ShaderRD::is_variant_enabled(int p_variant) const {
return variants_enabled[p_variant];
}
+bool ShaderRD::shader_cache_cleanup_on_start = false;
+
ShaderRD::ShaderRD() {
// Do not feel forced to use this, in most cases it makes little to no difference.
bool use_32_threads = false;
@@ -469,8 +701,64 @@ void ShaderRD::initialize(const Vector<String> &p_variant_defines, const String
variant_defines.push_back(p_variant_defines[i].utf8());
variants_enabled.push_back(true);
}
+
+ if (shader_cache_dir != String()) {
+ StringBuilder hash_build;
+
+ hash_build.append("[base_hash]");
+ hash_build.append(base_sha256);
+ hash_build.append("[general_defines]");
+ hash_build.append(general_defines.get_data());
+ for (int i = 0; i < variant_defines.size(); i++) {
+ hash_build.append("[variant_defines:" + itos(i) + "]");
+ hash_build.append(variant_defines[i].get_data());
+ }
+
+ base_sha256 = hash_build.as_string().sha256_text();
+
+ DirAccessRef d = DirAccess::open(shader_cache_dir);
+ ERR_FAIL_COND(!d);
+ if (d->change_dir(name) != OK) {
+ Error err = d->make_dir(name);
+ ERR_FAIL_COND(err != OK);
+ d->change_dir(name);
+ }
+
+ //erase other versions?
+ if (shader_cache_cleanup_on_start) {
+ }
+ //
+ if (d->change_dir(base_sha256) != OK) {
+ Error err = d->make_dir(base_sha256);
+ ERR_FAIL_COND(err != OK);
+ }
+ shader_cache_dir_valid = true;
+
+ print_verbose("Shader '" + name + "' SHA256: " + base_sha256);
+ }
+}
+
+void ShaderRD::set_shader_cache_dir(const String &p_dir) {
+ shader_cache_dir = p_dir;
+}
+
+void ShaderRD::set_shader_cache_save_compressed(bool p_enable) {
+ shader_cache_save_compressed = p_enable;
}
+void ShaderRD::set_shader_cache_save_compressed_zstd(bool p_enable) {
+ shader_cache_save_compressed_zstd = p_enable;
+}
+
+void ShaderRD::set_shader_cache_save_debug(bool p_enable) {
+ shader_cache_save_debug = p_enable;
+}
+
+String ShaderRD::shader_cache_dir;
+bool ShaderRD::shader_cache_save_compressed = true;
+bool ShaderRD::shader_cache_save_compressed_zstd = true;
+bool ShaderRD::shader_cache_save_debug = true;
+
ShaderRD::~ShaderRD() {
List<RID> remaining;
version_owner.get_owned_list(&remaining);
diff --git a/servers/rendering/renderer_rd/shader_rd.h b/servers/rendering/renderer_rd/shader_rd.h
index f20d539621..9a68e02007 100644
--- a/servers/rendering/renderer_rd/shader_rd.h
+++ b/servers/rendering/renderer_rd/shader_rd.h
@@ -59,7 +59,8 @@ class ShaderRD {
Map<StringName, CharString> code_sections;
Vector<CharString> custom_defines;
- RID *variants; //same size as version defines
+ Vector<RD::ShaderStageData> *variant_stages = nullptr;
+ RID *variants = nullptr; //same size as version defines
bool valid;
bool dirty;
@@ -96,10 +97,19 @@ class ShaderRD {
bool is_compute = false;
- const char *name;
+ String name;
CharString base_compute_defines;
+ String base_sha256;
+
+ static String shader_cache_dir;
+ static bool shader_cache_cleanup_on_start;
+ static bool shader_cache_save_compressed;
+ static bool shader_cache_save_compressed_zstd;
+ static bool shader_cache_save_debug;
+ bool shader_cache_dir_valid = false;
+
enum StageType {
STAGE_TYPE_VERTEX,
STAGE_TYPE_FRAGMENT,
@@ -113,6 +123,10 @@ class ShaderRD {
void _add_stage(const char *p_code, StageType p_stage_type);
+ String _version_get_sha1(Version *p_version) const;
+ bool _load_from_cache(Version *p_version);
+ void _save_to_cache(Version *p_version);
+
protected:
ShaderRD();
void setup(const char *p_vertex_code, const char *p_fragment_code, const char *p_compute_code, const char *p_name);
@@ -148,6 +162,11 @@ public:
void set_variant_enabled(int p_variant, bool p_enabled);
bool is_variant_enabled(int p_variant) const;
+ static void set_shader_cache_dir(const String &p_dir);
+ static void set_shader_cache_save_compressed(bool p_enable);
+ static void set_shader_cache_save_compressed_zstd(bool p_enable);
+ static void set_shader_cache_save_debug(bool p_enable);
+
RS::ShaderNativeSourceCode version_get_native_source_code(RID p_version);
void initialize(const Vector<String> &p_variant_defines, const String &p_general_defines = "");
diff --git a/servers/rendering/rendering_device.cpp b/servers/rendering/rendering_device.cpp
index e6ad001807..056cec4c1f 100644
--- a/servers/rendering/rendering_device.cpp
+++ b/servers/rendering/rendering_device.cpp
@@ -40,6 +40,7 @@ RenderingDevice *RenderingDevice::get_singleton() {
RenderingDevice::ShaderCompileFunction RenderingDevice::compile_function = nullptr;
RenderingDevice::ShaderCacheFunction RenderingDevice::cache_function = nullptr;
+RenderingDevice::ShaderGetCacheKeyFunction RenderingDevice::get_cache_key_function = nullptr;
void RenderingDevice::shader_set_compile_function(ShaderCompileFunction p_function) {
compile_function = p_function;
@@ -49,6 +50,10 @@ void RenderingDevice::shader_set_cache_function(ShaderCacheFunction p_function)
cache_function = p_function;
}
+void RenderingDevice::shader_set_get_cache_key_function(ShaderGetCacheKeyFunction p_function) {
+ get_cache_key_function = p_function;
+}
+
Vector<uint8_t> RenderingDevice::shader_compile_from_source(ShaderStage p_stage, const String &p_source_code, ShaderLanguage p_language, String *r_error, bool p_allow_cache) {
if (p_allow_cache && cache_function) {
Vector<uint8_t> cache = cache_function(p_stage, p_source_code, p_language);
@@ -62,6 +67,13 @@ Vector<uint8_t> RenderingDevice::shader_compile_from_source(ShaderStage p_stage,
return compile_function(p_stage, p_source_code, p_language, r_error, &device_capabilities);
}
+String RenderingDevice::shader_get_cache_key() const {
+ if (get_cache_key_function) {
+ return get_cache_key_function(&device_capabilities);
+ }
+ return String();
+}
+
RID RenderingDevice::_texture_create(const Ref<RDTextureFormat> &p_format, const Ref<RDTextureView> &p_view, const TypedArray<PackedByteArray> &p_data) {
ERR_FAIL_COND_V(p_format.is_null(), RID());
ERR_FAIL_COND_V(p_view.is_null(), RID());
diff --git a/servers/rendering/rendering_device.h b/servers/rendering/rendering_device.h
index 27bded9810..4dcb9b963e 100644
--- a/servers/rendering/rendering_device.h
+++ b/servers/rendering/rendering_device.h
@@ -103,12 +103,14 @@ public:
bool supports_multiview = false; // If true this device supports multiview options
};
+ typedef String (*ShaderGetCacheKeyFunction)(const Capabilities *p_capabilities);
typedef Vector<uint8_t> (*ShaderCompileFunction)(ShaderStage p_stage, const String &p_source_code, ShaderLanguage p_language, String *r_error, const Capabilities *p_capabilities);
typedef Vector<uint8_t> (*ShaderCacheFunction)(ShaderStage p_stage, const String &p_source_code, ShaderLanguage p_language);
private:
static ShaderCompileFunction compile_function;
static ShaderCacheFunction cache_function;
+ static ShaderGetCacheKeyFunction get_cache_key_function;
static RenderingDevice *singleton;
@@ -635,9 +637,11 @@ public:
const Capabilities *get_device_capabilities() const { return &device_capabilities; };
virtual Vector<uint8_t> shader_compile_from_source(ShaderStage p_stage, const String &p_source_code, ShaderLanguage p_language = SHADER_LANGUAGE_GLSL, String *r_error = nullptr, bool p_allow_cache = true);
+ virtual String shader_get_cache_key() const;
static void shader_set_compile_function(ShaderCompileFunction p_function);
static void shader_set_cache_function(ShaderCacheFunction p_function);
+ static void shader_set_get_cache_key_function(ShaderGetCacheKeyFunction p_function);
struct ShaderStageData {
ShaderStage shader_stage;
diff --git a/servers/rendering/shader_language.cpp b/servers/rendering/shader_language.cpp
index 2ce7707257..14b21e1f42 100644
--- a/servers/rendering/shader_language.cpp
+++ b/servers/rendering/shader_language.cpp
@@ -926,6 +926,8 @@ void ShaderLanguage::clear() {
char_idx = 0;
error_set = false;
error_str = "";
+ last_const = false;
+ pass_array = false;
while (nodes) {
Node *n = nodes;
nodes = nodes->next;
@@ -973,7 +975,6 @@ bool ShaderLanguage::_find_identifier(const BlockNode *p_block, bool p_allow_rea
if (r_type) {
*r_type = IDENTIFIER_BUILTIN_VAR;
}
-
return true;
}
@@ -987,7 +988,6 @@ bool ShaderLanguage::_find_identifier(const BlockNode *p_block, bool p_allow_rea
if (r_type) {
*r_type = IDENTIFIER_FUNCTION;
}
-
return true;
}
@@ -1004,16 +1004,15 @@ bool ShaderLanguage::_find_identifier(const BlockNode *p_block, bool p_allow_rea
if (r_array_size) {
*r_array_size = p_block->variables[p_identifier].array_size;
}
- if (r_type) {
- *r_type = IDENTIFIER_LOCAL_VAR;
- }
if (r_struct_name) {
*r_struct_name = p_block->variables[p_identifier].struct_name;
}
if (r_constant_value) {
*r_constant_value = p_block->variables[p_identifier].value;
}
-
+ if (r_type) {
+ *r_type = IDENTIFIER_LOCAL_VAR;
+ }
return true;
}
@@ -1035,15 +1034,18 @@ bool ShaderLanguage::_find_identifier(const BlockNode *p_block, bool p_allow_rea
if (r_data_type) {
*r_data_type = function->arguments[i].type;
}
- if (r_type) {
- *r_type = IDENTIFIER_FUNCTION_ARGUMENT;
- }
if (r_struct_name) {
*r_struct_name = function->arguments[i].type_str;
}
+ if (r_array_size) {
+ *r_array_size = function->arguments[i].array_size;
+ }
if (r_is_const) {
*r_is_const = function->arguments[i].is_const;
}
+ if (r_type) {
+ *r_type = IDENTIFIER_FUNCTION_ARGUMENT;
+ }
return true;
}
}
@@ -1082,9 +1084,6 @@ bool ShaderLanguage::_find_identifier(const BlockNode *p_block, bool p_allow_rea
if (r_array_size) {
*r_array_size = shader->constants[p_identifier].array_size;
}
- if (r_type) {
- *r_type = IDENTIFIER_CONSTANT;
- }
if (r_struct_name) {
*r_struct_name = shader->constants[p_identifier].type_str;
}
@@ -1093,6 +1092,9 @@ bool ShaderLanguage::_find_identifier(const BlockNode *p_block, bool p_allow_rea
*r_constant_value = shader->constants[p_identifier].initializer->values[0];
}
}
+ if (r_type) {
+ *r_type = IDENTIFIER_CONSTANT;
+ }
return true;
}
@@ -1105,6 +1107,9 @@ bool ShaderLanguage::_find_identifier(const BlockNode *p_block, bool p_allow_rea
if (r_data_type) {
*r_data_type = shader->functions[i].function->return_type;
}
+ if (r_array_size) {
+ *r_array_size = shader->functions[i].function->return_array_size;
+ }
if (r_type) {
*r_type = IDENTIFIER_FUNCTION;
}
@@ -1115,13 +1120,18 @@ bool ShaderLanguage::_find_identifier(const BlockNode *p_block, bool p_allow_rea
return false;
}
-bool ShaderLanguage::_validate_operator(OperatorNode *p_op, DataType *r_ret_type) {
+bool ShaderLanguage::_validate_operator(OperatorNode *p_op, DataType *r_ret_type, int *r_ret_size) {
bool valid = false;
DataType ret_type = TYPE_VOID;
+ int ret_size = 0;
switch (p_op->op) {
case OP_EQUAL:
case OP_NOT_EQUAL: {
+ if ((!p_op->arguments[0]->is_indexed() && p_op->arguments[0]->get_array_size() > 0) || (!p_op->arguments[1]->is_indexed() && p_op->arguments[1]->get_array_size() > 0)) {
+ break; // don't accept arrays
+ }
+
DataType na = p_op->arguments[0]->get_datatype();
DataType nb = p_op->arguments[1]->get_datatype();
valid = na == nb;
@@ -1131,6 +1141,10 @@ bool ShaderLanguage::_validate_operator(OperatorNode *p_op, DataType *r_ret_type
case OP_LESS_EQUAL:
case OP_GREATER:
case OP_GREATER_EQUAL: {
+ if ((!p_op->arguments[0]->is_indexed() && p_op->arguments[0]->get_array_size() > 0) || (!p_op->arguments[1]->is_indexed() && p_op->arguments[1]->get_array_size() > 0)) {
+ break; // don't accept arrays
+ }
+
DataType na = p_op->arguments[0]->get_datatype();
DataType nb = p_op->arguments[1]->get_datatype();
@@ -1140,6 +1154,10 @@ bool ShaderLanguage::_validate_operator(OperatorNode *p_op, DataType *r_ret_type
} break;
case OP_AND:
case OP_OR: {
+ if ((!p_op->arguments[0]->is_indexed() && p_op->arguments[0]->get_array_size() > 0) || (!p_op->arguments[1]->is_indexed() && p_op->arguments[1]->get_array_size() > 0)) {
+ break; // don't accept arrays
+ }
+
DataType na = p_op->arguments[0]->get_datatype();
DataType nb = p_op->arguments[1]->get_datatype();
@@ -1148,6 +1166,10 @@ bool ShaderLanguage::_validate_operator(OperatorNode *p_op, DataType *r_ret_type
} break;
case OP_NOT: {
+ if (!p_op->arguments[0]->is_indexed() && p_op->arguments[0]->get_array_size() > 0) {
+ break; // don't accept arrays
+ }
+
DataType na = p_op->arguments[0]->get_datatype();
valid = na == TYPE_BOOL;
ret_type = TYPE_BOOL;
@@ -1158,6 +1180,10 @@ bool ShaderLanguage::_validate_operator(OperatorNode *p_op, DataType *r_ret_type
case OP_POST_INCREMENT:
case OP_POST_DECREMENT:
case OP_NEGATE: {
+ if (!p_op->arguments[0]->is_indexed() && p_op->arguments[0]->get_array_size() > 0) {
+ break; // don't accept arrays
+ }
+
DataType na = p_op->arguments[0]->get_datatype();
valid = na > TYPE_BOOL && na < TYPE_MAT2;
ret_type = na;
@@ -1166,6 +1192,10 @@ bool ShaderLanguage::_validate_operator(OperatorNode *p_op, DataType *r_ret_type
case OP_SUB:
case OP_MUL:
case OP_DIV: {
+ if ((!p_op->arguments[0]->is_indexed() && p_op->arguments[0]->get_array_size() > 0) || (!p_op->arguments[1]->is_indexed() && p_op->arguments[1]->get_array_size() > 0)) {
+ break; // don't accept arrays
+ }
+
DataType na = p_op->arguments[0]->get_datatype();
DataType nb = p_op->arguments[1]->get_datatype();
@@ -1234,6 +1264,10 @@ bool ShaderLanguage::_validate_operator(OperatorNode *p_op, DataType *r_ret_type
* component-wise.
*/
+ if ((!p_op->arguments[0]->is_indexed() && p_op->arguments[0]->get_array_size() > 0) || (!p_op->arguments[1]->is_indexed() && p_op->arguments[1]->get_array_size() > 0)) {
+ break; // don't accept arrays
+ }
+
DataType na = p_op->arguments[0]->get_datatype();
DataType nb = p_op->arguments[1]->get_datatype();
@@ -1286,6 +1320,10 @@ bool ShaderLanguage::_validate_operator(OperatorNode *p_op, DataType *r_ret_type
case OP_ASSIGN_SHIFT_RIGHT:
case OP_SHIFT_LEFT:
case OP_SHIFT_RIGHT: {
+ if ((!p_op->arguments[0]->is_indexed() && p_op->arguments[0]->get_array_size() > 0) || (!p_op->arguments[1]->is_indexed() && p_op->arguments[1]->get_array_size() > 0)) {
+ break; // don't accept arrays
+ }
+
DataType na = p_op->arguments[0]->get_datatype();
DataType nb = p_op->arguments[1]->get_datatype();
@@ -1334,6 +1372,18 @@ bool ShaderLanguage::_validate_operator(OperatorNode *p_op, DataType *r_ret_type
}
} break;
case OP_ASSIGN: {
+ int sa = 0;
+ int sb = 0;
+ if (!p_op->arguments[0]->is_indexed()) {
+ sa = p_op->arguments[0]->get_array_size();
+ }
+ if (!p_op->arguments[1]->is_indexed()) {
+ sb = p_op->arguments[1]->get_array_size();
+ }
+ if (sa != sb) {
+ break; // don't accept arrays if their sizes are not equal
+ }
+
DataType na = p_op->arguments[0]->get_datatype();
DataType nb = p_op->arguments[1]->get_datatype();
if (na == TYPE_STRUCT || nb == TYPE_STRUCT) {
@@ -1342,11 +1392,24 @@ bool ShaderLanguage::_validate_operator(OperatorNode *p_op, DataType *r_ret_type
valid = na == nb;
}
ret_type = na;
+ ret_size = sa;
} break;
case OP_ASSIGN_ADD:
case OP_ASSIGN_SUB:
case OP_ASSIGN_MUL:
case OP_ASSIGN_DIV: {
+ int sa = 0;
+ int sb = 0;
+ if (!p_op->arguments[0]->is_indexed()) {
+ sa = p_op->arguments[0]->get_array_size();
+ }
+ if (!p_op->arguments[1]->is_indexed()) {
+ sb = p_op->arguments[1]->get_array_size();
+ }
+ if (sa > 0 || sb > 0) {
+ break; // don't accept arrays
+ }
+
DataType na = p_op->arguments[0]->get_datatype();
DataType nb = p_op->arguments[1]->get_datatype();
@@ -1414,6 +1477,18 @@ bool ShaderLanguage::_validate_operator(OperatorNode *p_op, DataType *r_ret_type
* must match.
*/
+ int sa = 0;
+ int sb = 0;
+ if (!p_op->arguments[0]->is_indexed()) {
+ sa = p_op->arguments[0]->get_array_size();
+ }
+ if (!p_op->arguments[1]->is_indexed()) {
+ sb = p_op->arguments[1]->get_array_size();
+ }
+ if (sa > 0 || sb > 0) {
+ break; // don't accept arrays
+ }
+
DataType na = p_op->arguments[0]->get_datatype();
DataType nb = p_op->arguments[1]->get_datatype();
@@ -1468,17 +1543,34 @@ bool ShaderLanguage::_validate_operator(OperatorNode *p_op, DataType *r_ret_type
}
} break;
case OP_BIT_INVERT: { //unaries
+ if (!p_op->arguments[0]->is_indexed() && p_op->arguments[0]->get_array_size() > 0) {
+ break; // don't accept arrays
+ }
+
DataType na = p_op->arguments[0]->get_datatype();
valid = na >= TYPE_INT && na < TYPE_FLOAT;
ret_type = na;
} break;
case OP_SELECT_IF: {
+ int sa = 0;
+ int sb = 0;
+ if (!p_op->arguments[1]->is_indexed()) {
+ sa = p_op->arguments[1]->get_array_size();
+ }
+ if (!p_op->arguments[2]->is_indexed()) {
+ sb = p_op->arguments[2]->get_array_size();
+ }
+ if (sa != sb) {
+ break; // don't accept arrays if their sizes are not equal
+ }
+
DataType na = p_op->arguments[0]->get_datatype();
DataType nb = p_op->arguments[1]->get_datatype();
DataType nc = p_op->arguments[2]->get_datatype();
valid = na == TYPE_BOOL && (nb == nc);
ret_type = nb;
+ ret_size = sa;
} break;
default: {
ERR_FAIL_V(false);
@@ -1488,6 +1580,9 @@ bool ShaderLanguage::_validate_operator(OperatorNode *p_op, DataType *r_ret_type
if (r_ret_type) {
*r_ret_type = ret_type;
}
+ if (r_ret_size) {
+ *r_ret_size = ret_size;
+ }
return valid;
}
@@ -2223,6 +2318,7 @@ bool ShaderLanguage::_validate_function_call(BlockNode *p_block, const FunctionI
Vector<DataType> args;
Vector<StringName> args2;
+ Vector<int> args3;
ERR_FAIL_COND_V(p_func->arguments[0]->type != Node::TYPE_VARIABLE, false);
@@ -2231,6 +2327,7 @@ bool ShaderLanguage::_validate_function_call(BlockNode *p_block, const FunctionI
for (int i = 1; i < p_func->arguments.size(); i++) {
args.push_back(p_func->arguments[i]->get_datatype());
args2.push_back(p_func->arguments[i]->get_datatype_name());
+ args3.push_back(p_func->arguments[i]->get_array_size());
}
int argcount = args.size();
@@ -2498,6 +2595,11 @@ bool ShaderLanguage::_validate_function_call(BlockNode *p_block, const FunctionI
} else {
func_arg_name = get_datatype_name(pfunc->arguments[j].type);
}
+ if (pfunc->arguments[j].array_size > 0) {
+ func_arg_name += "[";
+ func_arg_name += itos(pfunc->arguments[j].array_size);
+ func_arg_name += "]";
+ }
arg_list += func_arg_name;
}
}
@@ -2510,21 +2612,32 @@ bool ShaderLanguage::_validate_function_call(BlockNode *p_block, const FunctionI
bool fail = false;
for (int j = 0; j < args.size(); j++) {
- if (get_scalar_type(args[j]) == args[j] && p_func->arguments[j + 1]->type == Node::TYPE_CONSTANT && convert_constant(static_cast<ConstantNode *>(p_func->arguments[j + 1]), pfunc->arguments[j].type)) {
+ if (get_scalar_type(args[j]) == args[j] && p_func->arguments[j + 1]->type == Node::TYPE_CONSTANT && args3[j] == 0 && convert_constant(static_cast<ConstantNode *>(p_func->arguments[j + 1]), pfunc->arguments[j].type)) {
//all good, but it needs implicit conversion later
- } else if (args[j] != pfunc->arguments[j].type || (args[j] == TYPE_STRUCT && args2[j] != pfunc->arguments[j].type_str)) {
+ } else if (args[j] != pfunc->arguments[j].type || (args[j] == TYPE_STRUCT && args2[j] != pfunc->arguments[j].type_str) || args3[j] != pfunc->arguments[j].array_size) {
String func_arg_name;
if (pfunc->arguments[j].type == TYPE_STRUCT) {
func_arg_name = pfunc->arguments[j].type_str;
} else {
func_arg_name = get_datatype_name(pfunc->arguments[j].type);
}
+ if (pfunc->arguments[j].array_size > 0) {
+ func_arg_name += "[";
+ func_arg_name += itos(pfunc->arguments[j].array_size);
+ func_arg_name += "]";
+ }
String arg_name;
if (args[j] == TYPE_STRUCT) {
arg_name = args2[j];
} else {
arg_name = get_datatype_name(args[j]);
}
+ if (args3[j] > 0) {
+ arg_name += "[";
+ arg_name += itos(args3[j]);
+ arg_name += "]";
+ }
+
_set_error(vformat("Invalid argument for \"%s(%s)\" function: argument %s should be %s but is %s.", String(name), arg_list, j + 1, func_arg_name, arg_name));
fail = true;
break;
@@ -2570,16 +2683,45 @@ bool ShaderLanguage::_validate_function_call(BlockNode *p_block, const FunctionI
return false;
}
-bool ShaderLanguage::_compare_datatypes_in_nodes(Node *a, Node *b) const {
- if (a->get_datatype() != b->get_datatype()) {
- return false;
+bool ShaderLanguage::_compare_datatypes(DataType p_datatype_a, String p_datatype_name_a, int p_array_size_a, DataType p_datatype_b, String p_datatype_name_b, int p_array_size_b) {
+ bool result = true;
+
+ if (p_datatype_a == TYPE_STRUCT || p_datatype_b == TYPE_STRUCT) {
+ if (p_datatype_name_a != p_datatype_name_b) {
+ result = false;
+ }
+ } else {
+ if (p_datatype_a != p_datatype_b) {
+ result = false;
+ }
}
- if (a->get_datatype() == TYPE_STRUCT || b->get_datatype() == TYPE_STRUCT) {
- if (a->get_datatype_name() != b->get_datatype_name()) {
- return false;
+
+ if (p_array_size_a != p_array_size_b) {
+ result = false;
+ }
+
+ if (!result) {
+ String type_name = p_datatype_a == TYPE_STRUCT ? p_datatype_name_a : get_datatype_name(p_datatype_a);
+ if (p_array_size_a > 0) {
+ type_name += "[";
+ type_name += itos(p_array_size_a);
+ type_name += "]";
}
+
+ String type_name2 = p_datatype_b == TYPE_STRUCT ? p_datatype_name_b : get_datatype_name(p_datatype_b);
+ if (p_array_size_b > 0) {
+ type_name2 += "[";
+ type_name2 += itos(p_array_size_b);
+ type_name2 += "]";
+ }
+
+ _set_error("Invalid assignment of '" + type_name2 + "' to '" + type_name + "'");
}
- return true;
+ return result;
+}
+
+bool ShaderLanguage::_compare_datatypes_in_nodes(Node *a, Node *b) {
+ return _compare_datatypes(a->get_datatype(), a->get_datatype_name(), a->get_array_size(), b->get_datatype(), b->get_datatype_name(), b->get_array_size());
}
bool ShaderLanguage::_parse_function_arguments(BlockNode *p_block, const FunctionInfo &p_function_info, OperatorNode *p_func, int *r_complete_arg) {
@@ -3390,11 +3532,12 @@ bool ShaderLanguage::_propagate_function_call_sampler_builtin_reference(StringNa
ERR_FAIL_V(false); //bug? function not found
}
-ShaderLanguage::Node *ShaderLanguage::_parse_array_constructor(BlockNode *p_block, const FunctionInfo &p_function_info, DataType p_type, const StringName &p_struct_name, int p_array_size) {
+ShaderLanguage::Node *ShaderLanguage::_parse_array_constructor(BlockNode *p_block, const FunctionInfo &p_function_info) {
DataType type = TYPE_VOID;
String struct_name = "";
int array_size = 0;
bool auto_size = false;
+ bool undefined_size = false;
Token tk = _get_token();
if (tk.type == TK_CURLY_BRACKET_OPEN) {
@@ -3415,6 +3558,137 @@ ShaderLanguage::Node *ShaderLanguage::_parse_array_constructor(BlockNode *p_bloc
TkPos pos = _get_tkpos();
tk = _get_token();
if (tk.type == TK_BRACKET_CLOSE) {
+ undefined_size = true;
+ tk = _get_token();
+ } else {
+ _set_tkpos(pos);
+
+ Node *n = _parse_and_reduce_expression(p_block, p_function_info);
+ if (!n || n->type != Node::TYPE_CONSTANT || n->get_datatype() != TYPE_INT) {
+ _set_error("Expected single integer constant > 0");
+ return nullptr;
+ }
+
+ ConstantNode *cnode = (ConstantNode *)n;
+ if (cnode->values.size() == 1) {
+ array_size = cnode->values[0].sint;
+ if (array_size <= 0) {
+ _set_error("Expected single integer constant > 0");
+ return nullptr;
+ }
+ } else {
+ _set_error("Expected single integer constant > 0");
+ return nullptr;
+ }
+
+ tk = _get_token();
+ if (tk.type != TK_BRACKET_CLOSE) {
+ _set_error("Expected ']'");
+ return nullptr;
+ } else {
+ tk = _get_token();
+ }
+ }
+ } else {
+ _set_error("Expected '['");
+ return nullptr;
+ }
+ }
+
+ ArrayConstructNode *an = alloc_node<ArrayConstructNode>();
+
+ if (tk.type == TK_PARENTHESIS_OPEN || auto_size) { // initialization
+ int idx = 0;
+ while (true) {
+ Node *n = _parse_and_reduce_expression(p_block, p_function_info);
+ if (!n) {
+ return nullptr;
+ }
+
+ // define type by using the first member
+ if (auto_size && idx == 0) {
+ type = n->get_datatype();
+ if (type == TYPE_STRUCT) {
+ struct_name = n->get_datatype_name();
+ }
+ } else {
+ if (!_compare_datatypes(type, struct_name, 0, n->get_datatype(), n->get_datatype_name(), 0)) {
+ return nullptr;
+ }
+ }
+
+ tk = _get_token();
+ if (tk.type == TK_COMMA) {
+ an->initializer.push_back(n);
+ } else if (!auto_size && tk.type == TK_PARENTHESIS_CLOSE) {
+ an->initializer.push_back(n);
+ break;
+ } else if (auto_size && tk.type == TK_CURLY_BRACKET_CLOSE) {
+ an->initializer.push_back(n);
+ break;
+ } else {
+ if (auto_size) {
+ _set_error("Expected '}' or ','");
+ } else {
+ _set_error("Expected ')' or ','");
+ }
+ return nullptr;
+ }
+ idx++;
+ }
+ if (!auto_size && !undefined_size && an->initializer.size() != array_size) {
+ _set_error("Array size mismatch");
+ return nullptr;
+ }
+ } else {
+ _set_error("Expected array initialization!");
+ return nullptr;
+ }
+
+ an->datatype = type;
+ an->struct_name = struct_name;
+ return an;
+}
+
+ShaderLanguage::Node *ShaderLanguage::_parse_array_constructor(BlockNode *p_block, const FunctionInfo &p_function_info, DataType p_type, const StringName &p_struct_name, int p_array_size) {
+ DataType type = TYPE_VOID;
+ String struct_name = "";
+ int array_size = 0;
+ bool auto_size = false;
+ TkPos prev_pos = _get_tkpos();
+ Token tk = _get_token();
+
+ if (tk.type == TK_CURLY_BRACKET_OPEN) {
+ auto_size = true;
+ } else {
+ if (shader->structs.has(tk.text)) {
+ type = TYPE_STRUCT;
+ struct_name = tk.text;
+ } else {
+ if (!is_token_variable_datatype(tk.type)) {
+ _set_tkpos(prev_pos);
+
+ pass_array = true;
+ Node *n = _parse_and_reduce_expression(p_block, p_function_info);
+ pass_array = false;
+
+ if (!n) {
+ _set_error("Invalid data type for array");
+ return nullptr;
+ }
+
+ if (!_compare_datatypes(p_type, p_struct_name, p_array_size, n->get_datatype(), n->get_datatype_name(), n->get_array_size())) {
+ return nullptr;
+ }
+ return n;
+ }
+ type = get_token_datatype(tk.type);
+ }
+ tk = _get_token();
+ if (tk.type == TK_BRACKET_OPEN) {
+ TkPos pos = _get_tkpos();
+ tk = _get_token();
+ if (tk.type == TK_BRACKET_CLOSE) {
array_size = p_array_size;
tk = _get_token();
} else {
@@ -3486,8 +3760,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_array_constructor(BlockNode *p_bloc
return nullptr;
}
- if (p_type != n->get_datatype() || p_struct_name != n->get_datatype_name()) {
- _set_error("Invalid assignment of '" + (n->get_datatype() == TYPE_STRUCT ? n->get_datatype_name() : get_datatype_name(n->get_datatype())) + "' to '" + (type == TYPE_STRUCT ? struct_name : get_datatype_name(type)) + "'");
+ if (!_compare_datatypes(p_type, p_struct_name, 0, n->get_datatype(), n->get_datatype_name(), 0)) {
return nullptr;
}
@@ -3587,50 +3860,73 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
//make sure void is not used in expression
_set_error("Void value not allowed in Expression");
return nullptr;
- } else if (is_token_nonvoid_datatype(tk.type)) {
- //basic type constructor
+ } else if (is_token_nonvoid_datatype(tk.type) || tk.type == TK_CURLY_BRACKET_OPEN) {
+ if (tk.type == TK_CURLY_BRACKET_OPEN) {
+ //array constructor
- OperatorNode *func = alloc_node<OperatorNode>();
- func->op = OP_CONSTRUCT;
+ _set_tkpos(prepos);
+ expr = _parse_array_constructor(p_block, p_function_info);
+ } else {
+ DataType datatype;
+ DataPrecision precision;
+ bool precision_defined = false;
- if (is_token_precision(tk.type)) {
- func->return_precision_cache = get_token_precision(tk.type);
+ if (is_token_precision(tk.type)) {
+ precision = get_token_precision(tk.type);
+ precision_defined = true;
+ tk = _get_token();
+ }
+
+ datatype = get_token_datatype(tk.type);
tk = _get_token();
- }
- VariableNode *funcname = alloc_node<VariableNode>();
- funcname->name = get_datatype_name(get_token_datatype(tk.type));
- func->arguments.push_back(funcname);
+ if (tk.type == TK_BRACKET_OPEN) {
+ //array constructor
- tk = _get_token();
- if (tk.type != TK_PARENTHESIS_OPEN) {
- _set_error("Expected '(' after type name");
- return nullptr;
- }
+ _set_tkpos(prepos);
+ expr = _parse_array_constructor(p_block, p_function_info);
+ } else {
+ if (tk.type != TK_PARENTHESIS_OPEN) {
+ _set_error("Expected '(' after type name");
+ return nullptr;
+ }
+ //basic type constructor
- int carg = -1;
+ OperatorNode *func = alloc_node<OperatorNode>();
+ func->op = OP_CONSTRUCT;
- bool ok = _parse_function_arguments(p_block, p_function_info, func, &carg);
+ if (precision_defined) {
+ func->return_precision_cache = precision;
+ }
- if (carg >= 0) {
- completion_type = COMPLETION_CALL_ARGUMENTS;
- completion_line = tk_line;
- completion_block = p_block;
- completion_function = funcname->name;
- completion_argument = carg;
- }
+ VariableNode *funcname = alloc_node<VariableNode>();
+ funcname->name = get_datatype_name(datatype);
+ func->arguments.push_back(funcname);
- if (!ok) {
- return nullptr;
- }
+ int carg = -1;
- if (!_validate_function_call(p_block, p_function_info, func, &func->return_cache, &func->struct_name)) {
- _set_error("No matching constructor found for: '" + String(funcname->name) + "'");
- return nullptr;
- }
+ bool ok = _parse_function_arguments(p_block, p_function_info, func, &carg);
+
+ if (carg >= 0) {
+ completion_type = COMPLETION_CALL_ARGUMENTS;
+ completion_line = tk_line;
+ completion_block = p_block;
+ completion_function = funcname->name;
+ completion_argument = carg;
+ }
- expr = _reduce_expression(p_block, func);
+ if (!ok) {
+ return nullptr;
+ }
+
+ if (!_validate_function_call(p_block, p_function_info, func, &func->return_cache, &func->struct_name)) {
+ _set_error("No matching constructor found for: '" + String(funcname->name) + "'");
+ return nullptr;
+ }
+ expr = _reduce_expression(p_block, func);
+ }
+ }
} else if (tk.type == TK_IDENTIFIER) {
_set_tkpos(prepos);
@@ -3678,11 +3974,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
if (!nexpr) {
return nullptr;
}
- Node *node = pstruct->members[i];
if (!_compare_datatypes_in_nodes(pstruct->members[i], nexpr)) {
- String type_name = nexpr->get_datatype() == TYPE_STRUCT ? nexpr->get_datatype_name() : get_datatype_name(nexpr->get_datatype());
- String type_name2 = node->get_datatype() == TYPE_STRUCT ? node->get_datatype_name() : get_datatype_name(node->get_datatype());
- _set_error("Invalid assignment of '" + type_name + "' to '" + type_name2 + "'");
return nullptr;
}
}
@@ -3704,7 +3996,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
expr = func;
- } else { //a function
+ } else { //a function call
const StringName &name = identifier;
@@ -3716,7 +4008,9 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
int carg = -1;
+ pass_array = true;
bool ok = _parse_function_arguments(p_block, p_function_info, func, &carg);
+ pass_array = false;
// Check if block has a variable with the same name as function to prevent shader crash.
ShaderLanguage::BlockNode *bnode = p_block;
@@ -3769,6 +4063,10 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
FunctionNode *call_function = shader->functions[function_index].function;
if (call_function) {
+ func->return_cache = call_function->get_datatype();
+ func->struct_name = call_function->get_datatype_name();
+ func->return_array_size = call_function->get_array_size();
+
//get current base function
FunctionNode *base_function = nullptr;
{
@@ -3943,60 +4241,61 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
Node *assign_expression = nullptr;
if (array_size > 0) {
- tk = _get_token();
-
- if (tk.type != TK_BRACKET_OPEN && tk.type != TK_PERIOD && tk.type != TK_OP_ASSIGN) {
- _set_error("Expected '[','.' or '='");
- return nullptr;
- }
+ if (!pass_array) {
+ tk = _get_token();
- if (tk.type == TK_OP_ASSIGN) {
- if (is_const) {
- _set_error("Constants cannot be modified.");
+ if (tk.type != TK_BRACKET_OPEN && tk.type != TK_PERIOD && tk.type != TK_OP_ASSIGN) {
+ _set_error("Expected '[','.' or '='");
return nullptr;
}
- assign_expression = _parse_array_constructor(p_block, p_function_info, data_type, struct_name, array_size);
- if (!assign_expression) {
- return nullptr;
- }
- } else if (tk.type == TK_PERIOD) {
- completion_class = TAG_ARRAY;
- p_block->block_tag = SubClassTag::TAG_ARRAY;
- call_expression = _parse_and_reduce_expression(p_block, p_function_info);
- p_block->block_tag = SubClassTag::TAG_GLOBAL;
- if (!call_expression) {
- return nullptr;
- }
- data_type = call_expression->get_datatype();
- } else { // indexing
- index_expression = _parse_and_reduce_expression(p_block, p_function_info);
- if (!index_expression) {
- return nullptr;
- }
+ if (tk.type == TK_OP_ASSIGN) {
+ if (is_const) {
+ _set_error("Constants cannot be modified.");
+ return nullptr;
+ }
+ assign_expression = _parse_array_constructor(p_block, p_function_info, data_type, struct_name, array_size);
+ if (!assign_expression) {
+ return nullptr;
+ }
+ } else if (tk.type == TK_PERIOD) {
+ completion_class = TAG_ARRAY;
+ p_block->block_tag = SubClassTag::TAG_ARRAY;
+ call_expression = _parse_and_reduce_expression(p_block, p_function_info);
+ p_block->block_tag = SubClassTag::TAG_GLOBAL;
+ if (!call_expression) {
+ return nullptr;
+ }
+ data_type = call_expression->get_datatype();
+ } else { // indexing
+ index_expression = _parse_and_reduce_expression(p_block, p_function_info);
+ if (!index_expression) {
+ return nullptr;
+ }
- if (index_expression->get_datatype() != TYPE_INT && index_expression->get_datatype() != TYPE_UINT) {
- _set_error("Only integer expressions are allowed for indexing");
- return nullptr;
- }
+ if (index_expression->get_datatype() != TYPE_INT && index_expression->get_datatype() != TYPE_UINT) {
+ _set_error("Only integer expressions are allowed for indexing");
+ return nullptr;
+ }
- if (index_expression->type == Node::TYPE_CONSTANT) {
- ConstantNode *cnode = (ConstantNode *)index_expression;
- if (cnode) {
- if (!cnode->values.is_empty()) {
- int value = cnode->values[0].sint;
- if (value < 0 || value >= array_size) {
- _set_error(vformat("Index [%s] out of range [%s..%s]", value, 0, array_size - 1));
- return nullptr;
+ if (index_expression->type == Node::TYPE_CONSTANT) {
+ ConstantNode *cnode = (ConstantNode *)index_expression;
+ if (cnode) {
+ if (!cnode->values.is_empty()) {
+ int value = cnode->values[0].sint;
+ if (value < 0 || value >= array_size) {
+ _set_error(vformat("Index [%s] out of range [%s..%s]", value, 0, array_size - 1));
+ return nullptr;
+ }
}
}
}
- }
- tk = _get_token();
- if (tk.type != TK_BRACKET_CLOSE) {
- _set_error("Expected ']'");
- return nullptr;
+ tk = _get_token();
+ if (tk.type != TK_BRACKET_CLOSE) {
+ _set_error("Expected ']'");
+ return nullptr;
+ }
}
}
@@ -4008,6 +4307,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
arrname->call_expression = call_expression;
arrname->assign_expression = assign_expression;
arrname->is_const = is_const;
+ arrname->array_size = array_size;
expr = arrname;
} else {
VariableNode *varname = alloc_node<VariableNode>();
@@ -4072,6 +4372,18 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
DataType dt = expr->get_datatype();
String st = expr->get_datatype_name();
+ if (!expr->is_indexed() && expr->get_array_size() > 0) {
+ completion_class = TAG_ARRAY;
+ p_block->block_tag = SubClassTag::TAG_ARRAY;
+ Node *call_expression = _parse_and_reduce_expression(p_block, p_function_info);
+ p_block->block_tag = SubClassTag::TAG_GLOBAL;
+ if (!call_expression) {
+ return nullptr;
+ }
+ expr = call_expression;
+ break;
+ }
+
StringName identifier;
if (_get_completable_identifier(p_block, dt == TYPE_STRUCT ? COMPLETION_STRUCT : COMPLETION_INDEX, identifier)) {
if (dt == TYPE_STRUCT) {
@@ -4348,6 +4660,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
mn->has_swizzling_duplicates = repeated;
if (array_size > 0) {
+ TkPos prev_pos = _get_tkpos();
tk = _get_token();
if (tk.type == TK_OP_ASSIGN) {
if (last_type == IDENTIFIER_CONSTANT) {
@@ -4399,13 +4712,14 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
return nullptr;
}
mn->index_expression = index_expression;
-
} else {
- _set_error("Expected '[','.' or '='");
- return nullptr;
+ if (!pass_array) {
+ _set_error("Expected '[','.' or '='");
+ return nullptr;
+ }
+ _set_tkpos(prev_pos);
}
}
-
expr = mn;
//todo
@@ -4430,117 +4744,130 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
}
DataType member_type = TYPE_VOID;
+ String member_struct_name;
- switch (expr->get_datatype()) {
- case TYPE_BVEC2:
- case TYPE_VEC2:
- case TYPE_IVEC2:
- case TYPE_UVEC2:
- case TYPE_MAT2:
- if (index->type == Node::TYPE_CONSTANT) {
- uint32_t index_constant = static_cast<ConstantNode *>(index)->values[0].uint;
- if (index_constant >= 2) {
- _set_error("Index out of range (0-1)");
- return nullptr;
+ if (expr->get_array_size() > 0) {
+ uint32_t index_constant = static_cast<ConstantNode *>(index)->values[0].uint;
+ if (index_constant >= (uint32_t)expr->get_array_size()) {
+ _set_error(vformat("Index [%s] out of range [%s..%s]", index_constant, 0, expr->get_array_size() - 1));
+ return nullptr;
+ }
+ member_type = expr->get_datatype();
+ if (member_type == TYPE_STRUCT) {
+ member_struct_name = expr->get_datatype_name();
+ }
+ } else {
+ switch (expr->get_datatype()) {
+ case TYPE_BVEC2:
+ case TYPE_VEC2:
+ case TYPE_IVEC2:
+ case TYPE_UVEC2:
+ case TYPE_MAT2:
+ if (index->type == Node::TYPE_CONSTANT) {
+ uint32_t index_constant = static_cast<ConstantNode *>(index)->values[0].uint;
+ if (index_constant >= 2) {
+ _set_error("Index out of range (0-1)");
+ return nullptr;
+ }
}
- }
- switch (expr->get_datatype()) {
- case TYPE_BVEC2:
- member_type = TYPE_BOOL;
- break;
- case TYPE_VEC2:
- member_type = TYPE_FLOAT;
- break;
- case TYPE_IVEC2:
- member_type = TYPE_INT;
- break;
- case TYPE_UVEC2:
- member_type = TYPE_UINT;
- break;
- case TYPE_MAT2:
- member_type = TYPE_VEC2;
- break;
- default:
- break;
- }
+ switch (expr->get_datatype()) {
+ case TYPE_BVEC2:
+ member_type = TYPE_BOOL;
+ break;
+ case TYPE_VEC2:
+ member_type = TYPE_FLOAT;
+ break;
+ case TYPE_IVEC2:
+ member_type = TYPE_INT;
+ break;
+ case TYPE_UVEC2:
+ member_type = TYPE_UINT;
+ break;
+ case TYPE_MAT2:
+ member_type = TYPE_VEC2;
+ break;
+ default:
+ break;
+ }
- break;
- case TYPE_BVEC3:
- case TYPE_VEC3:
- case TYPE_IVEC3:
- case TYPE_UVEC3:
- case TYPE_MAT3:
- if (index->type == Node::TYPE_CONSTANT) {
- uint32_t index_constant = static_cast<ConstantNode *>(index)->values[0].uint;
- if (index_constant >= 3) {
- _set_error("Index out of range (0-2)");
- return nullptr;
+ break;
+ case TYPE_BVEC3:
+ case TYPE_VEC3:
+ case TYPE_IVEC3:
+ case TYPE_UVEC3:
+ case TYPE_MAT3:
+ if (index->type == Node::TYPE_CONSTANT) {
+ uint32_t index_constant = static_cast<ConstantNode *>(index)->values[0].uint;
+ if (index_constant >= 3) {
+ _set_error("Index out of range (0-2)");
+ return nullptr;
+ }
}
- }
- switch (expr->get_datatype()) {
- case TYPE_BVEC3:
- member_type = TYPE_BOOL;
- break;
- case TYPE_VEC3:
- member_type = TYPE_FLOAT;
- break;
- case TYPE_IVEC3:
- member_type = TYPE_INT;
- break;
- case TYPE_UVEC3:
- member_type = TYPE_UINT;
- break;
- case TYPE_MAT3:
- member_type = TYPE_VEC3;
- break;
- default:
- break;
- }
- break;
- case TYPE_BVEC4:
- case TYPE_VEC4:
- case TYPE_IVEC4:
- case TYPE_UVEC4:
- case TYPE_MAT4:
- if (index->type == Node::TYPE_CONSTANT) {
- uint32_t index_constant = static_cast<ConstantNode *>(index)->values[0].uint;
- if (index_constant >= 4) {
- _set_error("Index out of range (0-3)");
- return nullptr;
+ switch (expr->get_datatype()) {
+ case TYPE_BVEC3:
+ member_type = TYPE_BOOL;
+ break;
+ case TYPE_VEC3:
+ member_type = TYPE_FLOAT;
+ break;
+ case TYPE_IVEC3:
+ member_type = TYPE_INT;
+ break;
+ case TYPE_UVEC3:
+ member_type = TYPE_UINT;
+ break;
+ case TYPE_MAT3:
+ member_type = TYPE_VEC3;
+ break;
+ default:
+ break;
+ }
+ break;
+ case TYPE_BVEC4:
+ case TYPE_VEC4:
+ case TYPE_IVEC4:
+ case TYPE_UVEC4:
+ case TYPE_MAT4:
+ if (index->type == Node::TYPE_CONSTANT) {
+ uint32_t index_constant = static_cast<ConstantNode *>(index)->values[0].uint;
+ if (index_constant >= 4) {
+ _set_error("Index out of range (0-3)");
+ return nullptr;
+ }
}
- }
- switch (expr->get_datatype()) {
- case TYPE_BVEC4:
- member_type = TYPE_BOOL;
- break;
- case TYPE_VEC4:
- member_type = TYPE_FLOAT;
- break;
- case TYPE_IVEC4:
- member_type = TYPE_INT;
- break;
- case TYPE_UVEC4:
- member_type = TYPE_UINT;
- break;
- case TYPE_MAT4:
- member_type = TYPE_VEC4;
- break;
- default:
- break;
+ switch (expr->get_datatype()) {
+ case TYPE_BVEC4:
+ member_type = TYPE_BOOL;
+ break;
+ case TYPE_VEC4:
+ member_type = TYPE_FLOAT;
+ break;
+ case TYPE_IVEC4:
+ member_type = TYPE_INT;
+ break;
+ case TYPE_UVEC4:
+ member_type = TYPE_UINT;
+ break;
+ case TYPE_MAT4:
+ member_type = TYPE_VEC4;
+ break;
+ default:
+ break;
+ }
+ break;
+ default: {
+ _set_error("Object of type '" + (expr->get_datatype() == TYPE_STRUCT ? expr->get_datatype_name() : get_datatype_name(expr->get_datatype())) + "' can't be indexed");
+ return nullptr;
}
- break;
- default: {
- _set_error("Object of type '" + (expr->get_datatype() == TYPE_STRUCT ? expr->get_datatype_name() : get_datatype_name(expr->get_datatype())) + "' can't be indexed");
- return nullptr;
}
}
-
OperatorNode *op = alloc_node<OperatorNode>();
op->op = OP_INDEX;
op->return_cache = member_type;
+ op->struct_name = member_struct_name;
op->arguments.push_back(expr);
op->arguments.push_back(index);
expr = op;
@@ -4556,7 +4883,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
op->op = tk.type == TK_OP_DECREMENT ? OP_POST_DECREMENT : OP_POST_INCREMENT;
op->arguments.push_back(expr);
- if (!_validate_operator(op, &op->return_cache)) {
+ if (!_validate_operator(op, &op->return_cache, &op->return_array_size)) {
_set_error("Invalid base type for increment/decrement operator");
return nullptr;
}
@@ -4874,13 +5201,18 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
expression.write[i].is_op = false;
expression.write[i].node = op;
- if (!_validate_operator(op, &op->return_cache)) {
+ if (!_validate_operator(op, &op->return_cache, &op->return_array_size)) {
String at;
for (int j = 0; j < op->arguments.size(); j++) {
if (j > 0) {
at += " and ";
}
at += get_datatype_name(op->arguments[j]->get_datatype());
+ if (!op->arguments[j]->is_indexed() && op->arguments[j]->get_array_size() > 0) {
+ at += "[";
+ at += itos(op->arguments[j]->get_array_size());
+ at += "]";
+ }
}
_set_error("Invalid arguments to unary operator '" + get_operator_text(op->op) + "' :" + at);
return nullptr;
@@ -4907,13 +5239,18 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
expression.write[next_op - 1].is_op = false;
expression.write[next_op - 1].node = op;
- if (!_validate_operator(op, &op->return_cache)) {
+ if (!_validate_operator(op, &op->return_cache, &op->return_array_size)) {
String at;
for (int i = 0; i < op->arguments.size(); i++) {
if (i > 0) {
at += " and ";
}
at += get_datatype_name(op->arguments[i]->get_datatype());
+ if (!op->arguments[i]->is_indexed() && op->arguments[i]->get_array_size() > 0) {
+ at += "[";
+ at += itos(op->arguments[i]->get_array_size());
+ at += "]";
+ }
}
_set_error("Invalid argument to ternary ?: operator: " + at);
return nullptr;
@@ -4960,7 +5297,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
//replace all 3 nodes by this operator and make it an expression
- if (!_validate_operator(op, &op->return_cache)) {
+ if (!_validate_operator(op, &op->return_cache, &op->return_array_size)) {
String at;
for (int i = 0; i < op->arguments.size(); i++) {
if (i > 0) {
@@ -4971,6 +5308,11 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
} else {
at += get_datatype_name(op->arguments[i]->get_datatype());
}
+ if (!op->arguments[i]->is_indexed() && op->arguments[i]->get_array_size() > 0) {
+ at += "[";
+ at += itos(op->arguments[i]->get_array_size());
+ at += "]";
+ }
}
_set_error("Invalid arguments to operator '" + get_operator_text(op->op) + "' :" + at);
return nullptr;
@@ -5227,6 +5569,7 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const FunctionInfo &p_fun
ArrayDeclarationNode::Declaration decl;
decl.name = name;
decl.size = 0U;
+ decl.single_expression = false;
pos = _get_tkpos();
tk = _get_token();
@@ -5293,179 +5636,205 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const FunctionInfo &p_fun
return ERR_PARSE_ERROR;
}
+ TkPos prev_pos = _get_tkpos();
tk = _get_token();
- if (tk.type != TK_CURLY_BRACKET_OPEN) {
- if (unknown_size) {
- _set_error("Expected '{'");
- return ERR_PARSE_ERROR;
- }
-
- full_def = true;
+ if (tk.type == TK_IDENTIFIER) { // a function call array initialization
+ _set_tkpos(prev_pos);
+ pass_array = true;
+ Node *n = _parse_and_reduce_expression(p_block, p_function_info);
+ pass_array = false;
- DataPrecision precision2 = PRECISION_DEFAULT;
- if (is_token_precision(tk.type)) {
- precision2 = get_token_precision(tk.type);
- tk = _get_token();
- if (shader->structs.has(tk.text)) {
- _set_error("Precision modifier cannot be used on structs.");
- return ERR_PARSE_ERROR;
+ if (!n) {
+ _set_error("Expected correct array initializer!");
+ return ERR_PARSE_ERROR;
+ } else {
+ if (unknown_size) {
+ decl.size = n->get_array_size();
+ var.array_size = n->get_array_size();
}
- if (!is_token_nonvoid_datatype(tk.type)) {
- _set_error("Expected datatype after precision");
+
+ if (!_compare_datatypes(var.type, var.struct_name, var.array_size, n->get_datatype(), n->get_datatype_name(), n->get_array_size())) {
return ERR_PARSE_ERROR;
}
- }
- DataType type2;
- StringName struct_name2 = "";
+ decl.single_expression = true;
+ decl.initializer.push_back(n);
+ }
- if (shader->structs.has(tk.text)) {
- type2 = TYPE_STRUCT;
- struct_name2 = tk.text;
- } else {
- if (!is_token_variable_datatype(tk.type)) {
- _set_error("Invalid data type for array");
+ tk = _get_token();
+ } else {
+ if (tk.type != TK_CURLY_BRACKET_OPEN) {
+ if (unknown_size) {
+ _set_error("Expected '{'");
return ERR_PARSE_ERROR;
}
- type2 = get_token_datatype(tk.type);
- }
- int array_size2 = 0;
+ full_def = true;
- tk = _get_token();
- if (tk.type == TK_BRACKET_OPEN) {
- TkPos pos2 = _get_tkpos();
- tk = _get_token();
- if (tk.type == TK_BRACKET_CLOSE) {
- array_size2 = var.array_size;
+ DataPrecision precision2 = PRECISION_DEFAULT;
+ if (is_token_precision(tk.type)) {
+ precision2 = get_token_precision(tk.type);
tk = _get_token();
- } else {
- _set_tkpos(pos2);
-
- Node *n = _parse_and_reduce_expression(p_block, p_function_info);
- if (!n || n->type != Node::TYPE_CONSTANT || n->get_datatype() != TYPE_INT) {
- _set_error("Expected single integer constant > 0");
+ if (shader->structs.has(tk.text)) {
+ _set_error("Precision modifier cannot be used on structs.");
+ return ERR_PARSE_ERROR;
+ }
+ if (!is_token_nonvoid_datatype(tk.type)) {
+ _set_error("Expected datatype after precision");
return ERR_PARSE_ERROR;
}
+ }
- ConstantNode *cnode = (ConstantNode *)n;
- if (cnode->values.size() == 1) {
- array_size2 = cnode->values[0].sint;
- if (array_size2 <= 0) {
- _set_error("Expected single integer constant > 0");
- return ERR_PARSE_ERROR;
- }
- } else {
- _set_error("Expected single integer constant > 0");
+ DataType type2;
+ StringName struct_name2 = "";
+
+ if (shader->structs.has(tk.text)) {
+ type2 = TYPE_STRUCT;
+ struct_name2 = tk.text;
+ } else {
+ if (!is_token_variable_datatype(tk.type)) {
+ _set_error("Invalid data type for array");
return ERR_PARSE_ERROR;
}
+ type2 = get_token_datatype(tk.type);
+ }
+ int array_size2 = 0;
+
+ tk = _get_token();
+ if (tk.type == TK_BRACKET_OPEN) {
+ TkPos pos2 = _get_tkpos();
tk = _get_token();
- if (tk.type != TK_BRACKET_CLOSE) {
- _set_error("Expected ']'");
- return ERR_PARSE_ERROR;
+ if (tk.type == TK_BRACKET_CLOSE) {
+ array_size2 = var.array_size;
+ tk = _get_token();
} else {
+ _set_tkpos(pos2);
+
+ Node *n = _parse_and_reduce_expression(p_block, p_function_info);
+ if (!n || n->type != Node::TYPE_CONSTANT || n->get_datatype() != TYPE_INT) {
+ _set_error("Expected single integer constant > 0");
+ return ERR_PARSE_ERROR;
+ }
+
+ ConstantNode *cnode = (ConstantNode *)n;
+ if (cnode->values.size() == 1) {
+ array_size2 = cnode->values[0].sint;
+ if (array_size2 <= 0) {
+ _set_error("Expected single integer constant > 0");
+ return ERR_PARSE_ERROR;
+ }
+ } else {
+ _set_error("Expected single integer constant > 0");
+ return ERR_PARSE_ERROR;
+ }
+
tk = _get_token();
+ if (tk.type != TK_BRACKET_CLOSE) {
+ _set_error("Expected ']'");
+ return ERR_PARSE_ERROR;
+ } else {
+ tk = _get_token();
+ }
}
- }
- } else {
- _set_error("Expected '['");
- return ERR_PARSE_ERROR;
- }
-
- if (precision != precision2 || type != type2 || struct_name != struct_name2 || var.array_size != array_size2) {
- String error_str = "Cannot convert from '";
- if (precision2 != PRECISION_DEFAULT) {
- error_str += get_precision_name(precision2);
- error_str += " ";
- }
- if (type2 == TYPE_STRUCT) {
- error_str += struct_name2;
} else {
- error_str += get_datatype_name(type2);
- }
- error_str += "[";
- error_str += itos(array_size2);
- error_str += "]'";
- error_str += " to '";
- if (precision != PRECISION_DEFAULT) {
- error_str += get_precision_name(precision);
- error_str += " ";
+ _set_error("Expected '['");
+ return ERR_PARSE_ERROR;
}
- if (type == TYPE_STRUCT) {
- error_str += struct_name;
- } else {
- error_str += get_datatype_name(type);
+
+ if (precision != precision2 || type != type2 || struct_name != struct_name2 || var.array_size != array_size2) {
+ String error_str = "Cannot convert from '";
+ if (precision2 != PRECISION_DEFAULT) {
+ error_str += get_precision_name(precision2);
+ error_str += " ";
+ }
+ if (type2 == TYPE_STRUCT) {
+ error_str += struct_name2;
+ } else {
+ error_str += get_datatype_name(type2);
+ }
+ error_str += "[";
+ error_str += itos(array_size2);
+ error_str += "]'";
+ error_str += " to '";
+ if (precision != PRECISION_DEFAULT) {
+ error_str += get_precision_name(precision);
+ error_str += " ";
+ }
+ if (type == TYPE_STRUCT) {
+ error_str += struct_name;
+ } else {
+ error_str += get_datatype_name(type);
+ }
+ error_str += "[";
+ error_str += itos(var.array_size);
+ error_str += "]'";
+ _set_error(error_str);
+ return ERR_PARSE_ERROR;
}
- error_str += "[";
- error_str += itos(var.array_size);
- error_str += "]'";
- _set_error(error_str);
- return ERR_PARSE_ERROR;
}
- }
- bool curly = tk.type == TK_CURLY_BRACKET_OPEN;
+ bool curly = tk.type == TK_CURLY_BRACKET_OPEN;
- if (unknown_size) {
- if (!curly) {
- _set_error("Expected '{'");
- return ERR_PARSE_ERROR;
- }
- } else {
- if (full_def) {
- if (curly) {
- _set_error("Expected '('");
+ if (unknown_size) {
+ if (!curly) {
+ _set_error("Expected '{'");
return ERR_PARSE_ERROR;
}
+ } else {
+ if (full_def) {
+ if (curly) {
+ _set_error("Expected '('");
+ return ERR_PARSE_ERROR;
+ }
+ }
}
- }
- if (tk.type == TK_PARENTHESIS_OPEN || curly) { // initialization
- while (true) {
- Node *n = _parse_and_reduce_expression(p_block, p_function_info);
- if (!n) {
- return ERR_PARSE_ERROR;
- }
+ if (tk.type == TK_PARENTHESIS_OPEN || curly) { // initialization
+ while (true) {
+ Node *n = _parse_and_reduce_expression(p_block, p_function_info);
+ if (!n) {
+ return ERR_PARSE_ERROR;
+ }
- if (node->is_const && n->type == Node::TYPE_OPERATOR && ((OperatorNode *)n)->op == OP_CALL) {
- _set_error("Expected constant expression");
- return ERR_PARSE_ERROR;
- }
+ if (node->is_const && n->type == Node::TYPE_OPERATOR && ((OperatorNode *)n)->op == OP_CALL) {
+ _set_error("Expected constant expression");
+ return ERR_PARSE_ERROR;
+ }
- if (var.type != n->get_datatype() || struct_name != n->get_datatype_name()) {
- _set_error("Invalid assignment of '" + (n->get_datatype() == TYPE_STRUCT ? n->get_datatype_name() : get_datatype_name(n->get_datatype())) + "' to '" + (var.type == TYPE_STRUCT ? struct_name : get_datatype_name(var.type)) + "'");
- return ERR_PARSE_ERROR;
- }
+ if (!_compare_datatypes(var.type, struct_name, 0, n->get_datatype(), n->get_datatype_name(), 0)) {
+ return ERR_PARSE_ERROR;
+ }
- tk = _get_token();
- if (tk.type == TK_COMMA) {
- decl.initializer.push_back(n);
- continue;
- } else if (!curly && tk.type == TK_PARENTHESIS_CLOSE) {
- decl.initializer.push_back(n);
- break;
- } else if (curly && tk.type == TK_CURLY_BRACKET_CLOSE) {
- decl.initializer.push_back(n);
- break;
- } else {
- if (curly) {
- _set_error("Expected '}' or ','");
+ tk = _get_token();
+ if (tk.type == TK_COMMA) {
+ decl.initializer.push_back(n);
+ continue;
+ } else if (!curly && tk.type == TK_PARENTHESIS_CLOSE) {
+ decl.initializer.push_back(n);
+ break;
+ } else if (curly && tk.type == TK_CURLY_BRACKET_CLOSE) {
+ decl.initializer.push_back(n);
+ break;
} else {
- _set_error("Expected ')' or ','");
+ if (curly) {
+ _set_error("Expected '}' or ','");
+ } else {
+ _set_error("Expected ')' or ','");
+ }
+ return ERR_PARSE_ERROR;
}
+ }
+ if (unknown_size) {
+ decl.size = decl.initializer.size();
+ var.array_size = decl.initializer.size();
+ } else if (decl.initializer.size() != var.array_size) {
+ _set_error("Array size mismatch");
return ERR_PARSE_ERROR;
}
+ tk = _get_token();
}
- if (unknown_size) {
- decl.size = decl.initializer.size();
- var.array_size = decl.initializer.size();
- } else if (decl.initializer.size() != var.array_size) {
- _set_error("Array size mismatch");
- return ERR_PARSE_ERROR;
- }
- tk = _get_token();
}
} else {
if (unknown_size) {
@@ -5518,8 +5887,7 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const FunctionInfo &p_fun
}
}
- if (var.type == TYPE_STRUCT ? (var.struct_name != n->get_datatype_name()) : (var.type != n->get_datatype())) {
- _set_error("Invalid assignment of '" + (n->get_datatype() == TYPE_STRUCT ? n->get_datatype_name() : get_datatype_name(n->get_datatype())) + "' to '" + (var.type == TYPE_STRUCT ? String(var.struct_name) : get_datatype_name(var.type)) + "'");
+ if (!_compare_datatypes(var.type, var.struct_name, var.array_size, n->get_datatype(), n->get_datatype_name(), n->get_array_size())) {
return ERR_PARSE_ERROR;
}
tk = _get_token();
@@ -5980,6 +6348,11 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const FunctionInfo &p_fun
}
String return_struct_name = String(b->parent_function->return_struct_name);
+ String array_size_string;
+
+ if (b->parent_function->return_array_size > 0) {
+ array_size_string = "[" + itos(b->parent_function->return_array_size) + "]";
+ }
ControlFlowNode *flow = alloc_node<ControlFlowNode>();
flow->flow_op = FLOW_OP_RETURN;
@@ -5989,18 +6362,27 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const FunctionInfo &p_fun
if (tk.type == TK_SEMICOLON) {
//all is good
if (b->parent_function->return_type != TYPE_VOID) {
- _set_error("Expected return with an expression of type '" + (return_struct_name != "" ? return_struct_name : get_datatype_name(b->parent_function->return_type)) + "'");
+ _set_error("Expected return with an expression of type '" + (return_struct_name != "" ? return_struct_name : get_datatype_name(b->parent_function->return_type)) + array_size_string + "'");
return ERR_PARSE_ERROR;
}
} else {
_set_tkpos(pos); //rollback, wants expression
+
+ pass_array = true;
Node *expr = _parse_and_reduce_expression(p_block, p_function_info);
if (!expr) {
return ERR_PARSE_ERROR;
}
+ pass_array = false;
+
+ bool array_size_incorrect = false;
+
+ if (b->parent_function->return_array_size > 0 && b->parent_function->return_array_size != expr->get_array_size()) {
+ array_size_incorrect = true;
+ }
- if (b->parent_function->return_type != expr->get_datatype() || return_struct_name != expr->get_datatype_name()) {
- _set_error("Expected return with an expression of type '" + (return_struct_name != "" ? return_struct_name : get_datatype_name(b->parent_function->return_type)) + "'");
+ if (b->parent_function->return_type != expr->get_datatype() || array_size_incorrect || return_struct_name != expr->get_datatype_name()) {
+ _set_error("Expected return with an expression of type '" + (return_struct_name != "" ? return_struct_name : get_datatype_name(b->parent_function->return_type)) + array_size_string + "'");
return ERR_PARSE_ERROR;
}
@@ -6795,6 +7177,7 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct
DataPrecision precision = PRECISION_DEFAULT;
DataType type;
StringName name;
+ int return_array_size = 0;
if (tk.type == TK_CONST) {
is_constant = true;
@@ -6832,10 +7215,33 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct
}
TkPos prev_pos = _get_tkpos();
tk = _get_token();
+
if (tk.type == TK_BRACKET_OPEN) {
- _set_error("Cannot use arrays as return types");
- return ERR_PARSE_ERROR;
+ bool error = false;
+ tk = _get_token();
+
+ if (tk.type == TK_INT_CONSTANT) {
+ return_array_size = (int)tk.constant;
+ if (return_array_size > 0) {
+ tk = _get_token();
+ if (tk.type != TK_BRACKET_CLOSE) {
+ _set_error("Expected ']'");
+ return ERR_PARSE_ERROR;
+ }
+ } else {
+ error = true;
+ }
+ } else {
+ error = true;
+ }
+ if (error) {
+ _set_error("Expected integer constant > 0");
+ return ERR_PARSE_ERROR;
+ }
+
+ prev_pos = _get_tkpos();
}
+
_set_tkpos(prev_pos);
_get_completable_identifier(nullptr, COMPLETION_MAIN_FUNCTION, name);
@@ -7049,8 +7455,7 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct
return ERR_PARSE_ERROR;
}
- if (constant.type != n->get_datatype() || n->get_datatype_name() != struct_name) {
- _set_error("Invalid assignment of '" + (n->get_datatype() == TYPE_STRUCT ? n->get_datatype_name() : get_datatype_name(n->get_datatype())) + "' to '" + (is_struct ? String(struct_name) : get_datatype_name(constant.type)) + "'");
+ if (!_compare_datatypes(constant.type, struct_name, 0, n->get_datatype(), n->get_datatype_name(), 0)) {
return ERR_PARSE_ERROR;
}
@@ -7111,8 +7516,7 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct
constant.initializer = static_cast<ConstantNode *>(expr);
- if (type != expr->get_datatype() || expr->get_datatype_name() != struct_name) {
- _set_error("Invalid assignment of '" + (expr->get_datatype() == TYPE_STRUCT ? expr->get_datatype_name() : get_datatype_name(expr->get_datatype())) + "' to '" + (is_struct ? String(struct_name) : get_datatype_name(type)) + "'");
+ if (!_compare_datatypes(type, struct_name, 0, expr->get_datatype(), expr->get_datatype_name(), 0)) {
return ERR_PARSE_ERROR;
}
}
@@ -7192,6 +7596,7 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct
func_node->return_type = type;
func_node->return_struct_name = struct_name;
func_node->return_precision = precision;
+ func_node->return_array_size = return_array_size;
if (p_functions.has(name)) {
func_node->can_discard = p_functions[name].can_discard;
@@ -7245,6 +7650,7 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct
StringName param_struct_name;
DataPrecision pprecision = PRECISION_DEFAULT;
bool use_precision = false;
+ int array_size = 0;
if (is_token_precision(tk.type)) {
pprecision = get_token_precision(tk.type);
@@ -7291,8 +7697,29 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct
tk = _get_token();
if (tk.type == TK_BRACKET_OPEN) {
- _set_error("Arrays as parameters are not implemented yet");
- return ERR_PARSE_ERROR;
+ bool error = false;
+ tk = _get_token();
+
+ if (tk.type == TK_INT_CONSTANT) {
+ array_size = (int)tk.constant;
+
+ if (array_size > 0) {
+ tk = _get_token();
+ if (tk.type != TK_BRACKET_CLOSE) {
+ _set_error("Expected ']'");
+ return ERR_PARSE_ERROR;
+ }
+ } else {
+ error = true;
+ }
+ } else {
+ error = true;
+ }
+ if (error) {
+ _set_error("Expected integer constant > 0");
+ return ERR_PARSE_ERROR;
+ }
+ tk = _get_token();
}
if (tk.type != TK_IDENTIFIER) {
_set_error("Expected identifier for argument name");
@@ -7326,14 +7753,41 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct
arg.tex_argument_repeat = REPEAT_DEFAULT;
arg.is_const = is_const;
- func_node->arguments.push_back(arg);
-
tk = _get_token();
if (tk.type == TK_BRACKET_OPEN) {
- _set_error("Arrays as parameters are not implemented yet");
- return ERR_PARSE_ERROR;
+ if (array_size > 0) {
+ _set_error("Array size is already defined!");
+ return ERR_PARSE_ERROR;
+ }
+ bool error = false;
+ tk = _get_token();
+
+ if (tk.type == TK_INT_CONSTANT) {
+ array_size = (int)tk.constant;
+
+ if (array_size > 0) {
+ tk = _get_token();
+ if (tk.type != TK_BRACKET_CLOSE) {
+ _set_error("Expected ']'");
+ return ERR_PARSE_ERROR;
+ }
+ } else {
+ error = true;
+ }
+ } else {
+ error = true;
+ }
+
+ if (error) {
+ _set_error("Expected integer constant > 0");
+ return ERR_PARSE_ERROR;
+ }
+ tk = _get_token();
}
+ arg.array_size = array_size;
+ func_node->arguments.push_back(arg);
+
if (tk.type == TK_COMMA) {
tk = _get_token();
//do none and go on
@@ -7756,6 +8210,13 @@ Error ShaderLanguage::complete(const String &p_code, const Map<StringName, Funct
String calltip;
calltip += get_datatype_name(shader->functions[i].function->return_type);
+
+ if (shader->functions[i].function->return_array_size > 0) {
+ calltip += "[";
+ calltip += itos(shader->functions[i].function->return_array_size);
+ calltip += "]";
+ }
+
calltip += " ";
calltip += shader->functions[i].name;
calltip += "(";
@@ -7787,6 +8248,12 @@ Error ShaderLanguage::complete(const String &p_code, const Map<StringName, Funct
calltip += " ";
calltip += shader->functions[i].function->arguments[j].name;
+ if (shader->functions[i].function->arguments[j].array_size > 0) {
+ calltip += "[";
+ calltip += itos(shader->functions[i].function->arguments[j].array_size);
+ calltip += "]";
+ }
+
if (j == completion_argument) {
calltip += char32_t(0xFFFF);
}
diff --git a/servers/rendering/shader_language.h b/servers/rendering/shader_language.h
index c8c5bb1fa7..af66e32e06 100644
--- a/servers/rendering/shader_language.h
+++ b/servers/rendering/shader_language.h
@@ -369,6 +369,8 @@ public:
virtual DataType get_datatype() const { return TYPE_VOID; }
virtual String get_datatype_name() const { return ""; }
+ virtual int get_array_size() const { return 0; }
+ virtual bool is_indexed() const { return false; }
Node(Type t) :
type(t) {}
@@ -388,11 +390,15 @@ public:
struct OperatorNode : public Node {
DataType return_cache = TYPE_VOID;
DataPrecision return_precision_cache = PRECISION_DEFAULT;
+ int return_array_size = 0;
Operator op = OP_EQUAL;
StringName struct_name;
Vector<Node *> arguments;
- virtual DataType get_datatype() const { return return_cache; }
- virtual String get_datatype_name() const { return String(struct_name); }
+
+ virtual DataType get_datatype() const override { return return_cache; }
+ virtual String get_datatype_name() const override { return String(struct_name); }
+ virtual int get_array_size() const override { return return_array_size; }
+ virtual bool is_indexed() const override { return op == OP_INDEX; }
OperatorNode() :
Node(TYPE_OPERATOR) {}
@@ -402,10 +408,11 @@ public:
DataType datatype_cache = TYPE_VOID;
StringName name;
StringName struct_name;
- virtual DataType get_datatype() const { return datatype_cache; }
- virtual String get_datatype_name() const { return String(struct_name); }
bool is_const = false;
+ virtual DataType get_datatype() const override { return datatype_cache; }
+ virtual String get_datatype_name() const override { return String(struct_name); }
+
VariableNode() :
Node(TYPE_VARIABLE) {}
};
@@ -420,9 +427,9 @@ public:
StringName name;
Node *initializer;
};
-
Vector<Declaration> declarations;
- virtual DataType get_datatype() const { return datatype; }
+
+ virtual DataType get_datatype() const override { return datatype; }
VariableDeclarationNode() :
Node(TYPE_VARIABLE_DECLARATION) {}
@@ -436,9 +443,12 @@ public:
Node *call_expression = nullptr;
Node *assign_expression = nullptr;
bool is_const = false;
+ int array_size = 0;
- virtual DataType get_datatype() const { return datatype_cache; }
- virtual String get_datatype_name() const { return String(struct_name); }
+ virtual DataType get_datatype() const override { return datatype_cache; }
+ virtual String get_datatype_name() const override { return String(struct_name); }
+ virtual int get_array_size() const override { return array_size; }
+ virtual bool is_indexed() const override { return index_expression != nullptr; }
ArrayNode() :
Node(TYPE_ARRAY) {}
@@ -449,6 +459,10 @@ public:
String struct_name;
Vector<Node *> initializer;
+ virtual DataType get_datatype() const override { return datatype; }
+ virtual String get_datatype_name() const override { return struct_name; }
+ virtual int get_array_size() const override { return initializer.size(); }
+
ArrayConstructNode() :
Node(TYPE_ARRAY_CONSTRUCT) {}
};
@@ -464,10 +478,11 @@ public:
StringName name;
uint32_t size;
Vector<Node *> initializer;
+ bool single_expression;
};
-
Vector<Declaration> declarations;
- virtual DataType get_datatype() const { return datatype; }
+
+ virtual DataType get_datatype() const override { return datatype; }
ArrayDeclarationNode() :
Node(TYPE_ARRAY_DECLARATION) {}
@@ -487,8 +502,10 @@ public:
Vector<Value> values;
Vector<ArrayDeclarationNode::Declaration> array_declarations;
- virtual DataType get_datatype() const { return datatype; }
- virtual String get_datatype_name() const { return struct_name; }
+
+ virtual DataType get_datatype() const override { return datatype; }
+ virtual String get_datatype_name() const override { return struct_name; }
+ virtual int get_array_size() const override { return array_size; }
ConstantNode() :
Node(TYPE_CONSTANT) {}
@@ -553,8 +570,10 @@ public:
Node *call_expression = nullptr;
bool has_swizzling_duplicates = false;
- virtual DataType get_datatype() const { return datatype; }
- virtual String get_datatype_name() const { return String(struct_name); }
+ virtual DataType get_datatype() const override { return datatype; }
+ virtual String get_datatype_name() const override { return String(struct_name); }
+ virtual int get_array_size() const override { return array_size; }
+ virtual bool is_indexed() const override { return index_expression != nullptr || call_expression != nullptr; }
MemberNode() :
Node(TYPE_MEMBER) {}
@@ -580,6 +599,7 @@ public:
bool tex_builtin_check;
StringName tex_builtin;
bool is_const;
+ int array_size;
Map<StringName, Set<int>> tex_argument_connect;
};
@@ -588,10 +608,15 @@ public:
DataType return_type = TYPE_VOID;
StringName return_struct_name;
DataPrecision return_precision = PRECISION_DEFAULT;
+ int return_array_size = 0;
Vector<Argument> arguments;
BlockNode *body = nullptr;
bool can_discard = false;
+ virtual DataType get_datatype() const override { return return_type; }
+ virtual String get_datatype_name() const override { return String(return_struct_name); }
+ virtual int get_array_size() const override { return return_array_size; }
+
FunctionNode() :
Node(TYPE_FUNCTION) {}
};
@@ -842,6 +867,8 @@ private:
int tk_line;
StringName current_function;
+ bool last_const = false;
+ bool pass_array = false;
StringName last_name;
VaryingFunctionNames varying_function_names;
@@ -894,7 +921,7 @@ private:
#endif // DEBUG_ENABLED
bool _is_operator_assign(Operator p_op) const;
bool _validate_assign(Node *p_node, const FunctionInfo &p_function_info, String *r_message = nullptr);
- bool _validate_operator(OperatorNode *p_op, DataType *r_ret_type = nullptr);
+ bool _validate_operator(OperatorNode *p_op, DataType *r_ret_type = nullptr, int *r_ret_size = nullptr);
struct BuiltinFuncDef {
enum { MAX_ARGS = 5 };
@@ -925,7 +952,8 @@ private:
static const BuiltinFuncOutArgs builtin_func_out_args[];
Error _validate_datatype(DataType p_type);
- bool _compare_datatypes_in_nodes(Node *a, Node *b) const;
+ bool _compare_datatypes(DataType p_datatype_a, String p_datatype_name_a, int p_array_size_a, DataType p_datatype_b, String p_datatype_name_b, int p_array_size_b);
+ bool _compare_datatypes_in_nodes(Node *a, Node *b);
bool _validate_function_call(BlockNode *p_block, const FunctionInfo &p_function_info, OperatorNode *p_func, DataType *r_ret_type, StringName *r_ret_type_str);
bool _parse_function_arguments(BlockNode *p_block, const FunctionInfo &p_function_info, OperatorNode *p_func, int *r_complete_arg = nullptr);
@@ -936,6 +964,7 @@ private:
bool _check_node_constness(const Node *p_node) const;
Node *_parse_expression(BlockNode *p_block, const FunctionInfo &p_function_info);
+ Node *_parse_array_constructor(BlockNode *p_block, const FunctionInfo &p_function_info);
Node *_parse_array_constructor(BlockNode *p_block, const FunctionInfo &p_function_info, DataType p_type, const StringName &p_struct_name, int p_array_size);
ShaderLanguage::Node *_reduce_expression(BlockNode *p_block, ShaderLanguage::Node *p_node);
diff --git a/servers/rendering/shader_types.cpp b/servers/rendering/shader_types.cpp
index 0bf68b9e0f..a1e892498b 100644
--- a/servers/rendering/shader_types.cpp
+++ b/servers/rendering/shader_types.cpp
@@ -29,6 +29,7 @@
/*************************************************************************/
#include "shader_types.h"
+#include "core/math/math_defs.h"
const Map<StringName, ShaderLanguage::FunctionInfo> &ShaderTypes::get_functions(RS::ShaderMode p_mode) {
return shader_modes[p_mode].functions;
@@ -54,6 +55,9 @@ ShaderTypes::ShaderTypes() {
/*************** SPATIAL ***********************/
shader_modes[RS::SHADER_SPATIAL].functions["global"].built_ins["TIME"] = constt(ShaderLanguage::TYPE_FLOAT);
+ shader_modes[RS::SHADER_SPATIAL].functions["global"].built_ins["PI"] = constt(ShaderLanguage::TYPE_FLOAT);
+ shader_modes[RS::SHADER_SPATIAL].functions["global"].built_ins["TAU"] = constt(ShaderLanguage::TYPE_FLOAT);
+ shader_modes[RS::SHADER_SPATIAL].functions["global"].built_ins["E"] = constt(ShaderLanguage::TYPE_FLOAT);
shader_modes[RS::SHADER_SPATIAL].functions["vertex"].built_ins["VERTEX"] = ShaderLanguage::TYPE_VEC3;
shader_modes[RS::SHADER_SPATIAL].functions["vertex"].built_ins["NORMAL"] = ShaderLanguage::TYPE_VEC3;
@@ -120,11 +124,11 @@ ShaderTypes::ShaderTypes() {
shader_modes[RS::SHADER_SPATIAL].functions["fragment"].built_ins["AO"] = ShaderLanguage::TYPE_FLOAT;
shader_modes[RS::SHADER_SPATIAL].functions["fragment"].built_ins["AO_LIGHT_AFFECT"] = ShaderLanguage::TYPE_FLOAT;
shader_modes[RS::SHADER_SPATIAL].functions["fragment"].built_ins["EMISSION"] = ShaderLanguage::TYPE_VEC3;
- shader_modes[RS::SHADER_SPATIAL].functions["fragment"].built_ins["SCREEN_TEXTURE"] = ShaderLanguage::TYPE_SAMPLER2D;
- shader_modes[RS::SHADER_SPATIAL].functions["fragment"].built_ins["NORMAL_ROUGHNESS_TEXTURE"] = ShaderLanguage::TYPE_SAMPLER2D;
- shader_modes[RS::SHADER_SPATIAL].functions["fragment"].built_ins["DEPTH_TEXTURE"] = ShaderLanguage::TYPE_SAMPLER2D;
+ shader_modes[RS::SHADER_SPATIAL].functions["fragment"].built_ins["SCREEN_TEXTURE"] = constt(ShaderLanguage::TYPE_SAMPLER2D);
+ shader_modes[RS::SHADER_SPATIAL].functions["fragment"].built_ins["NORMAL_ROUGHNESS_TEXTURE"] = constt(ShaderLanguage::TYPE_SAMPLER2D);
+ shader_modes[RS::SHADER_SPATIAL].functions["fragment"].built_ins["DEPTH_TEXTURE"] = constt(ShaderLanguage::TYPE_SAMPLER2D);
shader_modes[RS::SHADER_SPATIAL].functions["fragment"].built_ins["DEPTH"] = ShaderLanguage::TYPE_FLOAT;
- shader_modes[RS::SHADER_SPATIAL].functions["fragment"].built_ins["SCREEN_UV"] = ShaderLanguage::TYPE_VEC2;
+ shader_modes[RS::SHADER_SPATIAL].functions["fragment"].built_ins["SCREEN_UV"] = constt(ShaderLanguage::TYPE_VEC2);
shader_modes[RS::SHADER_SPATIAL].functions["fragment"].built_ins["POINT_COORD"] = constt(ShaderLanguage::TYPE_VEC2);
shader_modes[RS::SHADER_SPATIAL].functions["fragment"].built_ins["OUTPUT_IS_SRGB"] = constt(ShaderLanguage::TYPE_BOOL);
@@ -227,6 +231,9 @@ ShaderTypes::ShaderTypes() {
/************ CANVAS ITEM **************************/
shader_modes[RS::SHADER_CANVAS_ITEM].functions["global"].built_ins["TIME"] = constt(ShaderLanguage::TYPE_FLOAT);
+ shader_modes[RS::SHADER_CANVAS_ITEM].functions["global"].built_ins["PI"] = constt(ShaderLanguage::TYPE_FLOAT);
+ shader_modes[RS::SHADER_CANVAS_ITEM].functions["global"].built_ins["TAU"] = constt(ShaderLanguage::TYPE_FLOAT);
+ shader_modes[RS::SHADER_CANVAS_ITEM].functions["global"].built_ins["E"] = constt(ShaderLanguage::TYPE_FLOAT);
shader_modes[RS::SHADER_CANVAS_ITEM].functions["vertex"].built_ins["VERTEX"] = ShaderLanguage::TYPE_VEC2;
shader_modes[RS::SHADER_CANVAS_ITEM].functions["vertex"].built_ins["UV"] = ShaderLanguage::TYPE_VEC2;
@@ -317,6 +324,9 @@ ShaderTypes::ShaderTypes() {
/************ PARTICLES **************************/
shader_modes[RS::SHADER_PARTICLES].functions["global"].built_ins["TIME"] = constt(ShaderLanguage::TYPE_FLOAT);
+ shader_modes[RS::SHADER_PARTICLES].functions["global"].built_ins["PI"] = constt(ShaderLanguage::TYPE_FLOAT);
+ shader_modes[RS::SHADER_PARTICLES].functions["global"].built_ins["TAU"] = constt(ShaderLanguage::TYPE_FLOAT);
+ shader_modes[RS::SHADER_PARTICLES].functions["global"].built_ins["E"] = constt(ShaderLanguage::TYPE_FLOAT);
shader_modes[RS::SHADER_PARTICLES].functions["start"].built_ins["COLOR"] = ShaderLanguage::TYPE_VEC4;
shader_modes[RS::SHADER_PARTICLES].functions["start"].built_ins["VELOCITY"] = ShaderLanguage::TYPE_VEC3;
@@ -381,6 +391,9 @@ ShaderTypes::ShaderTypes() {
/************ SKY **************************/
shader_modes[RS::SHADER_SKY].functions["global"].built_ins["TIME"] = constt(ShaderLanguage::TYPE_FLOAT);
+ shader_modes[RS::SHADER_SKY].functions["global"].built_ins["PI"] = constt(ShaderLanguage::TYPE_FLOAT);
+ shader_modes[RS::SHADER_SKY].functions["global"].built_ins["TAU"] = constt(ShaderLanguage::TYPE_FLOAT);
+ shader_modes[RS::SHADER_SKY].functions["global"].built_ins["E"] = constt(ShaderLanguage::TYPE_FLOAT);
shader_modes[RS::SHADER_SKY].functions["global"].built_ins["POSITION"] = constt(ShaderLanguage::TYPE_VEC3);
shader_modes[RS::SHADER_SKY].functions["global"].built_ins["RADIANCE"] = constt(ShaderLanguage::TYPE_SAMPLERCUBE);
shader_modes[RS::SHADER_SKY].functions["global"].built_ins["AT_HALF_RES_PASS"] = constt(ShaderLanguage::TYPE_BOOL);
diff --git a/servers/rendering_server.cpp b/servers/rendering_server.cpp
index a9601fd661..4741e90a81 100644
--- a/servers/rendering_server.cpp
+++ b/servers/rendering_server.cpp
@@ -2306,6 +2306,12 @@ RenderingServer::RenderingServer() {
"rendering/vulkan/rendering/back_end",
PROPERTY_HINT_ENUM, "ForwardClustered,ForwardMobile"));
+ GLOBAL_DEF("rendering/shader_compiler/shader_cache/enabled", true);
+ GLOBAL_DEF("rendering/shader_compiler/shader_cache/compress", true);
+ GLOBAL_DEF("rendering/shader_compiler/shader_cache/use_zstd_compression", true);
+ GLOBAL_DEF("rendering/shader_compiler/shader_cache/strip_debug", false);
+ GLOBAL_DEF("rendering/shader_compiler/shader_cache/strip_debug.release", true);
+
GLOBAL_DEF("rendering/reflections/sky_reflections/roughness_layers", 8);
GLOBAL_DEF("rendering/reflections/sky_reflections/texture_array_reflections", true);
GLOBAL_DEF("rendering/reflections/sky_reflections/texture_array_reflections.mobile", false);
diff --git a/tests/test_image.h b/tests/test_image.h
index d73717f5b7..99c2a9380d 100644
--- a/tests/test_image.h
+++ b/tests/test_image.h
@@ -101,8 +101,8 @@ TEST_CASE("[Image] Saving and loading") {
Ref<Image> image_bmp = memnew(Image());
FileAccessRef f_bmp = FileAccess::open(TestUtils::get_data_path("images/icon.bmp"), FileAccess::READ, &err);
PackedByteArray data_bmp;
- data_bmp.resize(f_bmp->get_len() + 1);
- f_bmp->get_buffer(data_bmp.ptrw(), f_bmp->get_len());
+ data_bmp.resize(f_bmp->get_length() + 1);
+ f_bmp->get_buffer(data_bmp.ptrw(), f_bmp->get_length());
CHECK_MESSAGE(
image_bmp->load_bmp_from_buffer(data_bmp) == OK,
"The BMP image should load successfully.");
@@ -111,8 +111,8 @@ TEST_CASE("[Image] Saving and loading") {
Ref<Image> image_jpg = memnew(Image());
FileAccessRef f_jpg = FileAccess::open(TestUtils::get_data_path("images/icon.jpg"), FileAccess::READ, &err);
PackedByteArray data_jpg;
- data_jpg.resize(f_jpg->get_len() + 1);
- f_jpg->get_buffer(data_jpg.ptrw(), f_jpg->get_len());
+ data_jpg.resize(f_jpg->get_length() + 1);
+ f_jpg->get_buffer(data_jpg.ptrw(), f_jpg->get_length());
CHECK_MESSAGE(
image_jpg->load_jpg_from_buffer(data_jpg) == OK,
"The JPG image should load successfully.");
@@ -121,8 +121,8 @@ TEST_CASE("[Image] Saving and loading") {
Ref<Image> image_webp = memnew(Image());
FileAccessRef f_webp = FileAccess::open(TestUtils::get_data_path("images/icon.webp"), FileAccess::READ, &err);
PackedByteArray data_webp;
- data_webp.resize(f_webp->get_len() + 1);
- f_webp->get_buffer(data_webp.ptrw(), f_webp->get_len());
+ data_webp.resize(f_webp->get_length() + 1);
+ f_webp->get_buffer(data_webp.ptrw(), f_webp->get_length());
CHECK_MESSAGE(
image_webp->load_webp_from_buffer(data_webp) == OK,
"The WEBP image should load successfully.");
@@ -131,8 +131,8 @@ TEST_CASE("[Image] Saving and loading") {
Ref<Image> image_png = memnew(Image());
FileAccessRef f_png = FileAccess::open(TestUtils::get_data_path("images/icon.png"), FileAccess::READ, &err);
PackedByteArray data_png;
- data_png.resize(f_png->get_len() + 1);
- f_png->get_buffer(data_png.ptrw(), f_png->get_len());
+ data_png.resize(f_png->get_length() + 1);
+ f_png->get_buffer(data_png.ptrw(), f_png->get_length());
CHECK_MESSAGE(
image_png->load_png_from_buffer(data_png) == OK,
"The PNG image should load successfully.");
@@ -141,8 +141,8 @@ TEST_CASE("[Image] Saving and loading") {
Ref<Image> image_tga = memnew(Image());
FileAccessRef f_tga = FileAccess::open(TestUtils::get_data_path("images/icon.tga"), FileAccess::READ, &err);
PackedByteArray data_tga;
- data_tga.resize(f_tga->get_len() + 1);
- f_tga->get_buffer(data_tga.ptrw(), f_tga->get_len());
+ data_tga.resize(f_tga->get_length() + 1);
+ f_tga->get_buffer(data_tga.ptrw(), f_tga->get_length());
CHECK_MESSAGE(
image_tga->load_tga_from_buffer(data_tga) == OK,
"The TGA image should load successfully.");
diff --git a/tests/test_main.cpp b/tests/test_main.cpp
index 67fb38aa86..54327caf3d 100644
--- a/tests/test_main.cpp
+++ b/tests/test_main.cpp
@@ -121,24 +121,27 @@ int test_main(int argc, char *argv[]) {
test_args.push_back(arg);
}
}
- // Convert Godot command line arguments back to standard arguments.
- char **doctest_args = new char *[test_args.size()];
- for (int x = 0; x < test_args.size(); x++) {
- // Operation to convert Godot string to non wchar string.
- CharString cs = test_args[x].utf8();
- const char *str = cs.get_data();
- // Allocate the string copy.
- doctest_args[x] = new char[strlen(str) + 1];
- // Copy this into memory.
- memcpy(doctest_args[x], str, strlen(str) + 1);
- }
- test_context.applyCommandLine(test_args.size(), doctest_args);
+ if (test_args.size() > 0) {
+ // Convert Godot command line arguments back to standard arguments.
+ char **doctest_args = new char *[test_args.size()];
+ for (int x = 0; x < test_args.size(); x++) {
+ // Operation to convert Godot string to non wchar string.
+ CharString cs = test_args[x].utf8();
+ const char *str = cs.get_data();
+ // Allocate the string copy.
+ doctest_args[x] = new char[strlen(str) + 1];
+ // Copy this into memory.
+ memcpy(doctest_args[x], str, strlen(str) + 1);
+ }
+
+ test_context.applyCommandLine(test_args.size(), doctest_args);
- for (int x = 0; x < test_args.size(); x++) {
- delete[] doctest_args[x];
+ for (int x = 0; x < test_args.size(); x++) {
+ delete[] doctest_args[x];
+ }
+ delete[] doctest_args;
}
- delete[] doctest_args;
return test_context.run();
}
diff --git a/tests/test_math.cpp b/tests/test_math.cpp
index 85cfdf7d2c..88c32713ed 100644
--- a/tests/test_math.cpp
+++ b/tests/test_math.cpp
@@ -529,8 +529,8 @@ MainLoop *test() {
ERR_FAIL_COND_V_MSG(!fa, nullptr, "Could not open file: " + test);
Vector<uint8_t> buf;
- uint64_t flen = fa->get_len();
- buf.resize(fa->get_len() + 1);
+ uint64_t flen = fa->get_length();
+ buf.resize(fa->get_length() + 1);
fa->get_buffer(buf.ptrw(), flen);
buf.write[flen] = 0;
diff --git a/tests/test_pck_packer.h b/tests/test_pck_packer.h
index 8e4721b821..06e4e64963 100644
--- a/tests/test_pck_packer.h
+++ b/tests/test_pck_packer.h
@@ -62,10 +62,10 @@ TEST_CASE("[PCKPacker] Pack an empty PCK file") {
err == OK,
"The generated empty PCK file should be opened successfully.");
CHECK_MESSAGE(
- f->get_len() >= 100,
+ f->get_length() >= 100,
"The generated empty PCK file shouldn't be too small (it should have the PCK header).");
CHECK_MESSAGE(
- f->get_len() <= 500,
+ f->get_length() <= 500,
"The generated empty PCK file shouldn't be too large.");
}
@@ -103,10 +103,10 @@ TEST_CASE("[PCKPacker] Pack a PCK file with some files and directories") {
err == OK,
"The generated non-empty PCK file should be opened successfully.");
CHECK_MESSAGE(
- f->get_len() >= 25000,
+ f->get_length() >= 25000,
"The generated non-empty PCK file should be large enough to actually hold the contents specified above.");
CHECK_MESSAGE(
- f->get_len() <= 35000,
+ f->get_length() <= 35000,
"The generated non-empty PCK file shouldn't be too large.");
}
} // namespace TestPCKPacker
diff --git a/thirdparty/README.md b/thirdparty/README.md
index d3599fd195..03a2ddf5e4 100644
--- a/thirdparty/README.md
+++ b/thirdparty/README.md
@@ -367,7 +367,7 @@ File extracted from upstream release tarball:
## meshoptimizer
- Upstream: https://github.com/zeux/meshoptimizer
-- Version: 0.16 (95893c0566646434dd675b708d293fcb2d526d08, 2021)
+- Version: git (f5d83e879c48f8664783a69b4f50711d27549b66, 2021)
- License: MIT
Files extracted from upstream repository:
@@ -469,6 +469,10 @@ Collection of single-file libraries used in Godot components.
* Version: git (2f625846a775501fb69456567409a8b12f10ea25, 2012)
* License: BSD-3-Clause
* Modifications: use `const char*` instead of `char*` for input string
+- `smolv.h`
+ * Upstream: https://github.com/aras-p/smol-v
+ * Version: git (4b52c165c13763051a18e80ffbc2ee436314ceb2, 2020)
+ * License: Public Domain or MIT
- `stb_rect_pack.h`
* Upstream: https://github.com/nothings/stb
* Version: 1.00 (2bb4a0accd4003c1db4c24533981e01b1adfd656, 2019)
@@ -731,3 +735,4 @@ Files extracted from upstream source:
- lib/{common/,compress/,decompress/,zstd.h}
- LICENSE
+
diff --git a/thirdparty/meshoptimizer/simplifier.cpp b/thirdparty/meshoptimizer/simplifier.cpp
index 059cabb055..0f10ebef4b 100644
--- a/thirdparty/meshoptimizer/simplifier.cpp
+++ b/thirdparty/meshoptimizer/simplifier.cpp
@@ -120,8 +120,13 @@ struct PositionHasher
{
const unsigned int* key = reinterpret_cast<const unsigned int*>(vertex_positions + index * vertex_stride_float);
+ // scramble bits to make sure that integer coordinates have entropy in lower bits
+ unsigned int x = key[0] ^ (key[0] >> 17);
+ unsigned int y = key[1] ^ (key[1] >> 17);
+ unsigned int z = key[2] ^ (key[2] >> 17);
+
// Optimized Spatial Hashing for Collision Detection of Deformable Objects
- return (key[0] * 73856093) ^ (key[1] * 19349663) ^ (key[2] * 83492791);
+ return (x * 73856093) ^ (y * 19349663) ^ (z * 83492791);
}
bool equal(unsigned int lhs, unsigned int rhs) const
@@ -1160,7 +1165,7 @@ struct IdHasher
struct TriangleHasher
{
- unsigned int* indices;
+ const unsigned int* indices;
size_t hash(unsigned int i) const
{
@@ -1375,9 +1380,10 @@ static float interpolate(float y, float x0, float y0, float x1, float y1, float
} // namespace meshopt
#ifndef NDEBUG
-unsigned char* meshopt_simplifyDebugKind = 0;
-unsigned int* meshopt_simplifyDebugLoop = 0;
-unsigned int* meshopt_simplifyDebugLoopBack = 0;
+// Note: this is only exposed for debug visualization purposes; do *not* use these in debug builds
+MESHOPTIMIZER_API unsigned char* meshopt_simplifyDebugKind = 0;
+MESHOPTIMIZER_API unsigned int* meshopt_simplifyDebugLoop = 0;
+MESHOPTIMIZER_API unsigned int* meshopt_simplifyDebugLoopBack = 0;
#endif
size_t meshopt_simplify(unsigned int* destination, const unsigned int* indices, size_t index_count, const float* vertex_positions_data, size_t vertex_count, size_t vertex_positions_stride, size_t target_index_count, float target_error, float* out_result_error)
diff --git a/thirdparty/misc/smolv.cpp b/thirdparty/misc/smolv.cpp
new file mode 100644
index 0000000000..26ed7294f9
--- /dev/null
+++ b/thirdparty/misc/smolv.cpp
@@ -0,0 +1,2108 @@
+// smol-v - public domain - https://github.com/aras-p/smol-v
+// authored 2016-2020 by Aras Pranckevicius
+// no warranty implied; use at your own risk
+// See end of file for license information.
+
+#include "smolv.h"
+#include <stdint.h>
+#include <vector>
+#include <algorithm>
+#include <cstdio>
+#include <cstring>
+
+#if !defined(_MSC_VER) && __cplusplus < 201103L
+#define static_assert(x,y)
+#endif
+
+#define _SMOLV_ARRAY_SIZE(a) (sizeof(a)/sizeof((a)[0]))
+
+// --------------------------------------------------------------------------------------------
+// Metadata about known SPIR-V operations
+
+enum SpvOp
+{
+ SpvOpNop = 0,
+ SpvOpUndef = 1,
+ SpvOpSourceContinued = 2,
+ SpvOpSource = 3,
+ SpvOpSourceExtension = 4,
+ SpvOpName = 5,
+ SpvOpMemberName = 6,
+ SpvOpString = 7,
+ SpvOpLine = 8,
+ SpvOpExtension = 10,
+ SpvOpExtInstImport = 11,
+ SpvOpExtInst = 12,
+ SpvOpVectorShuffleCompact = 13, // not in SPIR-V, added for SMOL-V!
+ SpvOpMemoryModel = 14,
+ SpvOpEntryPoint = 15,
+ SpvOpExecutionMode = 16,
+ SpvOpCapability = 17,
+ SpvOpTypeVoid = 19,
+ SpvOpTypeBool = 20,
+ SpvOpTypeInt = 21,
+ SpvOpTypeFloat = 22,
+ SpvOpTypeVector = 23,
+ SpvOpTypeMatrix = 24,
+ SpvOpTypeImage = 25,
+ SpvOpTypeSampler = 26,
+ SpvOpTypeSampledImage = 27,
+ SpvOpTypeArray = 28,
+ SpvOpTypeRuntimeArray = 29,
+ SpvOpTypeStruct = 30,
+ SpvOpTypeOpaque = 31,
+ SpvOpTypePointer = 32,
+ SpvOpTypeFunction = 33,
+ SpvOpTypeEvent = 34,
+ SpvOpTypeDeviceEvent = 35,
+ SpvOpTypeReserveId = 36,
+ SpvOpTypeQueue = 37,
+ SpvOpTypePipe = 38,
+ SpvOpTypeForwardPointer = 39,
+ SpvOpConstantTrue = 41,
+ SpvOpConstantFalse = 42,
+ SpvOpConstant = 43,
+ SpvOpConstantComposite = 44,
+ SpvOpConstantSampler = 45,
+ SpvOpConstantNull = 46,
+ SpvOpSpecConstantTrue = 48,
+ SpvOpSpecConstantFalse = 49,
+ SpvOpSpecConstant = 50,
+ SpvOpSpecConstantComposite = 51,
+ SpvOpSpecConstantOp = 52,
+ SpvOpFunction = 54,
+ SpvOpFunctionParameter = 55,
+ SpvOpFunctionEnd = 56,
+ SpvOpFunctionCall = 57,
+ SpvOpVariable = 59,
+ SpvOpImageTexelPointer = 60,
+ SpvOpLoad = 61,
+ SpvOpStore = 62,
+ SpvOpCopyMemory = 63,
+ SpvOpCopyMemorySized = 64,
+ SpvOpAccessChain = 65,
+ SpvOpInBoundsAccessChain = 66,
+ SpvOpPtrAccessChain = 67,
+ SpvOpArrayLength = 68,
+ SpvOpGenericPtrMemSemantics = 69,
+ SpvOpInBoundsPtrAccessChain = 70,
+ SpvOpDecorate = 71,
+ SpvOpMemberDecorate = 72,
+ SpvOpDecorationGroup = 73,
+ SpvOpGroupDecorate = 74,
+ SpvOpGroupMemberDecorate = 75,
+ SpvOpVectorExtractDynamic = 77,
+ SpvOpVectorInsertDynamic = 78,
+ SpvOpVectorShuffle = 79,
+ SpvOpCompositeConstruct = 80,
+ SpvOpCompositeExtract = 81,
+ SpvOpCompositeInsert = 82,
+ SpvOpCopyObject = 83,
+ SpvOpTranspose = 84,
+ SpvOpSampledImage = 86,
+ SpvOpImageSampleImplicitLod = 87,
+ SpvOpImageSampleExplicitLod = 88,
+ SpvOpImageSampleDrefImplicitLod = 89,
+ SpvOpImageSampleDrefExplicitLod = 90,
+ SpvOpImageSampleProjImplicitLod = 91,
+ SpvOpImageSampleProjExplicitLod = 92,
+ SpvOpImageSampleProjDrefImplicitLod = 93,
+ SpvOpImageSampleProjDrefExplicitLod = 94,
+ SpvOpImageFetch = 95,
+ SpvOpImageGather = 96,
+ SpvOpImageDrefGather = 97,
+ SpvOpImageRead = 98,
+ SpvOpImageWrite = 99,
+ SpvOpImage = 100,
+ SpvOpImageQueryFormat = 101,
+ SpvOpImageQueryOrder = 102,
+ SpvOpImageQuerySizeLod = 103,
+ SpvOpImageQuerySize = 104,
+ SpvOpImageQueryLod = 105,
+ SpvOpImageQueryLevels = 106,
+ SpvOpImageQuerySamples = 107,
+ SpvOpConvertFToU = 109,
+ SpvOpConvertFToS = 110,
+ SpvOpConvertSToF = 111,
+ SpvOpConvertUToF = 112,
+ SpvOpUConvert = 113,
+ SpvOpSConvert = 114,
+ SpvOpFConvert = 115,
+ SpvOpQuantizeToF16 = 116,
+ SpvOpConvertPtrToU = 117,
+ SpvOpSatConvertSToU = 118,
+ SpvOpSatConvertUToS = 119,
+ SpvOpConvertUToPtr = 120,
+ SpvOpPtrCastToGeneric = 121,
+ SpvOpGenericCastToPtr = 122,
+ SpvOpGenericCastToPtrExplicit = 123,
+ SpvOpBitcast = 124,
+ SpvOpSNegate = 126,
+ SpvOpFNegate = 127,
+ SpvOpIAdd = 128,
+ SpvOpFAdd = 129,
+ SpvOpISub = 130,
+ SpvOpFSub = 131,
+ SpvOpIMul = 132,
+ SpvOpFMul = 133,
+ SpvOpUDiv = 134,
+ SpvOpSDiv = 135,
+ SpvOpFDiv = 136,
+ SpvOpUMod = 137,
+ SpvOpSRem = 138,
+ SpvOpSMod = 139,
+ SpvOpFRem = 140,
+ SpvOpFMod = 141,
+ SpvOpVectorTimesScalar = 142,
+ SpvOpMatrixTimesScalar = 143,
+ SpvOpVectorTimesMatrix = 144,
+ SpvOpMatrixTimesVector = 145,
+ SpvOpMatrixTimesMatrix = 146,
+ SpvOpOuterProduct = 147,
+ SpvOpDot = 148,
+ SpvOpIAddCarry = 149,
+ SpvOpISubBorrow = 150,
+ SpvOpUMulExtended = 151,
+ SpvOpSMulExtended = 152,
+ SpvOpAny = 154,
+ SpvOpAll = 155,
+ SpvOpIsNan = 156,
+ SpvOpIsInf = 157,
+ SpvOpIsFinite = 158,
+ SpvOpIsNormal = 159,
+ SpvOpSignBitSet = 160,
+ SpvOpLessOrGreater = 161,
+ SpvOpOrdered = 162,
+ SpvOpUnordered = 163,
+ SpvOpLogicalEqual = 164,
+ SpvOpLogicalNotEqual = 165,
+ SpvOpLogicalOr = 166,
+ SpvOpLogicalAnd = 167,
+ SpvOpLogicalNot = 168,
+ SpvOpSelect = 169,
+ SpvOpIEqual = 170,
+ SpvOpINotEqual = 171,
+ SpvOpUGreaterThan = 172,
+ SpvOpSGreaterThan = 173,
+ SpvOpUGreaterThanEqual = 174,
+ SpvOpSGreaterThanEqual = 175,
+ SpvOpULessThan = 176,
+ SpvOpSLessThan = 177,
+ SpvOpULessThanEqual = 178,
+ SpvOpSLessThanEqual = 179,
+ SpvOpFOrdEqual = 180,
+ SpvOpFUnordEqual = 181,
+ SpvOpFOrdNotEqual = 182,
+ SpvOpFUnordNotEqual = 183,
+ SpvOpFOrdLessThan = 184,
+ SpvOpFUnordLessThan = 185,
+ SpvOpFOrdGreaterThan = 186,
+ SpvOpFUnordGreaterThan = 187,
+ SpvOpFOrdLessThanEqual = 188,
+ SpvOpFUnordLessThanEqual = 189,
+ SpvOpFOrdGreaterThanEqual = 190,
+ SpvOpFUnordGreaterThanEqual = 191,
+ SpvOpShiftRightLogical = 194,
+ SpvOpShiftRightArithmetic = 195,
+ SpvOpShiftLeftLogical = 196,
+ SpvOpBitwiseOr = 197,
+ SpvOpBitwiseXor = 198,
+ SpvOpBitwiseAnd = 199,
+ SpvOpNot = 200,
+ SpvOpBitFieldInsert = 201,
+ SpvOpBitFieldSExtract = 202,
+ SpvOpBitFieldUExtract = 203,
+ SpvOpBitReverse = 204,
+ SpvOpBitCount = 205,
+ SpvOpDPdx = 207,
+ SpvOpDPdy = 208,
+ SpvOpFwidth = 209,
+ SpvOpDPdxFine = 210,
+ SpvOpDPdyFine = 211,
+ SpvOpFwidthFine = 212,
+ SpvOpDPdxCoarse = 213,
+ SpvOpDPdyCoarse = 214,
+ SpvOpFwidthCoarse = 215,
+ SpvOpEmitVertex = 218,
+ SpvOpEndPrimitive = 219,
+ SpvOpEmitStreamVertex = 220,
+ SpvOpEndStreamPrimitive = 221,
+ SpvOpControlBarrier = 224,
+ SpvOpMemoryBarrier = 225,
+ SpvOpAtomicLoad = 227,
+ SpvOpAtomicStore = 228,
+ SpvOpAtomicExchange = 229,
+ SpvOpAtomicCompareExchange = 230,
+ SpvOpAtomicCompareExchangeWeak = 231,
+ SpvOpAtomicIIncrement = 232,
+ SpvOpAtomicIDecrement = 233,
+ SpvOpAtomicIAdd = 234,
+ SpvOpAtomicISub = 235,
+ SpvOpAtomicSMin = 236,
+ SpvOpAtomicUMin = 237,
+ SpvOpAtomicSMax = 238,
+ SpvOpAtomicUMax = 239,
+ SpvOpAtomicAnd = 240,
+ SpvOpAtomicOr = 241,
+ SpvOpAtomicXor = 242,
+ SpvOpPhi = 245,
+ SpvOpLoopMerge = 246,
+ SpvOpSelectionMerge = 247,
+ SpvOpLabel = 248,
+ SpvOpBranch = 249,
+ SpvOpBranchConditional = 250,
+ SpvOpSwitch = 251,
+ SpvOpKill = 252,
+ SpvOpReturn = 253,
+ SpvOpReturnValue = 254,
+ SpvOpUnreachable = 255,
+ SpvOpLifetimeStart = 256,
+ SpvOpLifetimeStop = 257,
+ SpvOpGroupAsyncCopy = 259,
+ SpvOpGroupWaitEvents = 260,
+ SpvOpGroupAll = 261,
+ SpvOpGroupAny = 262,
+ SpvOpGroupBroadcast = 263,
+ SpvOpGroupIAdd = 264,
+ SpvOpGroupFAdd = 265,
+ SpvOpGroupFMin = 266,
+ SpvOpGroupUMin = 267,
+ SpvOpGroupSMin = 268,
+ SpvOpGroupFMax = 269,
+ SpvOpGroupUMax = 270,
+ SpvOpGroupSMax = 271,
+ SpvOpReadPipe = 274,
+ SpvOpWritePipe = 275,
+ SpvOpReservedReadPipe = 276,
+ SpvOpReservedWritePipe = 277,
+ SpvOpReserveReadPipePackets = 278,
+ SpvOpReserveWritePipePackets = 279,
+ SpvOpCommitReadPipe = 280,
+ SpvOpCommitWritePipe = 281,
+ SpvOpIsValidReserveId = 282,
+ SpvOpGetNumPipePackets = 283,
+ SpvOpGetMaxPipePackets = 284,
+ SpvOpGroupReserveReadPipePackets = 285,
+ SpvOpGroupReserveWritePipePackets = 286,
+ SpvOpGroupCommitReadPipe = 287,
+ SpvOpGroupCommitWritePipe = 288,
+ SpvOpEnqueueMarker = 291,
+ SpvOpEnqueueKernel = 292,
+ SpvOpGetKernelNDrangeSubGroupCount = 293,
+ SpvOpGetKernelNDrangeMaxSubGroupSize = 294,
+ SpvOpGetKernelWorkGroupSize = 295,
+ SpvOpGetKernelPreferredWorkGroupSizeMultiple = 296,
+ SpvOpRetainEvent = 297,
+ SpvOpReleaseEvent = 298,
+ SpvOpCreateUserEvent = 299,
+ SpvOpIsValidEvent = 300,
+ SpvOpSetUserEventStatus = 301,
+ SpvOpCaptureEventProfilingInfo = 302,
+ SpvOpGetDefaultQueue = 303,
+ SpvOpBuildNDRange = 304,
+ SpvOpImageSparseSampleImplicitLod = 305,
+ SpvOpImageSparseSampleExplicitLod = 306,
+ SpvOpImageSparseSampleDrefImplicitLod = 307,
+ SpvOpImageSparseSampleDrefExplicitLod = 308,
+ SpvOpImageSparseSampleProjImplicitLod = 309,
+ SpvOpImageSparseSampleProjExplicitLod = 310,
+ SpvOpImageSparseSampleProjDrefImplicitLod = 311,
+ SpvOpImageSparseSampleProjDrefExplicitLod = 312,
+ SpvOpImageSparseFetch = 313,
+ SpvOpImageSparseGather = 314,
+ SpvOpImageSparseDrefGather = 315,
+ SpvOpImageSparseTexelsResident = 316,
+ SpvOpNoLine = 317,
+ SpvOpAtomicFlagTestAndSet = 318,
+ SpvOpAtomicFlagClear = 319,
+ SpvOpImageSparseRead = 320,
+ SpvOpSizeOf = 321,
+ SpvOpTypePipeStorage = 322,
+ SpvOpConstantPipeStorage = 323,
+ SpvOpCreatePipeFromPipeStorage = 324,
+ SpvOpGetKernelLocalSizeForSubgroupCount = 325,
+ SpvOpGetKernelMaxNumSubgroups = 326,
+ SpvOpTypeNamedBarrier = 327,
+ SpvOpNamedBarrierInitialize = 328,
+ SpvOpMemoryNamedBarrier = 329,
+ SpvOpModuleProcessed = 330,
+ SpvOpExecutionModeId = 331,
+ SpvOpDecorateId = 332,
+ SpvOpGroupNonUniformElect = 333,
+ SpvOpGroupNonUniformAll = 334,
+ SpvOpGroupNonUniformAny = 335,
+ SpvOpGroupNonUniformAllEqual = 336,
+ SpvOpGroupNonUniformBroadcast = 337,
+ SpvOpGroupNonUniformBroadcastFirst = 338,
+ SpvOpGroupNonUniformBallot = 339,
+ SpvOpGroupNonUniformInverseBallot = 340,
+ SpvOpGroupNonUniformBallotBitExtract = 341,
+ SpvOpGroupNonUniformBallotBitCount = 342,
+ SpvOpGroupNonUniformBallotFindLSB = 343,
+ SpvOpGroupNonUniformBallotFindMSB = 344,
+ SpvOpGroupNonUniformShuffle = 345,
+ SpvOpGroupNonUniformShuffleXor = 346,
+ SpvOpGroupNonUniformShuffleUp = 347,
+ SpvOpGroupNonUniformShuffleDown = 348,
+ SpvOpGroupNonUniformIAdd = 349,
+ SpvOpGroupNonUniformFAdd = 350,
+ SpvOpGroupNonUniformIMul = 351,
+ SpvOpGroupNonUniformFMul = 352,
+ SpvOpGroupNonUniformSMin = 353,
+ SpvOpGroupNonUniformUMin = 354,
+ SpvOpGroupNonUniformFMin = 355,
+ SpvOpGroupNonUniformSMax = 356,
+ SpvOpGroupNonUniformUMax = 357,
+ SpvOpGroupNonUniformFMax = 358,
+ SpvOpGroupNonUniformBitwiseAnd = 359,
+ SpvOpGroupNonUniformBitwiseOr = 360,
+ SpvOpGroupNonUniformBitwiseXor = 361,
+ SpvOpGroupNonUniformLogicalAnd = 362,
+ SpvOpGroupNonUniformLogicalOr = 363,
+ SpvOpGroupNonUniformLogicalXor = 364,
+ SpvOpGroupNonUniformQuadBroadcast = 365,
+ SpvOpGroupNonUniformQuadSwap = 366,
+};
+static const int kKnownOpsCount = SpvOpGroupNonUniformQuadSwap+1;
+
+
+static const char* kSpirvOpNames[] =
+{
+ "Nop",
+ "Undef",
+ "SourceContinued",
+ "Source",
+ "SourceExtension",
+ "Name",
+ "MemberName",
+ "String",
+ "Line",
+ "#9",
+ "Extension",
+ "ExtInstImport",
+ "ExtInst",
+ "VectorShuffleCompact",
+ "MemoryModel",
+ "EntryPoint",
+ "ExecutionMode",
+ "Capability",
+ "#18",
+ "TypeVoid",
+ "TypeBool",
+ "TypeInt",
+ "TypeFloat",
+ "TypeVector",
+ "TypeMatrix",
+ "TypeImage",
+ "TypeSampler",
+ "TypeSampledImage",
+ "TypeArray",
+ "TypeRuntimeArray",
+ "TypeStruct",
+ "TypeOpaque",
+ "TypePointer",
+ "TypeFunction",
+ "TypeEvent",
+ "TypeDeviceEvent",
+ "TypeReserveId",
+ "TypeQueue",
+ "TypePipe",
+ "TypeForwardPointer",
+ "#40",
+ "ConstantTrue",
+ "ConstantFalse",
+ "Constant",
+ "ConstantComposite",
+ "ConstantSampler",
+ "ConstantNull",
+ "#47",
+ "SpecConstantTrue",
+ "SpecConstantFalse",
+ "SpecConstant",
+ "SpecConstantComposite",
+ "SpecConstantOp",
+ "#53",
+ "Function",
+ "FunctionParameter",
+ "FunctionEnd",
+ "FunctionCall",
+ "#58",
+ "Variable",
+ "ImageTexelPointer",
+ "Load",
+ "Store",
+ "CopyMemory",
+ "CopyMemorySized",
+ "AccessChain",
+ "InBoundsAccessChain",
+ "PtrAccessChain",
+ "ArrayLength",
+ "GenericPtrMemSemantics",
+ "InBoundsPtrAccessChain",
+ "Decorate",
+ "MemberDecorate",
+ "DecorationGroup",
+ "GroupDecorate",
+ "GroupMemberDecorate",
+ "#76",
+ "VectorExtractDynamic",
+ "VectorInsertDynamic",
+ "VectorShuffle",
+ "CompositeConstruct",
+ "CompositeExtract",
+ "CompositeInsert",
+ "CopyObject",
+ "Transpose",
+ "#85",
+ "SampledImage",
+ "ImageSampleImplicitLod",
+ "ImageSampleExplicitLod",
+ "ImageSampleDrefImplicitLod",
+ "ImageSampleDrefExplicitLod",
+ "ImageSampleProjImplicitLod",
+ "ImageSampleProjExplicitLod",
+ "ImageSampleProjDrefImplicitLod",
+ "ImageSampleProjDrefExplicitLod",
+ "ImageFetch",
+ "ImageGather",
+ "ImageDrefGather",
+ "ImageRead",
+ "ImageWrite",
+ "Image",
+ "ImageQueryFormat",
+ "ImageQueryOrder",
+ "ImageQuerySizeLod",
+ "ImageQuerySize",
+ "ImageQueryLod",
+ "ImageQueryLevels",
+ "ImageQuerySamples",
+ "#108",
+ "ConvertFToU",
+ "ConvertFToS",
+ "ConvertSToF",
+ "ConvertUToF",
+ "UConvert",
+ "SConvert",
+ "FConvert",
+ "QuantizeToF16",
+ "ConvertPtrToU",
+ "SatConvertSToU",
+ "SatConvertUToS",
+ "ConvertUToPtr",
+ "PtrCastToGeneric",
+ "GenericCastToPtr",
+ "GenericCastToPtrExplicit",
+ "Bitcast",
+ "#125",
+ "SNegate",
+ "FNegate",
+ "IAdd",
+ "FAdd",
+ "ISub",
+ "FSub",
+ "IMul",
+ "FMul",
+ "UDiv",
+ "SDiv",
+ "FDiv",
+ "UMod",
+ "SRem",
+ "SMod",
+ "FRem",
+ "FMod",
+ "VectorTimesScalar",
+ "MatrixTimesScalar",
+ "VectorTimesMatrix",
+ "MatrixTimesVector",
+ "MatrixTimesMatrix",
+ "OuterProduct",
+ "Dot",
+ "IAddCarry",
+ "ISubBorrow",
+ "UMulExtended",
+ "SMulExtended",
+ "#153",
+ "Any",
+ "All",
+ "IsNan",
+ "IsInf",
+ "IsFinite",
+ "IsNormal",
+ "SignBitSet",
+ "LessOrGreater",
+ "Ordered",
+ "Unordered",
+ "LogicalEqual",
+ "LogicalNotEqual",
+ "LogicalOr",
+ "LogicalAnd",
+ "LogicalNot",
+ "Select",
+ "IEqual",
+ "INotEqual",
+ "UGreaterThan",
+ "SGreaterThan",
+ "UGreaterThanEqual",
+ "SGreaterThanEqual",
+ "ULessThan",
+ "SLessThan",
+ "ULessThanEqual",
+ "SLessThanEqual",
+ "FOrdEqual",
+ "FUnordEqual",
+ "FOrdNotEqual",
+ "FUnordNotEqual",
+ "FOrdLessThan",
+ "FUnordLessThan",
+ "FOrdGreaterThan",
+ "FUnordGreaterThan",
+ "FOrdLessThanEqual",
+ "FUnordLessThanEqual",
+ "FOrdGreaterThanEqual",
+ "FUnordGreaterThanEqual",
+ "#192",
+ "#193",
+ "ShiftRightLogical",
+ "ShiftRightArithmetic",
+ "ShiftLeftLogical",
+ "BitwiseOr",
+ "BitwiseXor",
+ "BitwiseAnd",
+ "Not",
+ "BitFieldInsert",
+ "BitFieldSExtract",
+ "BitFieldUExtract",
+ "BitReverse",
+ "BitCount",
+ "#206",
+ "DPdx",
+ "DPdy",
+ "Fwidth",
+ "DPdxFine",
+ "DPdyFine",
+ "FwidthFine",
+ "DPdxCoarse",
+ "DPdyCoarse",
+ "FwidthCoarse",
+ "#216",
+ "#217",
+ "EmitVertex",
+ "EndPrimitive",
+ "EmitStreamVertex",
+ "EndStreamPrimitive",
+ "#222",
+ "#223",
+ "ControlBarrier",
+ "MemoryBarrier",
+ "#226",
+ "AtomicLoad",
+ "AtomicStore",
+ "AtomicExchange",
+ "AtomicCompareExchange",
+ "AtomicCompareExchangeWeak",
+ "AtomicIIncrement",
+ "AtomicIDecrement",
+ "AtomicIAdd",
+ "AtomicISub",
+ "AtomicSMin",
+ "AtomicUMin",
+ "AtomicSMax",
+ "AtomicUMax",
+ "AtomicAnd",
+ "AtomicOr",
+ "AtomicXor",
+ "#243",
+ "#244",
+ "Phi",
+ "LoopMerge",
+ "SelectionMerge",
+ "Label",
+ "Branch",
+ "BranchConditional",
+ "Switch",
+ "Kill",
+ "Return",
+ "ReturnValue",
+ "Unreachable",
+ "LifetimeStart",
+ "LifetimeStop",
+ "#258",
+ "GroupAsyncCopy",
+ "GroupWaitEvents",
+ "GroupAll",
+ "GroupAny",
+ "GroupBroadcast",
+ "GroupIAdd",
+ "GroupFAdd",
+ "GroupFMin",
+ "GroupUMin",
+ "GroupSMin",
+ "GroupFMax",
+ "GroupUMax",
+ "GroupSMax",
+ "#272",
+ "#273",
+ "ReadPipe",
+ "WritePipe",
+ "ReservedReadPipe",
+ "ReservedWritePipe",
+ "ReserveReadPipePackets",
+ "ReserveWritePipePackets",
+ "CommitReadPipe",
+ "CommitWritePipe",
+ "IsValidReserveId",
+ "GetNumPipePackets",
+ "GetMaxPipePackets",
+ "GroupReserveReadPipePackets",
+ "GroupReserveWritePipePackets",
+ "GroupCommitReadPipe",
+ "GroupCommitWritePipe",
+ "#289",
+ "#290",
+ "EnqueueMarker",
+ "EnqueueKernel",
+ "GetKernelNDrangeSubGroupCount",
+ "GetKernelNDrangeMaxSubGroupSize",
+ "GetKernelWorkGroupSize",
+ "GetKernelPreferredWorkGroupSizeMultiple",
+ "RetainEvent",
+ "ReleaseEvent",
+ "CreateUserEvent",
+ "IsValidEvent",
+ "SetUserEventStatus",
+ "CaptureEventProfilingInfo",
+ "GetDefaultQueue",
+ "BuildNDRange",
+ "ImageSparseSampleImplicitLod",
+ "ImageSparseSampleExplicitLod",
+ "ImageSparseSampleDrefImplicitLod",
+ "ImageSparseSampleDrefExplicitLod",
+ "ImageSparseSampleProjImplicitLod",
+ "ImageSparseSampleProjExplicitLod",
+ "ImageSparseSampleProjDrefImplicitLod",
+ "ImageSparseSampleProjDrefExplicitLod",
+ "ImageSparseFetch",
+ "ImageSparseGather",
+ "ImageSparseDrefGather",
+ "ImageSparseTexelsResident",
+ "NoLine",
+ "AtomicFlagTestAndSet",
+ "AtomicFlagClear",
+ "ImageSparseRead",
+ "SizeOf",
+ "TypePipeStorage",
+ "ConstantPipeStorage",
+ "CreatePipeFromPipeStorage",
+ "GetKernelLocalSizeForSubgroupCount",
+ "GetKernelMaxNumSubgroups",
+ "TypeNamedBarrier",
+ "NamedBarrierInitialize",
+ "MemoryNamedBarrier",
+ "ModuleProcessed",
+ "ExecutionModeId",
+ "DecorateId",
+ "GroupNonUniformElect",
+ "GroupNonUniformAll",
+ "GroupNonUniformAny",
+ "GroupNonUniformAllEqual",
+ "GroupNonUniformBroadcast",
+ "GroupNonUniformBroadcastFirst",
+ "GroupNonUniformBallot",
+ "GroupNonUniformInverseBallot",
+ "GroupNonUniformBallotBitExtract",
+ "GroupNonUniformBallotBitCount",
+ "GroupNonUniformBallotFindLSB",
+ "GroupNonUniformBallotFindMSB",
+ "GroupNonUniformShuffle",
+ "GroupNonUniformShuffleXor",
+ "GroupNonUniformShuffleUp",
+ "GroupNonUniformShuffleDown",
+ "GroupNonUniformIAdd",
+ "GroupNonUniformFAdd",
+ "GroupNonUniformIMul",
+ "GroupNonUniformFMul",
+ "GroupNonUniformSMin",
+ "GroupNonUniformUMin",
+ "GroupNonUniformFMin",
+ "GroupNonUniformSMax",
+ "GroupNonUniformUMax",
+ "GroupNonUniformFMax",
+ "GroupNonUniformBitwiseAnd",
+ "GroupNonUniformBitwiseOr",
+ "GroupNonUniformBitwiseXor",
+ "GroupNonUniformLogicalAnd",
+ "GroupNonUniformLogicalOr",
+ "GroupNonUniformLogicalXor",
+ "GroupNonUniformQuadBroadcast",
+ "GroupNonUniformQuadSwap",
+};
+static_assert(_SMOLV_ARRAY_SIZE(kSpirvOpNames) == kKnownOpsCount, "kSpirvOpNames table mismatch with known SpvOps");
+
+
+struct OpData
+{
+ uint8_t hasResult; // does it have result ID?
+ uint8_t hasType; // does it have type ID?
+ uint8_t deltaFromResult; // How many words after (optional) type+result to write out as deltas from result?
+ uint8_t varrest; // should the rest of words be written in varint encoding?
+};
+static const OpData kSpirvOpData[] =
+{
+ {0, 0, 0, 0}, // Nop
+ {1, 1, 0, 0}, // Undef
+ {0, 0, 0, 0}, // SourceContinued
+ {0, 0, 0, 1}, // Source
+ {0, 0, 0, 0}, // SourceExtension
+ {0, 0, 0, 0}, // Name
+ {0, 0, 0, 0}, // MemberName
+ {0, 0, 0, 0}, // String
+ {0, 0, 0, 1}, // Line
+ {1, 1, 0, 0}, // #9
+ {0, 0, 0, 0}, // Extension
+ {1, 0, 0, 0}, // ExtInstImport
+ {1, 1, 0, 1}, // ExtInst
+ {1, 1, 2, 1}, // VectorShuffleCompact - new in SMOLV
+ {0, 0, 0, 1}, // MemoryModel
+ {0, 0, 0, 1}, // EntryPoint
+ {0, 0, 0, 1}, // ExecutionMode
+ {0, 0, 0, 1}, // Capability
+ {1, 1, 0, 0}, // #18
+ {1, 0, 0, 1}, // TypeVoid
+ {1, 0, 0, 1}, // TypeBool
+ {1, 0, 0, 1}, // TypeInt
+ {1, 0, 0, 1}, // TypeFloat
+ {1, 0, 0, 1}, // TypeVector
+ {1, 0, 0, 1}, // TypeMatrix
+ {1, 0, 0, 1}, // TypeImage
+ {1, 0, 0, 1}, // TypeSampler
+ {1, 0, 0, 1}, // TypeSampledImage
+ {1, 0, 0, 1}, // TypeArray
+ {1, 0, 0, 1}, // TypeRuntimeArray
+ {1, 0, 0, 1}, // TypeStruct
+ {1, 0, 0, 1}, // TypeOpaque
+ {1, 0, 0, 1}, // TypePointer
+ {1, 0, 0, 1}, // TypeFunction
+ {1, 0, 0, 1}, // TypeEvent
+ {1, 0, 0, 1}, // TypeDeviceEvent
+ {1, 0, 0, 1}, // TypeReserveId
+ {1, 0, 0, 1}, // TypeQueue
+ {1, 0, 0, 1}, // TypePipe
+ {0, 0, 0, 1}, // TypeForwardPointer
+ {1, 1, 0, 0}, // #40
+ {1, 1, 0, 0}, // ConstantTrue
+ {1, 1, 0, 0}, // ConstantFalse
+ {1, 1, 0, 0}, // Constant
+ {1, 1, 9, 0}, // ConstantComposite
+ {1, 1, 0, 1}, // ConstantSampler
+ {1, 1, 0, 0}, // ConstantNull
+ {1, 1, 0, 0}, // #47
+ {1, 1, 0, 0}, // SpecConstantTrue
+ {1, 1, 0, 0}, // SpecConstantFalse
+ {1, 1, 0, 0}, // SpecConstant
+ {1, 1, 9, 0}, // SpecConstantComposite
+ {1, 1, 0, 0}, // SpecConstantOp
+ {1, 1, 0, 0}, // #53
+ {1, 1, 0, 1}, // Function
+ {1, 1, 0, 0}, // FunctionParameter
+ {0, 0, 0, 0}, // FunctionEnd
+ {1, 1, 9, 0}, // FunctionCall
+ {1, 1, 0, 0}, // #58
+ {1, 1, 0, 1}, // Variable
+ {1, 1, 0, 0}, // ImageTexelPointer
+ {1, 1, 1, 1}, // Load
+ {0, 0, 2, 1}, // Store
+ {0, 0, 0, 0}, // CopyMemory
+ {0, 0, 0, 0}, // CopyMemorySized
+ {1, 1, 0, 1}, // AccessChain
+ {1, 1, 0, 0}, // InBoundsAccessChain
+ {1, 1, 0, 0}, // PtrAccessChain
+ {1, 1, 0, 0}, // ArrayLength
+ {1, 1, 0, 0}, // GenericPtrMemSemantics
+ {1, 1, 0, 0}, // InBoundsPtrAccessChain
+ {0, 0, 0, 1}, // Decorate
+ {0, 0, 0, 1}, // MemberDecorate
+ {1, 0, 0, 0}, // DecorationGroup
+ {0, 0, 0, 0}, // GroupDecorate
+ {0, 0, 0, 0}, // GroupMemberDecorate
+ {1, 1, 0, 0}, // #76
+ {1, 1, 1, 1}, // VectorExtractDynamic
+ {1, 1, 2, 1}, // VectorInsertDynamic
+ {1, 1, 2, 1}, // VectorShuffle
+ {1, 1, 9, 0}, // CompositeConstruct
+ {1, 1, 1, 1}, // CompositeExtract
+ {1, 1, 2, 1}, // CompositeInsert
+ {1, 1, 1, 0}, // CopyObject
+ {1, 1, 0, 0}, // Transpose
+ {1, 1, 0, 0}, // #85
+ {1, 1, 0, 0}, // SampledImage
+ {1, 1, 2, 1}, // ImageSampleImplicitLod
+ {1, 1, 2, 1}, // ImageSampleExplicitLod
+ {1, 1, 3, 1}, // ImageSampleDrefImplicitLod
+ {1, 1, 3, 1}, // ImageSampleDrefExplicitLod
+ {1, 1, 2, 1}, // ImageSampleProjImplicitLod
+ {1, 1, 2, 1}, // ImageSampleProjExplicitLod
+ {1, 1, 3, 1}, // ImageSampleProjDrefImplicitLod
+ {1, 1, 3, 1}, // ImageSampleProjDrefExplicitLod
+ {1, 1, 2, 1}, // ImageFetch
+ {1, 1, 3, 1}, // ImageGather
+ {1, 1, 3, 1}, // ImageDrefGather
+ {1, 1, 2, 1}, // ImageRead
+ {0, 0, 3, 1}, // ImageWrite
+ {1, 1, 1, 0}, // Image
+ {1, 1, 1, 0}, // ImageQueryFormat
+ {1, 1, 1, 0}, // ImageQueryOrder
+ {1, 1, 2, 0}, // ImageQuerySizeLod
+ {1, 1, 1, 0}, // ImageQuerySize
+ {1, 1, 2, 0}, // ImageQueryLod
+ {1, 1, 1, 0}, // ImageQueryLevels
+ {1, 1, 1, 0}, // ImageQuerySamples
+ {1, 1, 0, 0}, // #108
+ {1, 1, 1, 0}, // ConvertFToU
+ {1, 1, 1, 0}, // ConvertFToS
+ {1, 1, 1, 0}, // ConvertSToF
+ {1, 1, 1, 0}, // ConvertUToF
+ {1, 1, 1, 0}, // UConvert
+ {1, 1, 1, 0}, // SConvert
+ {1, 1, 1, 0}, // FConvert
+ {1, 1, 1, 0}, // QuantizeToF16
+ {1, 1, 1, 0}, // ConvertPtrToU
+ {1, 1, 1, 0}, // SatConvertSToU
+ {1, 1, 1, 0}, // SatConvertUToS
+ {1, 1, 1, 0}, // ConvertUToPtr
+ {1, 1, 1, 0}, // PtrCastToGeneric
+ {1, 1, 1, 0}, // GenericCastToPtr
+ {1, 1, 1, 1}, // GenericCastToPtrExplicit
+ {1, 1, 1, 0}, // Bitcast
+ {1, 1, 0, 0}, // #125
+ {1, 1, 1, 0}, // SNegate
+ {1, 1, 1, 0}, // FNegate
+ {1, 1, 2, 0}, // IAdd
+ {1, 1, 2, 0}, // FAdd
+ {1, 1, 2, 0}, // ISub
+ {1, 1, 2, 0}, // FSub
+ {1, 1, 2, 0}, // IMul
+ {1, 1, 2, 0}, // FMul
+ {1, 1, 2, 0}, // UDiv
+ {1, 1, 2, 0}, // SDiv
+ {1, 1, 2, 0}, // FDiv
+ {1, 1, 2, 0}, // UMod
+ {1, 1, 2, 0}, // SRem
+ {1, 1, 2, 0}, // SMod
+ {1, 1, 2, 0}, // FRem
+ {1, 1, 2, 0}, // FMod
+ {1, 1, 2, 0}, // VectorTimesScalar
+ {1, 1, 2, 0}, // MatrixTimesScalar
+ {1, 1, 2, 0}, // VectorTimesMatrix
+ {1, 1, 2, 0}, // MatrixTimesVector
+ {1, 1, 2, 0}, // MatrixTimesMatrix
+ {1, 1, 2, 0}, // OuterProduct
+ {1, 1, 2, 0}, // Dot
+ {1, 1, 2, 0}, // IAddCarry
+ {1, 1, 2, 0}, // ISubBorrow
+ {1, 1, 2, 0}, // UMulExtended
+ {1, 1, 2, 0}, // SMulExtended
+ {1, 1, 0, 0}, // #153
+ {1, 1, 1, 0}, // Any
+ {1, 1, 1, 0}, // All
+ {1, 1, 1, 0}, // IsNan
+ {1, 1, 1, 0}, // IsInf
+ {1, 1, 1, 0}, // IsFinite
+ {1, 1, 1, 0}, // IsNormal
+ {1, 1, 1, 0}, // SignBitSet
+ {1, 1, 2, 0}, // LessOrGreater
+ {1, 1, 2, 0}, // Ordered
+ {1, 1, 2, 0}, // Unordered
+ {1, 1, 2, 0}, // LogicalEqual
+ {1, 1, 2, 0}, // LogicalNotEqual
+ {1, 1, 2, 0}, // LogicalOr
+ {1, 1, 2, 0}, // LogicalAnd
+ {1, 1, 1, 0}, // LogicalNot
+ {1, 1, 3, 0}, // Select
+ {1, 1, 2, 0}, // IEqual
+ {1, 1, 2, 0}, // INotEqual
+ {1, 1, 2, 0}, // UGreaterThan
+ {1, 1, 2, 0}, // SGreaterThan
+ {1, 1, 2, 0}, // UGreaterThanEqual
+ {1, 1, 2, 0}, // SGreaterThanEqual
+ {1, 1, 2, 0}, // ULessThan
+ {1, 1, 2, 0}, // SLessThan
+ {1, 1, 2, 0}, // ULessThanEqual
+ {1, 1, 2, 0}, // SLessThanEqual
+ {1, 1, 2, 0}, // FOrdEqual
+ {1, 1, 2, 0}, // FUnordEqual
+ {1, 1, 2, 0}, // FOrdNotEqual
+ {1, 1, 2, 0}, // FUnordNotEqual
+ {1, 1, 2, 0}, // FOrdLessThan
+ {1, 1, 2, 0}, // FUnordLessThan
+ {1, 1, 2, 0}, // FOrdGreaterThan
+ {1, 1, 2, 0}, // FUnordGreaterThan
+ {1, 1, 2, 0}, // FOrdLessThanEqual
+ {1, 1, 2, 0}, // FUnordLessThanEqual
+ {1, 1, 2, 0}, // FOrdGreaterThanEqual
+ {1, 1, 2, 0}, // FUnordGreaterThanEqual
+ {1, 1, 0, 0}, // #192
+ {1, 1, 0, 0}, // #193
+ {1, 1, 2, 0}, // ShiftRightLogical
+ {1, 1, 2, 0}, // ShiftRightArithmetic
+ {1, 1, 2, 0}, // ShiftLeftLogical
+ {1, 1, 2, 0}, // BitwiseOr
+ {1, 1, 2, 0}, // BitwiseXor
+ {1, 1, 2, 0}, // BitwiseAnd
+ {1, 1, 1, 0}, // Not
+ {1, 1, 4, 0}, // BitFieldInsert
+ {1, 1, 3, 0}, // BitFieldSExtract
+ {1, 1, 3, 0}, // BitFieldUExtract
+ {1, 1, 1, 0}, // BitReverse
+ {1, 1, 1, 0}, // BitCount
+ {1, 1, 0, 0}, // #206
+ {1, 1, 0, 0}, // DPdx
+ {1, 1, 0, 0}, // DPdy
+ {1, 1, 0, 0}, // Fwidth
+ {1, 1, 0, 0}, // DPdxFine
+ {1, 1, 0, 0}, // DPdyFine
+ {1, 1, 0, 0}, // FwidthFine
+ {1, 1, 0, 0}, // DPdxCoarse
+ {1, 1, 0, 0}, // DPdyCoarse
+ {1, 1, 0, 0}, // FwidthCoarse
+ {1, 1, 0, 0}, // #216
+ {1, 1, 0, 0}, // #217
+ {0, 0, 0, 0}, // EmitVertex
+ {0, 0, 0, 0}, // EndPrimitive
+ {0, 0, 0, 0}, // EmitStreamVertex
+ {0, 0, 0, 0}, // EndStreamPrimitive
+ {1, 1, 0, 0}, // #222
+ {1, 1, 0, 0}, // #223
+ {0, 0, 3, 0}, // ControlBarrier
+ {0, 0, 2, 0}, // MemoryBarrier
+ {1, 1, 0, 0}, // #226
+ {1, 1, 0, 0}, // AtomicLoad
+ {0, 0, 0, 0}, // AtomicStore
+ {1, 1, 0, 0}, // AtomicExchange
+ {1, 1, 0, 0}, // AtomicCompareExchange
+ {1, 1, 0, 0}, // AtomicCompareExchangeWeak
+ {1, 1, 0, 0}, // AtomicIIncrement
+ {1, 1, 0, 0}, // AtomicIDecrement
+ {1, 1, 0, 0}, // AtomicIAdd
+ {1, 1, 0, 0}, // AtomicISub
+ {1, 1, 0, 0}, // AtomicSMin
+ {1, 1, 0, 0}, // AtomicUMin
+ {1, 1, 0, 0}, // AtomicSMax
+ {1, 1, 0, 0}, // AtomicUMax
+ {1, 1, 0, 0}, // AtomicAnd
+ {1, 1, 0, 0}, // AtomicOr
+ {1, 1, 0, 0}, // AtomicXor
+ {1, 1, 0, 0}, // #243
+ {1, 1, 0, 0}, // #244
+ {1, 1, 0, 0}, // Phi
+ {0, 0, 2, 1}, // LoopMerge
+ {0, 0, 1, 1}, // SelectionMerge
+ {1, 0, 0, 0}, // Label
+ {0, 0, 1, 0}, // Branch
+ {0, 0, 3, 1}, // BranchConditional
+ {0, 0, 0, 0}, // Switch
+ {0, 0, 0, 0}, // Kill
+ {0, 0, 0, 0}, // Return
+ {0, 0, 0, 0}, // ReturnValue
+ {0, 0, 0, 0}, // Unreachable
+ {0, 0, 0, 0}, // LifetimeStart
+ {0, 0, 0, 0}, // LifetimeStop
+ {1, 1, 0, 0}, // #258
+ {1, 1, 0, 0}, // GroupAsyncCopy
+ {0, 0, 0, 0}, // GroupWaitEvents
+ {1, 1, 0, 0}, // GroupAll
+ {1, 1, 0, 0}, // GroupAny
+ {1, 1, 0, 0}, // GroupBroadcast
+ {1, 1, 0, 0}, // GroupIAdd
+ {1, 1, 0, 0}, // GroupFAdd
+ {1, 1, 0, 0}, // GroupFMin
+ {1, 1, 0, 0}, // GroupUMin
+ {1, 1, 0, 0}, // GroupSMin
+ {1, 1, 0, 0}, // GroupFMax
+ {1, 1, 0, 0}, // GroupUMax
+ {1, 1, 0, 0}, // GroupSMax
+ {1, 1, 0, 0}, // #272
+ {1, 1, 0, 0}, // #273
+ {1, 1, 0, 0}, // ReadPipe
+ {1, 1, 0, 0}, // WritePipe
+ {1, 1, 0, 0}, // ReservedReadPipe
+ {1, 1, 0, 0}, // ReservedWritePipe
+ {1, 1, 0, 0}, // ReserveReadPipePackets
+ {1, 1, 0, 0}, // ReserveWritePipePackets
+ {0, 0, 0, 0}, // CommitReadPipe
+ {0, 0, 0, 0}, // CommitWritePipe
+ {1, 1, 0, 0}, // IsValidReserveId
+ {1, 1, 0, 0}, // GetNumPipePackets
+ {1, 1, 0, 0}, // GetMaxPipePackets
+ {1, 1, 0, 0}, // GroupReserveReadPipePackets
+ {1, 1, 0, 0}, // GroupReserveWritePipePackets
+ {0, 0, 0, 0}, // GroupCommitReadPipe
+ {0, 0, 0, 0}, // GroupCommitWritePipe
+ {1, 1, 0, 0}, // #289
+ {1, 1, 0, 0}, // #290
+ {1, 1, 0, 0}, // EnqueueMarker
+ {1, 1, 0, 0}, // EnqueueKernel
+ {1, 1, 0, 0}, // GetKernelNDrangeSubGroupCount
+ {1, 1, 0, 0}, // GetKernelNDrangeMaxSubGroupSize
+ {1, 1, 0, 0}, // GetKernelWorkGroupSize
+ {1, 1, 0, 0}, // GetKernelPreferredWorkGroupSizeMultiple
+ {0, 0, 0, 0}, // RetainEvent
+ {0, 0, 0, 0}, // ReleaseEvent
+ {1, 1, 0, 0}, // CreateUserEvent
+ {1, 1, 0, 0}, // IsValidEvent
+ {0, 0, 0, 0}, // SetUserEventStatus
+ {0, 0, 0, 0}, // CaptureEventProfilingInfo
+ {1, 1, 0, 0}, // GetDefaultQueue
+ {1, 1, 0, 0}, // BuildNDRange
+ {1, 1, 2, 1}, // ImageSparseSampleImplicitLod
+ {1, 1, 2, 1}, // ImageSparseSampleExplicitLod
+ {1, 1, 3, 1}, // ImageSparseSampleDrefImplicitLod
+ {1, 1, 3, 1}, // ImageSparseSampleDrefExplicitLod
+ {1, 1, 2, 1}, // ImageSparseSampleProjImplicitLod
+ {1, 1, 2, 1}, // ImageSparseSampleProjExplicitLod
+ {1, 1, 3, 1}, // ImageSparseSampleProjDrefImplicitLod
+ {1, 1, 3, 1}, // ImageSparseSampleProjDrefExplicitLod
+ {1, 1, 2, 1}, // ImageSparseFetch
+ {1, 1, 3, 1}, // ImageSparseGather
+ {1, 1, 3, 1}, // ImageSparseDrefGather
+ {1, 1, 1, 0}, // ImageSparseTexelsResident
+ {0, 0, 0, 0}, // NoLine
+ {1, 1, 0, 0}, // AtomicFlagTestAndSet
+ {0, 0, 0, 0}, // AtomicFlagClear
+ {1, 1, 0, 0}, // ImageSparseRead
+ {1, 1, 0, 0}, // SizeOf
+ {1, 1, 0, 0}, // TypePipeStorage
+ {1, 1, 0, 0}, // ConstantPipeStorage
+ {1, 1, 0, 0}, // CreatePipeFromPipeStorage
+ {1, 1, 0, 0}, // GetKernelLocalSizeForSubgroupCount
+ {1, 1, 0, 0}, // GetKernelMaxNumSubgroups
+ {1, 1, 0, 0}, // TypeNamedBarrier
+ {1, 1, 0, 1}, // NamedBarrierInitialize
+ {0, 0, 2, 1}, // MemoryNamedBarrier
+ {1, 1, 0, 0}, // ModuleProcessed
+ {0, 0, 0, 1}, // ExecutionModeId
+ {0, 0, 0, 1}, // DecorateId
+ {1, 1, 1, 1}, // GroupNonUniformElect
+ {1, 1, 1, 1}, // GroupNonUniformAll
+ {1, 1, 1, 1}, // GroupNonUniformAny
+ {1, 1, 1, 1}, // GroupNonUniformAllEqual
+ {1, 1, 1, 1}, // GroupNonUniformBroadcast
+ {1, 1, 1, 1}, // GroupNonUniformBroadcastFirst
+ {1, 1, 1, 1}, // GroupNonUniformBallot
+ {1, 1, 1, 1}, // GroupNonUniformInverseBallot
+ {1, 1, 1, 1}, // GroupNonUniformBallotBitExtract
+ {1, 1, 1, 1}, // GroupNonUniformBallotBitCount
+ {1, 1, 1, 1}, // GroupNonUniformBallotFindLSB
+ {1, 1, 1, 1}, // GroupNonUniformBallotFindMSB
+ {1, 1, 1, 1}, // GroupNonUniformShuffle
+ {1, 1, 1, 1}, // GroupNonUniformShuffleXor
+ {1, 1, 1, 1}, // GroupNonUniformShuffleUp
+ {1, 1, 1, 1}, // GroupNonUniformShuffleDown
+ {1, 1, 1, 1}, // GroupNonUniformIAdd
+ {1, 1, 1, 1}, // GroupNonUniformFAdd
+ {1, 1, 1, 1}, // GroupNonUniformIMul
+ {1, 1, 1, 1}, // GroupNonUniformFMul
+ {1, 1, 1, 1}, // GroupNonUniformSMin
+ {1, 1, 1, 1}, // GroupNonUniformUMin
+ {1, 1, 1, 1}, // GroupNonUniformFMin
+ {1, 1, 1, 1}, // GroupNonUniformSMax
+ {1, 1, 1, 1}, // GroupNonUniformUMax
+ {1, 1, 1, 1}, // GroupNonUniformFMax
+ {1, 1, 1, 1}, // GroupNonUniformBitwiseAnd
+ {1, 1, 1, 1}, // GroupNonUniformBitwiseOr
+ {1, 1, 1, 1}, // GroupNonUniformBitwiseXor
+ {1, 1, 1, 1}, // GroupNonUniformLogicalAnd
+ {1, 1, 1, 1}, // GroupNonUniformLogicalOr
+ {1, 1, 1, 1}, // GroupNonUniformLogicalXor
+ {1, 1, 1, 1}, // GroupNonUniformQuadBroadcast
+ {1, 1, 1, 1}, // GroupNonUniformQuadSwap
+};
+static_assert(_SMOLV_ARRAY_SIZE(kSpirvOpData) == kKnownOpsCount, "kSpirvOpData table mismatch with known SpvOps");
+
+// Instruction encoding depends on the table that describes the various SPIR-V opcodes.
+// Whenever we change or expand the table, we need to bump up the SMOL-V version, and make
+// sure that we can still decode files encoded by an older version.
+static int smolv_GetKnownOpsCount(int version)
+{
+ if (version == 0)
+ return SpvOpModuleProcessed+1;
+ if (version == 1) // 2020 February, version 1 added ExecutionModeId..GroupNonUniformQuadSwap
+ return SpvOpGroupNonUniformQuadSwap+1;
+ return 0;
+}
+
+static bool smolv_OpHasResult(SpvOp op, int opsCount)
+{
+ if (op < 0 || op >= opsCount)
+ return false;
+ return kSpirvOpData[op].hasResult != 0;
+}
+
+static bool smolv_OpHasType(SpvOp op, int opsCount)
+{
+ if (op < 0 || op >= opsCount)
+ return false;
+ return kSpirvOpData[op].hasType != 0;
+}
+
+static int smolv_OpDeltaFromResult(SpvOp op, int opsCount)
+{
+ if (op < 0 || op >= opsCount)
+ return 0;
+ return kSpirvOpData[op].deltaFromResult;
+}
+
+static bool smolv_OpVarRest(SpvOp op, int opsCount)
+{
+ if (op < 0 || op >= opsCount)
+ return false;
+ return kSpirvOpData[op].varrest != 0;
+}
+
+static bool smolv_OpDebugInfo(SpvOp op, int opsCount)
+{
+ return
+ op == SpvOpSourceContinued ||
+ op == SpvOpSource ||
+ op == SpvOpSourceExtension ||
+ op == SpvOpName ||
+ op == SpvOpMemberName ||
+ op == SpvOpString ||
+ op == SpvOpLine ||
+ op == SpvOpNoLine ||
+ op == SpvOpModuleProcessed;
+}
+
+
+static int smolv_DecorationExtraOps(int dec)
+{
+ if (dec == 0 || (dec >= 2 && dec <= 5)) // RelaxedPrecision, Block..ColMajor
+ return 0;
+ if (dec >= 29 && dec <= 37) // Stream..XfbStride
+ return 1;
+ return -1; // unknown, encode length
+}
+
+
+// --------------------------------------------------------------------------------------------
+
+
+static bool smolv_CheckGenericHeader(const uint32_t* words, size_t wordCount, uint32_t expectedMagic, uint32_t versionMask)
+{
+ if (!words)
+ return false;
+ if (wordCount < 5)
+ return false;
+
+ uint32_t headerMagic = words[0];
+ if (headerMagic != expectedMagic)
+ return false;
+ uint32_t headerVersion = words[1] & versionMask;
+ if (headerVersion < 0x00010000 || headerVersion > 0x00010500)
+ return false; // only support 1.0 through 1.5
+
+ return true;
+}
+
+static const int kSpirVHeaderMagic = 0x07230203;
+static const int kSmolHeaderMagic = 0x534D4F4C; // "SMOL"
+
+static const int kSmolCurrEncodingVersion = 1;
+
+static bool smolv_CheckSpirVHeader(const uint32_t* words, size_t wordCount)
+{
+ //@TODO: if SPIR-V header magic was reversed, that means the file got written
+ // in a "big endian" order. Need to byteswap all words then.
+ return smolv_CheckGenericHeader(words, wordCount, kSpirVHeaderMagic, 0xFFFFFFFF);
+}
+static bool smolv_CheckSmolHeader(const uint8_t* bytes, size_t byteCount)
+{
+ if (!smolv_CheckGenericHeader((const uint32_t*)bytes, byteCount/4, kSmolHeaderMagic, 0x00FFFFFF))
+ return false;
+ if (byteCount < 24) // one more word past header to store decoded length
+ return false;
+ // SMOL-V version
+ int smolVersion = ((const uint32_t*)bytes)[1] >> 24;
+ if (smolVersion < 0 || smolVersion > kSmolCurrEncodingVersion)
+ return false;
+ return true;
+}
+
+
+static void smolv_Write4(smolv::ByteArray& arr, uint32_t v)
+{
+ arr.push_back(v & 0xFF);
+ arr.push_back((v >> 8) & 0xFF);
+ arr.push_back((v >> 16) & 0xFF);
+ arr.push_back(v >> 24);
+}
+
+static void smolv_Write4(uint8_t*& buf, uint32_t v)
+{
+ memcpy(buf, &v, 4);
+ buf += 4;
+}
+
+
+static bool smolv_Read4(const uint8_t*& data, const uint8_t* dataEnd, uint32_t& outv)
+{
+ if (data + 4 > dataEnd)
+ return false;
+ outv = (data[0]) | (data[1] << 8) | (data[2] << 16) | (data[3] << 24);
+ data += 4;
+ return true;
+}
+
+
+// --------------------------------------------------------------------------------------------
+
+// Variable-length integer encoding for unsigned integers. In each byte:
+// - highest bit set if more bytes follow, cleared if this is last byte.
+// - other 7 bits are the actual value payload.
+// Takes 1-5 bytes to encode an integer (values between 0 and 127 take one byte, etc.).
+
+static void smolv_WriteVarint(smolv::ByteArray& arr, uint32_t v)
+{
+ while (v > 127)
+ {
+ arr.push_back((v & 127) | 128);
+ v >>= 7;
+ }
+ arr.push_back(v & 127);
+}
+
+static bool smolv_ReadVarint(const uint8_t*& data, const uint8_t* dataEnd, uint32_t& outVal)
+{
+ uint32_t v = 0;
+ uint32_t shift = 0;
+ while (data < dataEnd)
+ {
+ uint8_t b = *data;
+ v |= (b & 127) << shift;
+ shift += 7;
+ data++;
+ if (!(b & 128))
+ break;
+ }
+ outVal = v;
+ return true; //@TODO: report failures
+}
+
+static uint32_t smolv_ZigEncode(int32_t i)
+{
+ return (uint32_t(i) << 1) ^ (i >> 31);
+}
+
+static int32_t smolv_ZigDecode(uint32_t u)
+{
+ return (u & 1) ? ((u >> 1) ^ ~0) : (u >> 1);
+}
+
+
+// Remap most common Op codes (Load, Store, Decorate, VectorShuffle etc.) to be in < 16 range, for
+// more compact varint encoding. This basically swaps rarely used op values that are < 16 with the
+// ones that are common.
+
+static SpvOp smolv_RemapOp(SpvOp op)
+{
+# define _SMOLV_SWAP_OP(op1,op2) if (op==op1) return op2; if (op==op2) return op1
+ _SMOLV_SWAP_OP(SpvOpDecorate,SpvOpNop); // 0: 24%
+ _SMOLV_SWAP_OP(SpvOpLoad,SpvOpUndef); // 1: 17%
+ _SMOLV_SWAP_OP(SpvOpStore,SpvOpSourceContinued); // 2: 9%
+ _SMOLV_SWAP_OP(SpvOpAccessChain,SpvOpSource); // 3: 7.2%
+ _SMOLV_SWAP_OP(SpvOpVectorShuffle,SpvOpSourceExtension); // 4: 5.0%
+ // Name - already small enum value - 5: 4.4%
+ // MemberName - already small enum value - 6: 2.9%
+ _SMOLV_SWAP_OP(SpvOpMemberDecorate,SpvOpString); // 7: 4.0%
+ _SMOLV_SWAP_OP(SpvOpLabel,SpvOpLine); // 8: 0.9%
+ _SMOLV_SWAP_OP(SpvOpVariable,(SpvOp)9); // 9: 3.9%
+ _SMOLV_SWAP_OP(SpvOpFMul,SpvOpExtension); // 10: 3.9%
+ _SMOLV_SWAP_OP(SpvOpFAdd,SpvOpExtInstImport); // 11: 2.5%
+ // ExtInst - already small enum value - 12: 1.2%
+ // VectorShuffleCompact - already small enum value - used for compact shuffle encoding
+ _SMOLV_SWAP_OP(SpvOpTypePointer,SpvOpMemoryModel); // 14: 2.2%
+ _SMOLV_SWAP_OP(SpvOpFNegate,SpvOpEntryPoint); // 15: 1.1%
+# undef _SMOLV_SWAP_OP
+ return op;
+}
+
+
+// For most compact varint encoding of common instructions, the instruction length should come out
+// into 3 bits (be <8). SPIR-V instruction lengths are always at least 1, and for some other
+// instructions they are guaranteed to be some other minimum length. Adjust the length before encoding,
+// and after decoding accordingly.
+
+static uint32_t smolv_EncodeLen(SpvOp op, uint32_t len)
+{
+ len--;
+ if (op == SpvOpVectorShuffle) len -= 4;
+ if (op == SpvOpVectorShuffleCompact) len -= 4;
+ if (op == SpvOpDecorate) len -= 2;
+ if (op == SpvOpLoad) len -= 3;
+ if (op == SpvOpAccessChain) len -= 3;
+ return len;
+}
+
+static uint32_t smolv_DecodeLen(SpvOp op, uint32_t len)
+{
+ len++;
+ if (op == SpvOpVectorShuffle) len += 4;
+ if (op == SpvOpVectorShuffleCompact) len += 4;
+ if (op == SpvOpDecorate) len += 2;
+ if (op == SpvOpLoad) len += 3;
+ if (op == SpvOpAccessChain) len += 3;
+ return len;
+}
+
+
+// Shuffling bits of length + opcode to be more compact in varint encoding in typical cases:
+// 0x LLLL OOOO is how SPIR-V encodes it (L=length, O=op), we shuffle into:
+// 0x LLLO OOLO, so that common case (op<16, len<8) is encoded into one byte.
+
+static bool smolv_WriteLengthOp(smolv::ByteArray& arr, uint32_t len, SpvOp op)
+{
+ len = smolv_EncodeLen(op, len);
+ // SPIR-V length field is 16 bits; if we get a larger value that means something
+ // was wrong, e.g. a vector shuffle instruction with less than 4 words (and our
+ // adjustment to common lengths in smolv_EncodeLen wrapped around)
+ if (len > 0xFFFF)
+ return false;
+ op = smolv_RemapOp(op);
+ uint32_t oplen = ((len >> 4) << 20) | ((op >> 4) << 8) | ((len & 0xF) << 4) | (op & 0xF);
+ smolv_WriteVarint(arr, oplen);
+ return true;
+}
+
+static bool smolv_ReadLengthOp(const uint8_t*& data, const uint8_t* dataEnd, uint32_t& outLen, SpvOp& outOp)
+{
+ uint32_t val;
+ if (!smolv_ReadVarint(data, dataEnd, val))
+ return false;
+ outLen = ((val >> 20) << 4) | ((val >> 4) & 0xF);
+ outOp = (SpvOp)(((val >> 4) & 0xFFF0) | (val & 0xF));
+
+ outOp = smolv_RemapOp(outOp);
+ outLen = smolv_DecodeLen(outOp, outLen);
+ return true;
+}
+
+
+
+#define _SMOLV_READ_OP(len, words, op) \
+ uint32_t len = words[0] >> 16; \
+ if (len < 1) return false; /* malformed instruction, length needs to be at least 1 */ \
+ if (words + len > wordsEnd) return false; /* malformed instruction, goes past end of data */ \
+ SpvOp op = (SpvOp)(words[0] & 0xFFFF)
+
+
+bool smolv::Encode(const void* spirvData, size_t spirvSize, ByteArray& outSmolv, uint32_t flags, StripOpNameFilterFunc stripFilter)
+{
+ const size_t wordCount = spirvSize / 4;
+ if (wordCount * 4 != spirvSize)
+ return false;
+ const uint32_t* words = (const uint32_t*)spirvData;
+ const uint32_t* wordsEnd = words + wordCount;
+ if (!smolv_CheckSpirVHeader(words, wordCount))
+ return false;
+
+ // reserve space in output (typical compression is to about 30%; reserve half of input space)
+ outSmolv.reserve(outSmolv.size() + spirvSize/2);
+
+ // header (matches SPIR-V one, except different magic)
+ smolv_Write4(outSmolv, kSmolHeaderMagic);
+ smolv_Write4(outSmolv, (words[1] & 0x00FFFFFF) + (kSmolCurrEncodingVersion<<24)); // SPIR-V version (_XXX) + SMOL-V version (X___)
+ smolv_Write4(outSmolv, words[2]); // generator
+ smolv_Write4(outSmolv, words[3]); // bound
+ smolv_Write4(outSmolv, words[4]); // schema
+
+ const size_t headerSpirvSizeOffset = outSmolv.size(); // size field may get updated later if stripping is enabled
+ smolv_Write4(outSmolv, (uint32_t)spirvSize); // space needed to decode (i.e. original SPIR-V size)
+
+ size_t strippedSpirvWordCount = wordCount;
+ uint32_t prevResult = 0;
+ uint32_t prevDecorate = 0;
+
+ const int knownOpsCount = smolv_GetKnownOpsCount(kSmolCurrEncodingVersion);
+
+ words += 5;
+ while (words < wordsEnd)
+ {
+ _SMOLV_READ_OP(instrLen, words, op);
+
+ if ((flags & kEncodeFlagStripDebugInfo) && smolv_OpDebugInfo(op, knownOpsCount))
+ {
+ if (!stripFilter || op != SpvOpName || !stripFilter(reinterpret_cast<const char*>(&words[2])))
+ {
+ strippedSpirvWordCount -= instrLen;
+ words += instrLen;
+ continue;
+ }
+ }
+
+ // A usual case of vector shuffle, with less than 4 components, each with a value
+ // in [0..3] range: encode it in a more compact form, with the swizzle pattern in one byte.
+ // Turn this into a VectorShuffleCompact instruction, that takes up unused slot in Ops.
+ uint32_t swizzle = 0;
+ if (op == SpvOpVectorShuffle && instrLen <= 9)
+ {
+ uint32_t swz0 = instrLen > 5 ? words[5] : 0;
+ uint32_t swz1 = instrLen > 6 ? words[6] : 0;
+ uint32_t swz2 = instrLen > 7 ? words[7] : 0;
+ uint32_t swz3 = instrLen > 8 ? words[8] : 0;
+ if (swz0 < 4 && swz1 < 4 && swz2 < 4 && swz3 < 4)
+ {
+ op = SpvOpVectorShuffleCompact;
+ swizzle = (swz0 << 6) | (swz1 << 4) | (swz2 << 2) | (swz3);
+ }
+ }
+
+ // length + opcode
+ if (!smolv_WriteLengthOp(outSmolv, instrLen, op))
+ return false;
+
+ size_t ioffs = 1;
+ // write type as varint, if we have it
+ if (smolv_OpHasType(op, knownOpsCount))
+ {
+ if (ioffs >= instrLen)
+ return false;
+ smolv_WriteVarint(outSmolv, words[ioffs]);
+ ioffs++;
+ }
+ // write result as delta+zig+varint, if we have it
+ if (smolv_OpHasResult(op, knownOpsCount))
+ {
+ if (ioffs >= instrLen)
+ return false;
+ uint32_t v = words[ioffs];
+ smolv_WriteVarint(outSmolv, smolv_ZigEncode(v - prevResult)); // some deltas are negative, use zig
+ prevResult = v;
+ ioffs++;
+ }
+
+ // Decorate & MemberDecorate: IDs relative to previous decorate
+ if (op == SpvOpDecorate || op == SpvOpMemberDecorate)
+ {
+ if (ioffs >= instrLen)
+ return false;
+ uint32_t v = words[ioffs];
+ smolv_WriteVarint(outSmolv, smolv_ZigEncode(v - prevDecorate)); // spirv-remapped deltas often negative, use zig
+ prevDecorate = v;
+ ioffs++;
+ }
+
+ // MemberDecorate special encoding: whole row of MemberDecorate instructions is often referring
+ // to the same type and linearly increasing member indices. Scan ahead to see how many we have,
+ // and encode whole bunch as one.
+ if (op == SpvOpMemberDecorate)
+ {
+ // scan ahead until we reach end, non-member-decoration or different type
+ const uint32_t decorationType = words[ioffs-1];
+ const uint32_t* memberWords = words;
+ uint32_t prevIndex = 0;
+ uint32_t prevOffset = 0;
+ // write a byte on how many we have encoded as a bunch
+ size_t countLocation = outSmolv.size();
+ outSmolv.push_back(0);
+ int count = 0;
+ while (memberWords < wordsEnd && count < 255)
+ {
+ _SMOLV_READ_OP(memberLen, memberWords, memberOp);
+ if (memberOp != SpvOpMemberDecorate)
+ break;
+ if (memberLen < 4)
+ return false; // invalid input
+ if (memberWords[1] != decorationType)
+ break;
+
+ // write member index as delta from previous
+ uint32_t memberIndex = memberWords[2];
+ smolv_WriteVarint(outSmolv, memberIndex - prevIndex);
+ prevIndex = memberIndex;
+
+ // decoration (and length if not common/known)
+ uint32_t memberDec = memberWords[3];
+ smolv_WriteVarint(outSmolv, memberDec);
+ const int knownExtraOps = smolv_DecorationExtraOps(memberDec);
+ if (knownExtraOps == -1)
+ smolv_WriteVarint(outSmolv, memberLen-4);
+ else if (unsigned(knownExtraOps) + 4 != memberLen)
+ return false; // invalid input
+
+ // Offset decorations are most often linearly increasing, so encode as deltas
+ if (memberDec == 35) // Offset
+ {
+ if (memberLen != 5)
+ return false;
+ smolv_WriteVarint(outSmolv, memberWords[4]-prevOffset);
+ prevOffset = memberWords[4];
+ }
+ else
+ {
+ // write rest of decorations as varint
+ for (uint32_t i = 4; i < memberLen; ++i)
+ smolv_WriteVarint(outSmolv, memberWords[i]);
+ }
+
+ memberWords += memberLen;
+ ++count;
+ }
+ outSmolv[countLocation] = uint8_t(count);
+ words = memberWords;
+ continue;
+ }
+
+ // Write out this many IDs, encoding them relative+zigzag to result ID
+ int relativeCount = smolv_OpDeltaFromResult(op, knownOpsCount);
+ for (int i = 0; i < relativeCount && ioffs < instrLen; ++i, ++ioffs)
+ {
+ if (ioffs >= instrLen)
+ return false;
+ uint32_t delta = prevResult - words[ioffs];
+ // some deltas are negative (often on branches, or if program was processed by spirv-remap),
+ // so use zig encoding
+ smolv_WriteVarint(outSmolv, smolv_ZigEncode(delta));
+ }
+
+ if (op == SpvOpVectorShuffleCompact)
+ {
+ // compact vector shuffle, just write out single swizzle byte
+ outSmolv.push_back(uint8_t(swizzle));
+ ioffs = instrLen;
+ }
+ else if (smolv_OpVarRest(op, knownOpsCount))
+ {
+ // write out rest of words with variable encoding (expected to be small integers)
+ for (; ioffs < instrLen; ++ioffs)
+ smolv_WriteVarint(outSmolv, words[ioffs]);
+ }
+ else
+ {
+ // write out rest of words without any encoding
+ for (; ioffs < instrLen; ++ioffs)
+ smolv_Write4(outSmolv, words[ioffs]);
+ }
+
+ words += instrLen;
+ }
+
+ if (strippedSpirvWordCount != wordCount)
+ {
+ uint8_t* headerSpirvSize = &outSmolv[headerSpirvSizeOffset];
+ smolv_Write4(headerSpirvSize, (uint32_t)strippedSpirvWordCount * 4);
+ }
+
+ return true;
+}
+
+
+size_t smolv::GetDecodedBufferSize(const void* smolvData, size_t smolvSize)
+{
+ if (!smolv_CheckSmolHeader((const uint8_t*)smolvData, smolvSize))
+ return 0;
+ const uint32_t* words = (const uint32_t*)smolvData;
+ return words[5];
+}
+
+
+bool smolv::Decode(const void* smolvData, size_t smolvSize, void* spirvOutputBuffer, size_t spirvOutputBufferSize, uint32_t flags)
+{
+ // check header, and whether we have enough output buffer space
+ const size_t neededBufferSize = GetDecodedBufferSize(smolvData, smolvSize);
+ if (neededBufferSize == 0)
+ return false; // invalid SMOL-V
+ if (spirvOutputBufferSize < neededBufferSize)
+ return false; // not enough space in output buffer
+ if (spirvOutputBuffer == NULL)
+ return false; // output buffer is null
+
+ const uint8_t* bytes = (const uint8_t*)smolvData;
+ const uint8_t* bytesEnd = bytes + smolvSize;
+
+ uint8_t* outSpirv = (uint8_t*)spirvOutputBuffer;
+
+ uint32_t val;
+ int smolVersion = 0;
+
+ // header
+ smolv_Write4(outSpirv, kSpirVHeaderMagic); bytes += 4;
+ smolv_Read4(bytes, bytesEnd, val); smolVersion = val >> 24; val &= 0x00FFFFFF; smolv_Write4(outSpirv, val); // version
+ smolv_Read4(bytes, bytesEnd, val); smolv_Write4(outSpirv, val); // generator
+ smolv_Read4(bytes, bytesEnd, val); smolv_Write4(outSpirv, val); // bound
+ smolv_Read4(bytes, bytesEnd, val); smolv_Write4(outSpirv, val); // schema
+ bytes += 4; // decode buffer size
+
+ // there are two SMOL-V encoding versions, both not indicating anything in their header version field:
+ // one that is called "before zero" here (2016-08-31 code). Support decoding that one only by presence
+ // of this special flag.
+ const bool beforeZeroVersion = smolVersion == 0 && (flags & kDecodeFlagUse20160831AsZeroVersion) != 0;
+
+ const int knownOpsCount = smolv_GetKnownOpsCount(smolVersion);
+
+ uint32_t prevResult = 0;
+ uint32_t prevDecorate = 0;
+
+ while (bytes < bytesEnd)
+ {
+ // read length + opcode
+ uint32_t instrLen;
+ SpvOp op;
+ if (!smolv_ReadLengthOp(bytes, bytesEnd, instrLen, op))
+ return false;
+ const bool wasSwizzle = (op == SpvOpVectorShuffleCompact);
+ if (wasSwizzle)
+ op = SpvOpVectorShuffle;
+ smolv_Write4(outSpirv, (instrLen << 16) | op);
+
+ size_t ioffs = 1;
+
+ // read type as varint, if we have it
+ if (smolv_OpHasType(op, knownOpsCount))
+ {
+ if (!smolv_ReadVarint(bytes, bytesEnd, val)) return false;
+ smolv_Write4(outSpirv, val);
+ ioffs++;
+ }
+ // read result as delta+varint, if we have it
+ if (smolv_OpHasResult(op, knownOpsCount))
+ {
+ if (!smolv_ReadVarint(bytes, bytesEnd, val)) return false;
+ val = prevResult + smolv_ZigDecode(val);
+ smolv_Write4(outSpirv, val);
+ prevResult = val;
+ ioffs++;
+ }
+
+ // Decorate: IDs relative to previous decorate
+ if (op == SpvOpDecorate || op == SpvOpMemberDecorate)
+ {
+ if (!smolv_ReadVarint(bytes, bytesEnd, val)) return false;
+ // "before zero" version did not use zig encoding for the value
+ val = prevDecorate + (beforeZeroVersion ? val : smolv_ZigDecode(val));
+ smolv_Write4(outSpirv, val);
+ prevDecorate = val;
+ ioffs++;
+ }
+
+ // MemberDecorate special decoding
+ if (op == SpvOpMemberDecorate && !beforeZeroVersion)
+ {
+ if (bytes >= bytesEnd)
+ return false; // broken input
+ int count = *bytes++;
+ int prevIndex = 0;
+ int prevOffset = 0;
+ for (int m = 0; m < count; ++m)
+ {
+ // read member index
+ uint32_t memberIndex;
+ if (!smolv_ReadVarint(bytes, bytesEnd, memberIndex)) return false;
+ memberIndex += prevIndex;
+ prevIndex = memberIndex;
+
+ // decoration (and length if not common/known)
+ uint32_t memberDec;
+ if (!smolv_ReadVarint(bytes, bytesEnd, memberDec)) return false;
+ const int knownExtraOps = smolv_DecorationExtraOps(memberDec);
+ uint32_t memberLen;
+ if (knownExtraOps == -1)
+ {
+ if (!smolv_ReadVarint(bytes, bytesEnd, memberLen)) return false;
+ memberLen += 4;
+ }
+ else
+ memberLen = 4 + knownExtraOps;
+
+ // write SPIR-V op+length (unless it's first member decoration, in which case it was written before)
+ if (m != 0)
+ {
+ smolv_Write4(outSpirv, (memberLen << 16) | op);
+ smolv_Write4(outSpirv, prevDecorate);
+ }
+ smolv_Write4(outSpirv, memberIndex);
+ smolv_Write4(outSpirv, memberDec);
+ // Special case for Offset decorations
+ if (memberDec == 35) // Offset
+ {
+ if (memberLen != 5)
+ return false;
+ if (!smolv_ReadVarint(bytes, bytesEnd, val)) return false;
+ val += prevOffset;
+ smolv_Write4(outSpirv, val);
+ prevOffset = val;
+ }
+ else
+ {
+ for (uint32_t i = 4; i < memberLen; ++i)
+ {
+ if (!smolv_ReadVarint(bytes, bytesEnd, val)) return false;
+ smolv_Write4(outSpirv, val);
+ }
+ }
+ }
+ continue;
+ }
+
+ // Read this many IDs, that are relative to result ID
+ int relativeCount = smolv_OpDeltaFromResult(op, knownOpsCount);
+ // "before zero" version only used zig encoding for IDs of several ops; after
+ // that ops got zig encoding for their IDs
+ bool zigDecodeVals = true;
+ if (beforeZeroVersion)
+ {
+ if (op != SpvOpControlBarrier && op != SpvOpMemoryBarrier && op != SpvOpLoopMerge && op != SpvOpSelectionMerge && op != SpvOpBranch && op != SpvOpBranchConditional && op != SpvOpMemoryNamedBarrier)
+ zigDecodeVals = false;
+ }
+ for (int i = 0; i < relativeCount && ioffs < instrLen; ++i, ++ioffs)
+ {
+ if (!smolv_ReadVarint(bytes, bytesEnd, val)) return false;
+ if (zigDecodeVals)
+ val = smolv_ZigDecode(val);
+ smolv_Write4(outSpirv, prevResult - val);
+ }
+
+ if (wasSwizzle && instrLen <= 9)
+ {
+ uint32_t swizzle = *bytes++;
+ if (instrLen > 5) smolv_Write4(outSpirv, (swizzle >> 6) & 3);
+ if (instrLen > 6) smolv_Write4(outSpirv, (swizzle >> 4) & 3);
+ if (instrLen > 7) smolv_Write4(outSpirv, (swizzle >> 2) & 3);
+ if (instrLen > 8) smolv_Write4(outSpirv, swizzle & 3);
+ }
+ else if (smolv_OpVarRest(op, knownOpsCount))
+ {
+ // read rest of words with variable encoding
+ for (; ioffs < instrLen; ++ioffs)
+ {
+ if (!smolv_ReadVarint(bytes, bytesEnd, val)) return false;
+ smolv_Write4(outSpirv, val);
+ }
+ }
+ else
+ {
+ // read rest of words without any encoding
+ for (; ioffs < instrLen; ++ioffs)
+ {
+ if (!smolv_Read4(bytes, bytesEnd, val)) return false;
+ smolv_Write4(outSpirv, val);
+ }
+ }
+ }
+
+ if ((uint8_t*)spirvOutputBuffer + neededBufferSize != outSpirv)
+ return false; // something went wrong during decoding? we should have decoded to exact output size
+
+ return true;
+}
+
+
+
+// --------------------------------------------------------------------------------------------
+// Calculating instruction count / space stats on SPIR-V and SMOL-V
+
+
+struct smolv::Stats
+{
+ Stats() { memset(this, 0, sizeof(*this)); }
+ size_t opCounts[kKnownOpsCount];
+ size_t opSizes[kKnownOpsCount];
+ size_t smolOpSizes[kKnownOpsCount];
+ size_t varintCountsOp[6];
+ size_t varintCountsType[6];
+ size_t varintCountsRes[6];
+ size_t varintCountsOther[6];
+ size_t totalOps;
+ size_t totalSize;
+ size_t totalSizeSmol;
+ size_t inputCount;
+};
+
+
+smolv::Stats* smolv::StatsCreate()
+{
+ return new Stats();
+}
+
+void smolv::StatsDelete(smolv::Stats *s)
+{
+ delete s;
+}
+
+
+bool smolv::StatsCalculate(smolv::Stats* stats, const void* spirvData, size_t spirvSize)
+{
+ if (!stats)
+ return false;
+
+ const size_t wordCount = spirvSize / 4;
+ if (wordCount * 4 != spirvSize)
+ return false;
+ const uint32_t* words = (const uint32_t*)spirvData;
+ const uint32_t* wordsEnd = words + wordCount;
+ if (!smolv_CheckSpirVHeader(words, wordCount))
+ return false;
+ words += 5;
+
+ stats->inputCount++;
+ stats->totalSize += wordCount;
+
+ while (words < wordsEnd)
+ {
+ _SMOLV_READ_OP(instrLen, words, op);
+
+ if (op < kKnownOpsCount)
+ {
+ stats->opCounts[op]++;
+ stats->opSizes[op] += instrLen;
+ }
+ words += instrLen;
+ stats->totalOps++;
+ }
+
+ return true;
+}
+
+
+bool smolv::StatsCalculateSmol(smolv::Stats* stats, const void* smolvData, size_t smolvSize)
+{
+ if (!stats)
+ return false;
+
+ // debugging helper to dump all encoded bytes to stdout, keep at "if 0"
+# if 0
+# define _SMOLV_DEBUG_PRINT_ENCODED_BYTES() { \
+ printf("Op %-22s ", op < kKnownOpsCount ? kSpirvOpNames[op] : "???"); \
+ for (const uint8_t* b = instrBegin; b < bytes; ++b) \
+ printf("%02x ", *b); \
+ printf("\n"); \
+ }
+# else
+# define _SMOLV_DEBUG_PRINT_ENCODED_BYTES() {}
+# endif
+
+ const uint8_t* bytes = (const uint8_t*)smolvData;
+ const uint8_t* bytesEnd = bytes + smolvSize;
+ if (!smolv_CheckSmolHeader(bytes, smolvSize))
+ return false;
+
+ uint32_t val;
+ int smolVersion;
+ bytes += 4;
+ smolv_Read4(bytes, bytesEnd, val); smolVersion = val >> 24;
+ const int knownOpsCount = smolv_GetKnownOpsCount(smolVersion);
+ bytes += 16;
+
+ stats->totalSizeSmol += smolvSize;
+
+ while (bytes < bytesEnd)
+ {
+ const uint8_t* instrBegin = bytes;
+ const uint8_t* varBegin;
+
+ // read length + opcode
+ uint32_t instrLen;
+ SpvOp op;
+ varBegin = bytes;
+ if (!smolv_ReadLengthOp(bytes, bytesEnd, instrLen, op))
+ return false;
+ const bool wasSwizzle = (op == SpvOpVectorShuffleCompact);
+ if (wasSwizzle)
+ op = SpvOpVectorShuffle;
+ stats->varintCountsOp[bytes-varBegin]++;
+
+ size_t ioffs = 1;
+ if (smolv_OpHasType(op, knownOpsCount))
+ {
+ varBegin = bytes;
+ if (!smolv_ReadVarint(bytes, bytesEnd, val)) return false;
+ stats->varintCountsType[bytes-varBegin]++;
+ ioffs++;
+ }
+ if (smolv_OpHasResult(op, knownOpsCount))
+ {
+ varBegin = bytes;
+ if (!smolv_ReadVarint(bytes, bytesEnd, val)) return false;
+ stats->varintCountsRes[bytes-varBegin]++;
+ ioffs++;
+ }
+
+ if (op == SpvOpDecorate || op == SpvOpMemberDecorate)
+ {
+ if (!smolv_ReadVarint(bytes, bytesEnd, val)) return false;
+ ioffs++;
+ }
+ // MemberDecorate special decoding
+ if (op == SpvOpMemberDecorate)
+ {
+ if (bytes >= bytesEnd)
+ return false; // broken input
+ int count = *bytes++;
+ for (int m = 0; m < count; ++m)
+ {
+ uint32_t memberIndex;
+ if (!smolv_ReadVarint(bytes, bytesEnd, memberIndex)) return false;
+ uint32_t memberDec;
+ if (!smolv_ReadVarint(bytes, bytesEnd, memberDec)) return false;
+ const int knownExtraOps = smolv_DecorationExtraOps(memberDec);
+ uint32_t memberLen;
+ if (knownExtraOps == -1)
+ {
+ if (!smolv_ReadVarint(bytes, bytesEnd, memberLen)) return false;
+ memberLen += 4;
+ }
+ else
+ memberLen = 4 + knownExtraOps;
+ for (uint32_t i = 4; i < memberLen; ++i)
+ {
+ if (!smolv_ReadVarint(bytes, bytesEnd, val)) return false;
+ }
+ }
+ stats->smolOpSizes[op] += bytes - instrBegin;
+ _SMOLV_DEBUG_PRINT_ENCODED_BYTES();
+ continue;
+ }
+
+ int relativeCount = smolv_OpDeltaFromResult(op, knownOpsCount);
+ for (int i = 0; i < relativeCount && ioffs < instrLen; ++i, ++ioffs)
+ {
+ varBegin = bytes;
+ if (!smolv_ReadVarint(bytes, bytesEnd, val)) return false;
+ stats->varintCountsRes[bytes-varBegin]++;
+ }
+
+ if (wasSwizzle && instrLen <= 9)
+ {
+ bytes++;
+ }
+ else if (smolv_OpVarRest(op, knownOpsCount))
+ {
+ for (; ioffs < instrLen; ++ioffs)
+ {
+ varBegin = bytes;
+ if (!smolv_ReadVarint(bytes, bytesEnd, val)) return false;
+ stats->varintCountsOther[bytes-varBegin]++;
+ }
+ }
+ else
+ {
+ for (; ioffs < instrLen; ++ioffs)
+ {
+ if (!smolv_Read4(bytes, bytesEnd, val)) return false;
+ }
+ }
+
+ if (op < kKnownOpsCount)
+ {
+ stats->smolOpSizes[op] += bytes - instrBegin;
+ }
+ _SMOLV_DEBUG_PRINT_ENCODED_BYTES();
+ }
+
+ return true;
+}
+
+static bool CompareOpCounters (std::pair<SpvOp,size_t> a, std::pair<SpvOp,size_t> b)
+{
+ return a.second > b.second;
+}
+
+void smolv::StatsPrint(const Stats* stats)
+{
+ if (!stats)
+ return;
+
+ typedef std::pair<SpvOp,size_t> OpCounter;
+ OpCounter counts[kKnownOpsCount];
+ OpCounter sizes[kKnownOpsCount];
+ OpCounter sizesSmol[kKnownOpsCount];
+ for (int i = 0; i < kKnownOpsCount; ++i)
+ {
+ counts[i].first = (SpvOp)i;
+ counts[i].second = stats->opCounts[i];
+ sizes[i].first = (SpvOp)i;
+ sizes[i].second = stats->opSizes[i];
+ sizesSmol[i].first = (SpvOp)i;
+ sizesSmol[i].second = stats->smolOpSizes[i];
+ }
+ std::sort(counts, counts + kKnownOpsCount, CompareOpCounters);
+ std::sort(sizes, sizes + kKnownOpsCount, CompareOpCounters);
+ std::sort(sizesSmol, sizesSmol + kKnownOpsCount, CompareOpCounters);
+
+ printf("Stats for %i SPIR-V inputs, total size %i words (%.1fKB):\n", (int)stats->inputCount, (int)stats->totalSize, stats->totalSize * 4.0f / 1024.0f);
+ printf("Most occuring ops:\n");
+ for (int i = 0; i < 30; ++i)
+ {
+ SpvOp op = counts[i].first;
+ printf(" #%2i: %4i %-20s %4i (%4.1f%%)\n", i, op, kSpirvOpNames[op], (int)counts[i].second, (float)counts[i].second / (float)stats->totalOps * 100.0f);
+ }
+ printf("Largest total size of ops:\n");
+ for (int i = 0; i < 30; ++i)
+ {
+ SpvOp op = sizes[i].first;
+ printf(" #%2i: %-22s %6i (%4.1f%%) avg len %.1f\n",
+ i,
+ kSpirvOpNames[op],
+ (int)sizes[i].second*4,
+ (float)sizes[i].second / (float)stats->totalSize * 100.0f,
+ (float)sizes[i].second*4 / (float)stats->opCounts[op]
+ );
+ }
+ printf("SMOL varint encoding counts per byte length:\n");
+ printf(" B: %6s %6s %6s %6s\n", "Op", "Type", "Result", "Other");
+ for (int i = 1; i < 6; ++i)
+ {
+ printf(" %i: %6i %6i %6i %6i\n", i, (int)stats->varintCountsOp[i], (int)stats->varintCountsType[i], (int)stats->varintCountsRes[i], (int)stats->varintCountsOther[i]);
+ }
+ printf("Largest total size of ops in SMOL:\n");
+ for (int i = 0; i < 30; ++i)
+ {
+ SpvOp op = sizesSmol[i].first;
+ printf(" #%2i: %-22s %6i (%4.1f%%) avg len %.1f\n",
+ i,
+ kSpirvOpNames[op],
+ (int)sizesSmol[i].second,
+ (float)sizesSmol[i].second / (float)stats->totalSizeSmol * 100.0f,
+ (float)sizesSmol[i].second / (float)stats->opCounts[op]
+ );
+ }
+}
+
+
+// ------------------------------------------------------------------------------
+// This software is available under 2 licenses -- choose whichever you prefer.
+// ------------------------------------------------------------------------------
+// ALTERNATIVE A - MIT License
+// Copyright (c) 2016-2020 Aras Pranckevicius
+// Permission is hereby granted, free of charge, to any person obtaining a copy of
+// this software and associated documentation files (the "Software"), to deal in
+// the Software without restriction, including without limitation the rights to
+// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+// of the Software, and to permit persons to whom the Software is furnished to do
+// so, subject to the following conditions:
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+// SOFTWARE.
+// ------------------------------------------------------------------------------
+// ALTERNATIVE B - Public Domain (www.unlicense.org)
+// This is free and unencumbered software released into the public domain.
+// Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
+// software, either in source code form or as a compiled binary, for any purpose,
+// commercial or non-commercial, and by any means.
+// In jurisdictions that recognize copyright laws, the author or authors of this
+// software dedicate any and all copyright interest in the software to the public
+// domain. We make this dedication for the benefit of the public at large and to
+// the detriment of our heirs and successors. We intend this dedication to be an
+// overt act of relinquishment in perpetuity of all present and future rights to
+// this software under copyright law.
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+// ------------------------------------------------------------------------------
diff --git a/thirdparty/misc/smolv.h b/thirdparty/misc/smolv.h
new file mode 100644
index 0000000000..798ee4126f
--- /dev/null
+++ b/thirdparty/misc/smolv.h
@@ -0,0 +1,169 @@
+// smol-v - public domain - https://github.com/aras-p/smol-v
+// authored 2016-2020 by Aras Pranckevicius
+// no warranty implied; use at your own risk
+// See end of file for license information.
+//
+//
+// ### OVERVIEW:
+//
+// SMOL-V encodes Vulkan/Khronos SPIR-V format programs into a form that is smaller, and is more
+// compressible. Normally no changes to the programs are done; they decode
+// into exactly same program as what was encoded. Optionally, debug information
+// can be removed too.
+//
+// SPIR-V is a very verbose format, several times larger than same programs expressed in other
+// shader formats (e.g. DX11 bytecode, GLSL, DX9 bytecode etc.). The SSA-form with ever increasing
+// IDs is not very appreciated by regular data compressors either. SMOL-V does several things
+// to improve this:
+// - Many words, especially ones that most often have small values, are encoded using
+// "varint" scheme (1-5 bytes per word, with just one byte for values in 0..127 range).
+// See https://developers.google.com/protocol-buffers/docs/encoding
+// - Some IDs used in the program are delta-encoded, relative to previously seen IDs (e.g. Result
+// IDs). Often instructions reference things that were computed just before, so this results in
+// small deltas. These values are also encoded using "varint" scheme.
+// - Reordering instruction opcodes so that the most common ones are the smallest values, for smaller
+// varint encoding.
+// - Encoding several instructions in a more compact form, e.g. the "typical <=4 component swizzle"
+// shape of a VectorShuffle instruction, or sequences of MemberDecorate instructions.
+//
+// A somewhat similar utility is spirv-remap from glslang, see
+// https://github.com/KhronosGroup/glslang/blob/master/README-spirv-remap.txt
+//
+//
+// ### USAGE:
+//
+// Add source/smolv.h and source/smolv.cpp to your C++ project build.
+// Currently it might require C++11 or somesuch; I only tested with Visual Studio 2017/2019, Mac Xcode 11 and Gcc 5.4.
+//
+// smolv::Encode and smolv::Decode is the basic functionality.
+//
+// Other functions are for development/statistics purposes, to figure out frequencies and
+// distributions of the instructions.
+//
+// There's a test + compression benchmarking suite in testing/testmain.cpp; using that needs adding
+// other files under testing/external to the build too (3rd party code: glslang remapper, Zstd, LZ4).
+//
+//
+// ### LIMITATIONS / TODO:
+//
+// - SPIR-V where the words got stored in big-endian layout is not supported yet.
+// - The whole thing might not work on Big-Endian CPUs. It might, but I'm not 100% sure.
+// - Not much prevention is done against malformed/corrupted inputs, TODO.
+// - Out of memory cases are not handled. The code will either throw exception
+// or crash, depending on your compilation flags.
+
+#pragma once
+
+#include <stdint.h>
+#include <vector>
+#include <cstddef>
+
+namespace smolv
+{
+ typedef std::vector<uint8_t> ByteArray;
+
+ enum EncodeFlags
+ {
+ kEncodeFlagNone = 0,
+ kEncodeFlagStripDebugInfo = (1<<0), // Strip all optional SPIR-V instructions (debug names etc.)
+ };
+ enum DecodeFlags
+ {
+ kDecodeFlagNone = 0,
+ kDecodeFlagUse20160831AsZeroVersion = (1 << 0), // For "version zero" of SMOL-V encoding, use 2016 08 31 code path (this is what happens to be used by Unity 2017-2020)
+ };
+
+ // Preserve *some* OpName debug names.
+ // Return true to preserve, false to strip.
+ // This is really only used to implement a workaround for problems with some Vulkan drivers.
+ typedef bool(*StripOpNameFilterFunc)(const char* name);
+
+ // -------------------------------------------------------------------
+ // Encoding / Decoding
+
+ // Encode SPIR-V into SMOL-V.
+ //
+ // Resulting data is appended to outSmolv array (the array is not cleared).
+ //
+ // flags is bitset of EncodeFlags values.
+ //
+ // Returns false on malformed SPIR-V input; if that happens the output array might get
+ // partial/broken SMOL-V program.
+ bool Encode(const void* spirvData, size_t spirvSize, ByteArray& outSmolv, uint32_t flags = kEncodeFlagNone, StripOpNameFilterFunc stripFilter = 0);
+
+
+ // Decode SMOL-V into SPIR-V.
+ //
+ // Resulting data is written into the passed buffer. Get required buffer space with
+ // GetDecodeBufferSize; this is the size of decoded SPIR-V program.
+ //
+ // flags is bitset of DecodeFlags values.
+
+ // Decoding does no memory allocations.
+ //
+ // Returns false on malformed input; if that happens the output buffer might be only partially
+ // written to.
+ bool Decode(const void* smolvData, size_t smolvSize, void* spirvOutputBuffer, size_t spirvOutputBufferSize, uint32_t flags = kDecodeFlagNone);
+
+
+ // Given a SMOL-V program, get size of the decoded SPIR-V program.
+ // This is the buffer size that Decode expects.
+ //
+ // Returns zero on malformed input (just checks the header, not the full input).
+ size_t GetDecodedBufferSize(const void* smolvData, size_t smolvSize);
+
+
+ // -------------------------------------------------------------------
+ // Computing instruction statistics on SPIR-V/SMOL-V programs
+
+ struct Stats;
+
+ Stats* StatsCreate();
+ void StatsDelete(Stats* s);
+
+ bool StatsCalculate(Stats* stats, const void* spirvData, size_t spirvSize);
+ bool StatsCalculateSmol(Stats* stats, const void* smolvData, size_t smolvSize);
+ void StatsPrint(const Stats* stats);
+
+} // namespace smolv
+
+
+// ------------------------------------------------------------------------------
+// This software is available under 2 licenses -- choose whichever you prefer.
+// ------------------------------------------------------------------------------
+// ALTERNATIVE A - MIT License
+// Copyright (c) 2016-2020 Aras Pranckevicius
+// Permission is hereby granted, free of charge, to any person obtaining a copy of
+// this software and associated documentation files (the "Software"), to deal in
+// the Software without restriction, including without limitation the rights to
+// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+// of the Software, and to permit persons to whom the Software is furnished to do
+// so, subject to the following conditions:
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+// SOFTWARE.
+// ------------------------------------------------------------------------------
+// ALTERNATIVE B - Public Domain (www.unlicense.org)
+// This is free and unencumbered software released into the public domain.
+// Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
+// software, either in source code form or as a compiled binary, for any purpose,
+// commercial or non-commercial, and by any means.
+// In jurisdictions that recognize copyright laws, the author or authors of this
+// software dedicate any and all copyright interest in the software to the public
+// domain. We make this dedication for the benefit of the public at large and to
+// the detriment of our heirs and successors. We intend this dedication to be an
+// overt act of relinquishment in perpetuity of all present and future rights to
+// this software under copyright law.
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+// ------------------------------------------------------------------------------