summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSpartan322 <Megacake1234@gmail.com>2024-11-06 01:07:22 -0500
committerSpartan322 <Megacake1234@gmail.com>2024-11-06 01:12:36 -0500
commitb8b8a7127e3040582c5937fd2dff4c99e94a3b57 (patch)
tree849c2d29b6fb90100f31de20340d5f0ef4238edf
parentd14f2a31558686f00c4f351ea659918576711a7a (diff)
parent87318a2fb7fffeb72adca934e31915be077c3d1f (diff)
downloadredot-engine-b8b8a7127e3040582c5937fd2dff4c99e94a3b57.tar.gz
Merge commit godotengine/godot@87318a2fb7fffeb72adca934e31915be077c3d1f
-rw-r--r--.clang-tidy21
-rw-r--r--.git-blame-ignore-revs3
-rw-r--r--.github/workflows/linux_builds.yml2
-rw-r--r--.pre-commit-config.yaml8
-rw-r--r--COPYRIGHT.txt2
-rw-r--r--core/input/input.cpp2
-rw-r--r--core/object/message_queue.h2
-rw-r--r--core/typedefs.h2
-rw-r--r--core/variant/variant.cpp12
-rw-r--r--core/variant/variant.h3
-rw-r--r--doc/classes/CameraFeed.xml18
-rw-r--r--doc/classes/DisplayServer.xml2
-rw-r--r--doc/classes/RenderingServer.xml8
-rw-r--r--doc/classes/SpringArm3D.xml1
-rw-r--r--drivers/d3d12/rendering_device_driver_d3d12.cpp2
-rw-r--r--drivers/gles3/effects/feed_effects.cpp130
-rw-r--r--drivers/gles3/effects/feed_effects.h70
-rw-r--r--drivers/gles3/rasterizer_gles3.cpp2
-rw-r--r--drivers/gles3/rasterizer_gles3.h2
-rw-r--r--drivers/gles3/rasterizer_scene_gles3.cpp39
-rw-r--r--drivers/gles3/rasterizer_scene_gles3.h1
-rw-r--r--drivers/gles3/shaders/SCsub1
-rw-r--r--drivers/gles3/shaders/feed.glsl39
-rw-r--r--drivers/gles3/storage/particles_storage.h2
-rw-r--r--drivers/png/SCsub16
-rw-r--r--drivers/vulkan/rendering_device_driver_vulkan.cpp31
-rw-r--r--drivers/vulkan/rendering_device_driver_vulkan.h4
-rw-r--r--drivers/windows/dir_access_windows.cpp2
-rw-r--r--drivers/windows/file_access_windows.cpp2
-rw-r--r--drivers/windows/file_access_windows_pipe.cpp20
-rw-r--r--drivers/windows/file_access_windows_pipe.h2
-rw-r--r--editor/animation_track_editor.cpp3
-rw-r--r--editor/editor_audio_buses.cpp20
-rw-r--r--editor/editor_file_system.cpp4
-rw-r--r--editor/editor_node.h2
-rw-r--r--editor/gui/editor_quick_open_dialog.cpp3
-rw-r--r--editor/icons/MaterialPreviewQuad.svg1
-rw-r--r--editor/import/3d/resource_importer_obj.cpp4
-rw-r--r--editor/import/3d/resource_importer_scene.cpp4
-rw-r--r--editor/plugins/bone_map_editor_plugin.cpp4
-rw-r--r--editor/plugins/font_config_plugin.cpp2
-rw-r--r--editor/plugins/material_editor_plugin.cpp93
-rw-r--r--editor/plugins/material_editor_plugin.h7
-rw-r--r--editor/plugins/tiles/tile_map_layer_editor.cpp2
-rw-r--r--editor/plugins/tiles/tile_set_atlas_source_editor.h2
-rw-r--r--editor/project_converter_3_to_4.cpp6
-rw-r--r--editor/themes/editor_color_map.cpp2
-rw-r--r--methods.py57
-rw-r--r--modules/betsy/image_compress_betsy.h4
-rw-r--r--modules/camera/camera_feed_linux.cpp2
-rw-r--r--modules/fbx/fbx_document.cpp18
-rw-r--r--modules/gdscript/gdscript_disassembler.cpp6
-rw-r--r--modules/gltf/config.py1
-rw-r--r--modules/gltf/doc_classes/GLTFAccessor.xml38
-rw-r--r--modules/gltf/doc_classes/GLTFDocument.xml18
-rw-r--r--modules/gltf/doc_classes/GLTFDocumentExtension.xml25
-rw-r--r--modules/gltf/doc_classes/GLTFNode.xml9
-rw-r--r--modules/gltf/doc_classes/GLTFObjectModelProperty.xml114
-rw-r--r--modules/gltf/editor/editor_scene_importer_blend.cpp2
-rw-r--r--modules/gltf/extensions/gltf_document_extension.cpp18
-rw-r--r--modules/gltf/extensions/gltf_document_extension.h4
-rw-r--r--modules/gltf/extensions/gltf_light.cpp16
-rw-r--r--modules/gltf/extensions/gltf_light.h3
-rw-r--r--modules/gltf/extensions/physics/gltf_document_extension_physics.cpp278
-rw-r--r--modules/gltf/extensions/physics/gltf_document_extension_physics.h3
-rw-r--r--modules/gltf/extensions/physics/gltf_physics_body.cpp3
-rw-r--r--modules/gltf/gltf_defines.h1
-rw-r--r--modules/gltf/gltf_document.cpp2309
-rw-r--r--modules/gltf/gltf_document.h53
-rw-r--r--modules/gltf/gltf_state.cpp24
-rw-r--r--modules/gltf/gltf_state.h13
-rw-r--r--modules/gltf/register_types.cpp2
-rw-r--r--modules/gltf/structures/gltf_accessor.cpp17
-rw-r--r--modules/gltf/structures/gltf_accessor.h20
-rw-r--r--modules/gltf/structures/gltf_animation.cpp40
-rw-r--r--modules/gltf/structures/gltf_animation.h26
-rw-r--r--modules/gltf/structures/gltf_camera.cpp16
-rw-r--r--modules/gltf/structures/gltf_camera.h3
-rw-r--r--modules/gltf/structures/gltf_node.cpp45
-rw-r--r--modules/gltf/structures/gltf_node.h3
-rw-r--r--modules/gltf/structures/gltf_object_model_property.cpp175
-rw-r--r--modules/gltf/structures/gltf_object_model_property.h106
-rw-r--r--modules/godot_physics_2d/godot_joints_2d.cpp2
-rw-r--r--modules/godot_physics_2d/godot_physics_server_2d.cpp4
-rw-r--r--modules/godot_physics_2d/godot_space_2d.cpp4
-rw-r--r--modules/godot_physics_3d/godot_collision_solver_3d_sat.cpp11
-rw-r--r--modules/godot_physics_3d/godot_physics_server_3d.cpp2
-rw-r--r--modules/godot_physics_3d/godot_shape_3d.cpp3
-rw-r--r--modules/godot_physics_3d/godot_space_3d.cpp4
-rw-r--r--modules/godot_physics_3d/joints/godot_generic_6dof_joint_3d.cpp2
-rw-r--r--modules/meshoptimizer/register_types.cpp4
-rw-r--r--modules/navigation/nav_agent.h2
-rw-r--r--modules/navigation/nav_map.cpp61
-rw-r--r--modules/openxr/doc_classes/OpenXRExtensionWrapperExtension.xml15
-rw-r--r--modules/openxr/extensions/openxr_composition_layer_extension.cpp2
-rw-r--r--modules/openxr/extensions/openxr_debug_utils_extension.cpp4
-rw-r--r--modules/openxr/extensions/openxr_extension_wrapper_extension.cpp13
-rw-r--r--modules/openxr/extensions/openxr_extension_wrapper_extension.h4
-rw-r--r--modules/openxr/extensions/platform/openxr_opengl_extension.cpp2
-rw-r--r--modules/openxr/openxr_api.cpp3
-rw-r--r--modules/text_server_adv/text_server_adv.cpp62
-rw-r--r--modules/text_server_fb/text_server_fb.cpp62
-rw-r--r--modules/theora/video_stream_theora.cpp2
-rw-r--r--modules/webp/webp_common.cpp2
-rw-r--r--platform/android/display_server_android.cpp14
-rw-r--r--platform/android/display_server_android.h3
-rw-r--r--platform/android/export/export.cpp4
-rw-r--r--platform/android/export/export_plugin.cpp16
-rw-r--r--platform/android/java/editor/src/main/AndroidManifest.xml1
-rw-r--r--platform/android/java/editor/src/main/java/org/godotengine/editor/BaseGodotEditor.kt20
-rw-r--r--platform/android/java/editor/src/main/res/values/strings.xml1
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/Godot.kt6
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/GodotIO.java22
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/utils/PermissionsUtil.java16
-rw-r--r--platform/android/java/nativeSrcsConfigs/CMakeLists.txt2
-rw-r--r--platform/android/java_godot_io_wrapper.cpp11
-rw-r--r--platform/android/java_godot_io_wrapper.h2
-rw-r--r--platform/android/java_godot_wrapper.cpp18
-rw-r--r--platform/android/java_godot_wrapper.h3
-rw-r--r--platform/linuxbsd/os_linuxbsd.cpp8
-rw-r--r--platform/windows/SCsub1
-rw-r--r--platform/windows/crash_handler_windows_signal.cpp2
-rw-r--r--platform/windows/display_server_windows.cpp62
-rw-r--r--platform/windows/display_server_windows.h7
-rw-r--r--platform/windows/drop_target_windows.cpp377
-rw-r--r--platform/windows/drop_target_windows.h79
-rw-r--r--platform/windows/joypad_windows.cpp3
-rw-r--r--platform/windows/native_menu_windows.cpp6
-rw-r--r--platform/windows/os_windows.cpp14
-rw-r--r--platform/windows/windows_utils.cpp4
-rw-r--r--scene/2d/animated_sprite_2d.cpp6
-rw-r--r--scene/2d/animated_sprite_2d.h9
-rw-r--r--scene/2d/back_buffer_copy.cpp4
-rw-r--r--scene/2d/back_buffer_copy.h4
-rw-r--r--scene/2d/light_2d.cpp7
-rw-r--r--scene/2d/light_2d.h5
-rw-r--r--scene/2d/light_occluder_2d.cpp14
-rw-r--r--scene/2d/light_occluder_2d.h9
-rw-r--r--scene/2d/line_2d.cpp2
-rw-r--r--scene/2d/line_2d.h2
-rw-r--r--scene/2d/marker_2d.cpp4
-rw-r--r--scene/2d/marker_2d.h4
-rw-r--r--scene/2d/mesh_instance_2d.cpp4
-rw-r--r--scene/2d/mesh_instance_2d.h4
-rw-r--r--scene/2d/multimesh_instance_2d.cpp4
-rw-r--r--scene/2d/multimesh_instance_2d.h4
-rw-r--r--scene/2d/navigation_link_2d.cpp4
-rw-r--r--scene/2d/navigation_link_2d.h4
-rw-r--r--scene/2d/navigation_region_2d.cpp4
-rw-r--r--scene/2d/navigation_region_2d.h4
-rw-r--r--scene/2d/path_2d.cpp2
-rw-r--r--scene/2d/path_2d.h2
-rw-r--r--scene/2d/physics/collision_polygon_2d.cpp2
-rw-r--r--scene/2d/physics/collision_polygon_2d.h2
-rw-r--r--scene/2d/physics/collision_shape_2d.h4
-rw-r--r--scene/2d/polygon_2d.cpp4
-rw-r--r--scene/2d/polygon_2d.h5
-rw-r--r--scene/2d/sprite_2d.cpp4
-rw-r--r--scene/2d/sprite_2d.h5
-rw-r--r--scene/2d/tile_map.cpp16
-rw-r--r--scene/2d/tile_map.h8
-rw-r--r--scene/2d/touch_screen_button.cpp4
-rw-r--r--scene/2d/touch_screen_button.h4
-rw-r--r--scene/2d/visible_on_screen_notifier_2d.cpp4
-rw-r--r--scene/2d/visible_on_screen_notifier_2d.h4
-rw-r--r--scene/3d/skeleton_3d.cpp18
-rw-r--r--scene/3d/skeleton_3d.h2
-rw-r--r--scene/3d/skeleton_ik_3d.h2
-rw-r--r--scene/gui/code_edit.h6
-rw-r--r--scene/gui/control.cpp46
-rw-r--r--scene/gui/control.h18
-rw-r--r--scene/gui/text_edit.cpp4
-rw-r--r--scene/gui/text_edit.h2
-rw-r--r--scene/main/canvas_item.cpp8
-rw-r--r--scene/main/canvas_item.h23
-rw-r--r--scene/resources/2d/navigation_polygon.cpp4
-rw-r--r--scene/resources/2d/navigation_polygon.h5
-rw-r--r--scene/resources/2d/skeleton/skeleton_modification_stack_2d.h2
-rw-r--r--scene/resources/2d/tile_set.cpp8
-rw-r--r--scene/resources/2d/tile_set.h2
-rw-r--r--scene/resources/3d/importer_mesh.cpp103
-rw-r--r--scene/resources/3d/importer_mesh.h2
-rw-r--r--scene/resources/environment.cpp5
-rw-r--r--scene/resources/font.cpp34
-rw-r--r--scene/resources/surface_tool.cpp2
-rw-r--r--scene/resources/surface_tool.h8
-rw-r--r--scene/resources/visual_shader.cpp4
-rw-r--r--servers/audio/audio_stream.cpp2
-rw-r--r--servers/audio_server.cpp73
-rw-r--r--servers/audio_server.h8
-rw-r--r--servers/camera/camera_feed.cpp20
-rw-r--r--servers/camera/camera_feed.h5
-rw-r--r--servers/display_server.h7
-rw-r--r--servers/rendering/renderer_canvas_cull.cpp3
-rw-r--r--servers/rendering/renderer_rd/effects/copy_effects.cpp18
-rw-r--r--servers/rendering/renderer_rd/effects/copy_effects.h1
-rw-r--r--servers/rendering/renderer_rd/environment/gi.cpp2
-rw-r--r--servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp12
-rw-r--r--servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.cpp4
-rw-r--r--servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.h12
-rw-r--r--servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp41
-rw-r--r--servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.cpp1
-rw-r--r--servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.h26
-rw-r--r--servers/rendering/renderer_rd/renderer_compositor_rd.cpp4
-rw-r--r--servers/rendering/renderer_rd/renderer_scene_render_rd.cpp1
-rw-r--r--servers/rendering/renderer_rd/shaders/effects/cube_to_dp.glsl5
-rw-r--r--servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered.glsl44
-rw-r--r--servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered_inc.glsl29
-rw-r--r--servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile.glsl328
-rw-r--r--servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile_inc.glsl54
-rw-r--r--servers/rendering/renderer_rd/storage_rd/particles_storage.cpp35
-rw-r--r--servers/rendering/renderer_rd/storage_rd/particles_storage.h8
-rw-r--r--servers/rendering/renderer_rd/storage_rd/render_scene_data_rd.h1
-rw-r--r--servers/rendering/renderer_rd/storage_rd/texture_storage.h6
-rw-r--r--servers/rendering/renderer_scene_cull.cpp12
-rw-r--r--servers/rendering/renderer_scene_cull.h1
-rw-r--r--servers/rendering/renderer_scene_render.cpp14
-rw-r--r--servers/rendering/renderer_scene_render.h9
-rw-r--r--servers/rendering/rendering_device.cpp9
-rw-r--r--servers/rendering/rendering_device.h1
-rw-r--r--servers/rendering/rendering_device_driver.cpp2
-rw-r--r--servers/rendering/rendering_device_driver.h4
-rw-r--r--servers/rendering/rendering_device_graph.cpp42
-rw-r--r--servers/rendering/rendering_device_graph.h2
-rw-r--r--servers/rendering/rendering_method.h1
-rw-r--r--servers/rendering/rendering_server_default.h4
-rw-r--r--servers/rendering/shader_language.h2
-rw-r--r--servers/rendering/storage/environment_storage.cpp12
-rw-r--r--servers/rendering/storage/environment_storage.h5
-rw-r--r--servers/rendering_server.cpp3
-rw-r--r--servers/rendering_server.h1
-rw-r--r--tests/scene/test_primitives.h18
-rw-r--r--tests/servers/test_navigation_server_3d.h2
-rw-r--r--tests/test_macros.h2
-rw-r--r--thirdparty/README.md9
-rw-r--r--thirdparty/certs/ca-certificates.crt149
-rw-r--r--thirdparty/misc/smolv.cpp8
-rw-r--r--thirdparty/misc/smolv.h4
-rw-r--r--thirdparty/openxr/patches/use-egl-from-glad.diff27
-rw-r--r--thirdparty/openxr/src/common/xr_dependencies.h8
240 files changed, 4970 insertions, 1665 deletions
diff --git a/.clang-tidy b/.clang-tidy
index 366781cc82..1eb974f3f8 100644
--- a/.clang-tidy
+++ b/.clang-tidy
@@ -1,14 +1,12 @@
----
-Checks: >-
- -*,
- cppcoreguidelines-pro-type-member-init,
- modernize-redundant-void-arg,
- modernize-use-bool-literals,
- modernize-use-default-member-init,
- modernize-use-nullptr,
- readability-braces-around-statements,
- readability-redundant-member-init
-WarningsAsErrors: ''
+Checks:
+ - -*
+ - cppcoreguidelines-pro-type-member-init
+ - modernize-redundant-void-arg
+ - modernize-use-bool-literals
+ - modernize-use-default-member-init
+ - modernize-use-nullptr
+ - readability-braces-around-statements
+ - readability-redundant-member-init
HeaderFileExtensions: ['', h, hh, hpp, hxx, inc, glsl]
ImplementationFileExtensions: [c, cc, cpp, cxx, m, mm, java]
HeaderFilterRegex: (core|doc|drivers|editor|main|modules|platform|scene|servers|tests)/
@@ -19,4 +17,3 @@ CheckOptions:
modernize-use-bool-literals.IgnoreMacros: false
modernize-use-default-member-init.IgnoreMacros: false
modernize-use-default-member-init.UseAssignment: true
-...
diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs
index f0d61264bc..d028a2e60c 100644
--- a/.git-blame-ignore-revs
+++ b/.git-blame-ignore-revs
@@ -66,3 +66,6 @@ e8542b06acca3c1bdeee4b528411771f0819f084
# Set clang-format `RemoveSemicolon` rule to `true`
0d350e71086fffce0553811739aae9f6ad66136c
+
+# Style: Apply clang-tidy fixes (superficial)
+bb5f390fb9b466be35a5df7651323d7e66afca31
diff --git a/.github/workflows/linux_builds.yml b/.github/workflows/linux_builds.yml
index c4885ce131..911c377c50 100644
--- a/.github/workflows/linux_builds.yml
+++ b/.github/workflows/linux_builds.yml
@@ -106,7 +106,7 @@ jobs:
# TODO: Figure out somehow how to embed this one.
- name: wayland-scanner dependency
run: |
- sudo apt-get install libwayland-bin libegl-dev
+ sudo apt-get install libwayland-bin
- name: Free disk space on runner
run: |
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 29263f07ee..d81c1043a7 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -9,7 +9,7 @@ exclude: |
repos:
- repo: https://github.com/pre-commit/mirrors-clang-format
- rev: v19.1.0
+ rev: v19.1.3
hooks:
- id: clang-format
files: \.(c|h|cpp|hpp|cc|hh|cxx|hxx|m|mm|inc|java)$
@@ -22,7 +22,7 @@ repos:
)
- id: clang-format
name: clang-format-glsl
- files: \.(glsl)$
+ files: \.glsl$
types_or: [text]
exclude: |
(?x)^(
@@ -30,7 +30,7 @@ repos:
platform/android/java/editor/src/main/java/com/android/.*|
platform/android/java/lib/src/com/.*
)
- args: ["-style=file:misc/utility/.clang-format-glsl"]
+ args: ['-style=file:misc/utility/.clang-format-glsl']
- repo: https://github.com/pocc/pre-commit-hooks
rev: v1.3.5
@@ -45,7 +45,7 @@ repos:
platform/android/java/editor/src/main/java/com/android/.*|
platform/android/java/lib/src/com/.*
)
- additional_dependencies: [clang-tidy==18.1.1]
+ additional_dependencies: [clang-tidy==19.1.0]
require_serial: true
stages: [manual] # Not automatically triggered, invoked via `pre-commit run --hook-stage manual clang-tidy`
diff --git a/COPYRIGHT.txt b/COPYRIGHT.txt
index b83fd359b3..d8c2ffa5cc 100644
--- a/COPYRIGHT.txt
+++ b/COPYRIGHT.txt
@@ -441,7 +441,7 @@ License: BSD-3-clause
Files: ./thirdparty/misc/smolv.cpp
./thirdparty/misc/smolv.h
Comment: SMOL-V
-Copyright: 2016-2020, Aras Pranckevicius
+Copyright: 2016-2024, Aras Pranckevicius
License: public-domain or Unlicense or Expat
Files: ./thirdparty/misc/stb_rect_pack.h
diff --git a/core/input/input.cpp b/core/input/input.cpp
index 0b82292c7c..ad1fd7c9d6 100644
--- a/core/input/input.cpp
+++ b/core/input/input.cpp
@@ -1036,7 +1036,7 @@ void Input::action_release(const StringName &p_action) {
// Create or retrieve existing action.
ActionState &action_state = action_states[p_action];
- action_state.cache.pressed = 0;
+ action_state.cache.pressed = false;
action_state.cache.strength = 0.0;
action_state.cache.raw_strength = 0.0;
// As input may come in part way through a physics tick, the earliest we can react to it is the next physics tick.
diff --git a/core/object/message_queue.h b/core/object/message_queue.h
index ee62919356..04371a4d4a 100644
--- a/core/object/message_queue.h
+++ b/core/object/message_queue.h
@@ -155,7 +155,7 @@ public:
bool is_flushing() const;
int get_max_buffer_usage() const;
- CallQueue(Allocator *p_custom_allocator = 0, uint32_t p_max_pages = 8192, const String &p_error_text = String());
+ CallQueue(Allocator *p_custom_allocator = nullptr, uint32_t p_max_pages = 8192, const String &p_error_text = String());
virtual ~CallQueue();
};
diff --git a/core/typedefs.h b/core/typedefs.h
index 6ba03e1a11..78ba0f5792 100644
--- a/core/typedefs.h
+++ b/core/typedefs.h
@@ -317,6 +317,4 @@ struct BuildIndexSequence<0, Is...> : IndexSequence<Is...> {};
#define ___gd_is_defined(val) ____gd_is_defined(__GDARG_PLACEHOLDER_##val)
#define GD_IS_DEFINED(x) ___gd_is_defined(x)
-#define FORCE_SEMICOLON ;
-
#endif // TYPEDEFS_H
diff --git a/core/variant/variant.cpp b/core/variant/variant.cpp
index 26c0e8b6e1..257b290e46 100644
--- a/core/variant/variant.cpp
+++ b/core/variant/variant.cpp
@@ -2721,8 +2721,7 @@ Variant::Variant(const Vector<Plane> &p_array) :
}
}
-Variant::Variant(const Vector<Face3> &p_face_array) :
- type(NIL) {
+Variant::Variant(const Vector<Face3> &p_face_array) {
PackedVector3Array vertices;
int face_count = p_face_array.size();
vertices.resize(face_count * 3);
@@ -2741,8 +2740,7 @@ Variant::Variant(const Vector<Face3> &p_face_array) :
*this = vertices;
}
-Variant::Variant(const Vector<Variant> &p_array) :
- type(NIL) {
+Variant::Variant(const Vector<Variant> &p_array) {
Array arr;
arr.resize(p_array.size());
for (int i = 0; i < p_array.size(); i++) {
@@ -2751,8 +2749,7 @@ Variant::Variant(const Vector<Variant> &p_array) :
*this = arr;
}
-Variant::Variant(const Vector<StringName> &p_array) :
- type(NIL) {
+Variant::Variant(const Vector<StringName> &p_array) {
PackedStringArray v;
int len = p_array.size();
v.resize(len);
@@ -2910,8 +2907,7 @@ Variant::Variant(const IPAddress &p_address) :
memnew_placement(_data._mem, String(p_address));
}
-Variant::Variant(const Variant &p_variant) :
- type(NIL) {
+Variant::Variant(const Variant &p_variant) {
reference(p_variant);
}
diff --git a/core/variant/variant.h b/core/variant/variant.h
index 4e13c8351c..9fd0472550 100644
--- a/core/variant/variant.h
+++ b/core/variant/variant.h
@@ -816,8 +816,7 @@ public:
static void unregister_types();
Variant(const Variant &p_variant);
- _FORCE_INLINE_ Variant() :
- type(NIL) {}
+ _FORCE_INLINE_ Variant() {}
_FORCE_INLINE_ ~Variant() {
clear();
}
diff --git a/doc/classes/CameraFeed.xml b/doc/classes/CameraFeed.xml
index 794704d17e..04afc582b4 100644
--- a/doc/classes/CameraFeed.xml
+++ b/doc/classes/CameraFeed.xml
@@ -34,6 +34,21 @@
Returns the position of camera on the device.
</description>
</method>
+ <method name="get_texture_tex_id">
+ <return type="int" />
+ <param index="0" name="feed_image_type" type="int" enum="CameraServer.FeedImage" />
+ <description>
+ Returns the texture backend ID (usable by some external libraries that need a handle to a texture to write data).
+ </description>
+ </method>
+ <method name="set_external">
+ <return type="void" />
+ <param index="0" name="width" type="int" />
+ <param index="1" name="height" type="int" />
+ <description>
+ Sets the feed as external feed provided by another library.
+ </description>
+ </method>
<method name="set_format">
<return type="bool" />
<param index="0" name="index" type="int" />
@@ -110,6 +125,9 @@
<constant name="FEED_YCBCR_SEP" value="3" enum="FeedDataType">
Feed supplies separate Y and CbCr images that need to be combined and converted to RGB.
</constant>
+ <constant name="FEED_EXTERNAL" value="4" enum="FeedDataType">
+ Feed supplies external image.
+ </constant>
<constant name="FEED_UNSPECIFIED" value="0" enum="FeedPosition">
Unspecified position.
</constant>
diff --git a/doc/classes/DisplayServer.xml b/doc/classes/DisplayServer.xml
index 82b89beed1..e69748baff 100644
--- a/doc/classes/DisplayServer.xml
+++ b/doc/classes/DisplayServer.xml
@@ -185,7 +185,7 @@
<return type="Color" />
<description>
Returns OS theme accent color. Returns [code]Color(0, 0, 0, 0)[/code], if accent color is unknown.
- [b]Note:[/b] This method is implemented on macOS and Windows.
+ [b]Note:[/b] This method is implemented on macOS, Windows, and Android.
</description>
</method>
<method name="get_base_color" qualifiers="const">
diff --git a/doc/classes/RenderingServer.xml b/doc/classes/RenderingServer.xml
index 876f3bdb19..86eaa7e8bb 100644
--- a/doc/classes/RenderingServer.xml
+++ b/doc/classes/RenderingServer.xml
@@ -1278,6 +1278,14 @@
Sets the intensity of the background color.
</description>
</method>
+ <method name="environment_set_camera_id">
+ <return type="void" />
+ <param index="0" name="env" type="RID" />
+ <param index="1" name="id" type="int" />
+ <description>
+ Sets the camera ID to be used as environment background.
+ </description>
+ </method>
<method name="environment_set_canvas_max_layer">
<return type="void" />
<param index="0" name="env" type="RID" />
diff --git a/doc/classes/SpringArm3D.xml b/doc/classes/SpringArm3D.xml
index 99389fb78d..330317f352 100644
--- a/doc/classes/SpringArm3D.xml
+++ b/doc/classes/SpringArm3D.xml
@@ -7,6 +7,7 @@
[SpringArm3D] casts a ray or a shape along its Z axis and moves all its direct children to the collision point, with an optional margin. This is useful for 3rd person cameras that move closer to the player when inside a tight space (you may need to exclude the player's collider from the [SpringArm3D]'s collision check).
</description>
<tutorials>
+ <link title="Third-person camera with spring arm">$DOCS_URL/tutorials/3d/spring_arm.html</link>
</tutorials>
<methods>
<method name="add_excluded_object">
diff --git a/drivers/d3d12/rendering_device_driver_d3d12.cpp b/drivers/d3d12/rendering_device_driver_d3d12.cpp
index 35c8ea6b01..29187b5967 100644
--- a/drivers/d3d12/rendering_device_driver_d3d12.cpp
+++ b/drivers/d3d12/rendering_device_driver_d3d12.cpp
@@ -6179,6 +6179,8 @@ uint64_t RenderingDeviceDriverD3D12::api_trait_get(ApiTrait p_trait) {
return false;
case API_TRAIT_USE_GENERAL_IN_COPY_QUEUES:
return true;
+ case API_TRAIT_BUFFERS_REQUIRE_TRANSITIONS:
+ return !barrier_capabilities.enhanced_barriers_supported;
default:
return RenderingDeviceDriver::api_trait_get(p_trait);
}
diff --git a/drivers/gles3/effects/feed_effects.cpp b/drivers/gles3/effects/feed_effects.cpp
new file mode 100644
index 0000000000..088b60fa1a
--- /dev/null
+++ b/drivers/gles3/effects/feed_effects.cpp
@@ -0,0 +1,130 @@
+/**************************************************************************/
+/* feed_effects.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* REDOT ENGINE */
+/* https://redotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2024-present Redot Engine contributors */
+/* (see REDOT_AUTHORS.md) */
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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. */
+/**************************************************************************/
+
+#ifdef GLES3_ENABLED
+
+#include "feed_effects.h"
+
+#ifdef ANDROID_ENABLED
+#include <GLES3/gl3ext.h>
+#endif
+
+#define GL_PROGRAM_POINT_SIZE 0x8642
+
+using namespace GLES3;
+
+FeedEffects *FeedEffects::singleton = nullptr;
+
+FeedEffects *FeedEffects::get_singleton() {
+ return singleton;
+}
+
+FeedEffects::FeedEffects() {
+ singleton = this;
+
+ feed.shader.initialize();
+ feed.shader_version = feed.shader.version_create();
+ feed.shader.version_bind_shader(feed.shader_version, FeedShaderGLES3::MODE_DEFAULT);
+
+ { // Screen Triangle.
+ glGenBuffers(1, &screen_triangle);
+ glBindBuffer(GL_ARRAY_BUFFER, screen_triangle);
+
+ const float qv[6] = {
+ -1.0f,
+ -1.0f,
+ 3.0f,
+ -1.0f,
+ -1.0f,
+ 3.0f,
+ };
+
+ glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 6, qv, GL_STATIC_DRAW);
+ glBindBuffer(GL_ARRAY_BUFFER, 0); //unbind
+
+ glGenVertexArrays(1, &screen_triangle_array);
+ glBindVertexArray(screen_triangle_array);
+ glBindBuffer(GL_ARRAY_BUFFER, screen_triangle);
+ glVertexAttribPointer(RS::ARRAY_VERTEX, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 2, nullptr);
+ glEnableVertexAttribArray(RS::ARRAY_VERTEX);
+ glBindVertexArray(0);
+ glBindBuffer(GL_ARRAY_BUFFER, 0); //unbind
+ }
+}
+
+FeedEffects::~FeedEffects() {
+ singleton = nullptr;
+ glDeleteBuffers(1, &screen_triangle);
+ glDeleteVertexArrays(1, &screen_triangle_array);
+ feed.shader.version_free(feed.shader_version);
+}
+
+Transform3D transform3D_from_mat4(const float *p_mat4) {
+ Transform3D res;
+
+ res.basis.rows[0][0] = p_mat4[0];
+ res.basis.rows[1][0] = p_mat4[1];
+ res.basis.rows[2][0] = p_mat4[2];
+ // p_mat4[3] = 0;
+ res.basis.rows[0][1] = p_mat4[4];
+ res.basis.rows[1][1] = p_mat4[5];
+ res.basis.rows[2][1] = p_mat4[6];
+ // p_mat4[7] = 0;
+ res.basis.rows[0][2] = p_mat4[8];
+ res.basis.rows[1][2] = p_mat4[9];
+ res.basis.rows[2][2] = p_mat4[10];
+ // p_mat4[11] = 0;
+ res.origin.x = p_mat4[12];
+ res.origin.y = p_mat4[13];
+ res.origin.z = p_mat4[14];
+ // p_mat4[15] = 1;
+
+ return res;
+}
+
+void FeedEffects::draw() {
+ bool success = feed.shader.version_bind_shader(feed.shader_version, FeedShaderGLES3::MODE_DEFAULT, FeedShaderGLES3::USE_EXTERNAL_SAMPLER);
+ if (!success) {
+ OS::get_singleton()->print("Godot : FeedShaderGLES3 Could not bind version_bind_shader USE_EXTERNAL_SAMPLER");
+ return;
+ }
+
+ draw_screen_triangle();
+}
+
+void FeedEffects::draw_screen_triangle() {
+ glBindVertexArray(screen_triangle_array);
+ glDrawArrays(GL_TRIANGLES, 0, 3);
+ glBindVertexArray(0);
+}
+
+#endif // GLES3_ENABLED
diff --git a/drivers/gles3/effects/feed_effects.h b/drivers/gles3/effects/feed_effects.h
new file mode 100644
index 0000000000..4bd15d81ec
--- /dev/null
+++ b/drivers/gles3/effects/feed_effects.h
@@ -0,0 +1,70 @@
+/**************************************************************************/
+/* feed_effects.h */
+/**************************************************************************/
+/* This file is part of: */
+/* REDOT ENGINE */
+/* https://redotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2024-present Redot Engine contributors */
+/* (see REDOT_AUTHORS.md) */
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 FEED_EFFECTS_GLES3_H
+#define FEED_EFFECTS_GLES3_H
+
+#ifdef GLES3_ENABLED
+
+#include "drivers/gles3/shaders/feed.glsl.gen.h"
+
+namespace GLES3 {
+
+class FeedEffects {
+private:
+ struct Feed {
+ FeedShaderGLES3 shader;
+ RID shader_version;
+ } feed;
+
+ static FeedEffects *singleton;
+
+ GLuint screen_triangle = 0;
+ GLuint screen_triangle_array = 0;
+
+public:
+ static FeedEffects *get_singleton();
+
+ FeedEffects();
+ ~FeedEffects();
+
+ void draw();
+
+private:
+ void draw_screen_triangle();
+};
+
+} // namespace GLES3
+
+#endif // GLES3_ENABLED
+
+#endif // FEED_EFFECTS_GLES3_H
diff --git a/drivers/gles3/rasterizer_gles3.cpp b/drivers/gles3/rasterizer_gles3.cpp
index c2524ea05e..f2275431a7 100644
--- a/drivers/gles3/rasterizer_gles3.cpp
+++ b/drivers/gles3/rasterizer_gles3.cpp
@@ -220,6 +220,7 @@ void RasterizerGLES3::finalize() {
memdelete(glow);
memdelete(cubemap_filter);
memdelete(copy_effects);
+ memdelete(feed_effects);
memdelete(light_storage);
memdelete(particles_storage);
memdelete(mesh_storage);
@@ -368,6 +369,7 @@ RasterizerGLES3::RasterizerGLES3() {
cubemap_filter = memnew(GLES3::CubemapFilter);
glow = memnew(GLES3::Glow);
post_effects = memnew(GLES3::PostEffects);
+ feed_effects = memnew(GLES3::FeedEffects);
gi = memnew(GLES3::GI);
fog = memnew(GLES3::Fog);
canvas = memnew(RasterizerCanvasGLES3());
diff --git a/drivers/gles3/rasterizer_gles3.h b/drivers/gles3/rasterizer_gles3.h
index 576aead94a..0c5fcdcd9d 100644
--- a/drivers/gles3/rasterizer_gles3.h
+++ b/drivers/gles3/rasterizer_gles3.h
@@ -37,6 +37,7 @@
#include "effects/copy_effects.h"
#include "effects/cubemap_filter.h"
+#include "effects/feed_effects.h"
#include "effects/glow.h"
#include "effects/post_effects.h"
#include "environment/fog.h"
@@ -80,6 +81,7 @@ protected:
GLES3::CubemapFilter *cubemap_filter = nullptr;
GLES3::Glow *glow = nullptr;
GLES3::PostEffects *post_effects = nullptr;
+ GLES3::FeedEffects *feed_effects = nullptr;
RasterizerCanvasGLES3 *canvas = nullptr;
RasterizerSceneGLES3 *scene = nullptr;
static RasterizerGLES3 *singleton;
diff --git a/drivers/gles3/rasterizer_scene_gles3.cpp b/drivers/gles3/rasterizer_scene_gles3.cpp
index 7eef0544cb..96b311b048 100644
--- a/drivers/gles3/rasterizer_scene_gles3.cpp
+++ b/drivers/gles3/rasterizer_scene_gles3.cpp
@@ -33,6 +33,7 @@
#include "rasterizer_scene_gles3.h"
#include "drivers/gles3/effects/copy_effects.h"
+#include "drivers/gles3/effects/feed_effects.h"
#include "rasterizer_gles3.h"
#include "storage/config.h"
#include "storage/mesh_storage.h"
@@ -41,6 +42,8 @@
#include "core/config/project_settings.h"
#include "core/templates/sort_array.h"
+#include "servers/camera/camera_feed.h"
+#include "servers/camera_server.h"
#include "servers/rendering/rendering_server_default.h"
#include "servers/rendering/rendering_server_globals.h"
@@ -2261,6 +2264,7 @@ void RasterizerSceneGLES3::render_scene(const Ref<RenderSceneBuffers> &p_render_
render_data.inv_cam_transform = render_data.cam_transform.affine_inverse();
render_data.cam_projection = p_camera_data->main_projection;
render_data.cam_orthogonal = p_camera_data->is_orthogonal;
+ render_data.cam_frustum = p_camera_data->is_frustum;
render_data.camera_visible_layers = p_camera_data->visible_layers;
render_data.main_cam_transform = p_camera_data->main_transform;
@@ -2384,7 +2388,9 @@ void RasterizerSceneGLES3::render_scene(const Ref<RenderSceneBuffers> &p_render_
bool draw_sky = false;
bool draw_sky_fog_only = false;
bool keep_color = false;
+ bool draw_feed = false;
float sky_energy_multiplier = 1.0;
+ int camera_feed_id = -1;
if (unlikely(get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_OVERDRAW)) {
clear_color = Color(0, 0, 0, 1); //in overdraw mode, BG should always be black
@@ -2429,6 +2435,8 @@ void RasterizerSceneGLES3::render_scene(const Ref<RenderSceneBuffers> &p_render_
keep_color = true;
} break;
case RS::ENV_BG_CAMERA_FEED: {
+ camera_feed_id = environment_get_camera_feed_id(render_data.environment);
+ draw_feed = true;
} break;
default: {
}
@@ -2540,7 +2548,7 @@ void RasterizerSceneGLES3::render_scene(const Ref<RenderSceneBuffers> &p_render_
glClear(GL_DEPTH_BUFFER_BIT);
}
- if (!keep_color) {
+ if (!keep_color && !draw_feed) {
clear_color.a = render_data.transparent_bg ? 0.0f : 1.0f;
glClearBufferfv(GL_COLOR, 0, clear_color.components);
} else if (fbo != rt->fbo) {
@@ -2580,6 +2588,29 @@ void RasterizerSceneGLES3::render_scene(const Ref<RenderSceneBuffers> &p_render_
spec_constant_base_flags |= SceneShaderGLES3::APPLY_TONEMAPPING;
}
}
+
+ if (draw_feed && camera_feed_id > -1) {
+ RENDER_TIMESTAMP("Render Camera feed");
+
+ scene_state.enable_gl_depth_draw(false);
+ scene_state.enable_gl_depth_test(false);
+ scene_state.enable_gl_blend(false);
+ scene_state.set_gl_cull_mode(GLES3::SceneShaderData::CULL_BACK);
+
+ Ref<CameraFeed> feed = CameraServer::get_singleton()->get_feed_by_id(camera_feed_id);
+
+ if (feed.is_valid()) {
+ RID camera_YCBCR = feed->get_texture(CameraServer::FEED_YCBCR_IMAGE);
+ GLES3::TextureStorage::get_singleton()->texture_bind(camera_YCBCR, 0);
+
+ GLES3::FeedEffects *feed_effects = GLES3::FeedEffects::get_singleton();
+ feed_effects->draw();
+ }
+ scene_state.enable_gl_depth_draw(true);
+ scene_state.enable_gl_depth_test(true);
+ scene_state.enable_gl_blend(true);
+ }
+
// Render Opaque Objects.
RenderListParameters render_list_params(render_list[RENDER_LIST_OPAQUE].elements.ptr(), render_list[RENDER_LIST_OPAQUE].elements.size(), reverse_cull, spec_constant_base_flags, use_wireframe);
@@ -2594,14 +2625,18 @@ void RasterizerSceneGLES3::render_scene(const Ref<RenderSceneBuffers> &p_render_
scene_state.enable_gl_blend(false);
scene_state.set_gl_cull_mode(GLES3::SceneShaderData::CULL_BACK);
+ Transform3D transform = render_data.cam_transform;
Projection projection = render_data.cam_projection;
if (is_reflection_probe) {
Projection correction;
correction.columns[1][1] = -1.0;
projection = correction * render_data.cam_projection;
+ } else if (render_data.cam_frustum) {
+ // Sky is drawn upside down, the frustum offset doesn't know the image is upside down so needs a flip.
+ projection[2].y = -projection[2].y;
}
- _draw_sky(render_data.environment, projection, render_data.cam_transform, sky_energy_multiplier, render_data.luminance_multiplier, p_camera_data->view_count > 1, flip_y, apply_color_adjustments_in_post);
+ _draw_sky(render_data.environment, projection, transform, sky_energy_multiplier, render_data.luminance_multiplier, p_camera_data->view_count > 1, flip_y, apply_color_adjustments_in_post);
}
if (rt && (scene_state.used_screen_texture || scene_state.used_depth_texture)) {
diff --git a/drivers/gles3/rasterizer_scene_gles3.h b/drivers/gles3/rasterizer_scene_gles3.h
index 4a86106591..d719c425a7 100644
--- a/drivers/gles3/rasterizer_scene_gles3.h
+++ b/drivers/gles3/rasterizer_scene_gles3.h
@@ -105,6 +105,7 @@ struct RenderDataGLES3 {
Transform3D inv_cam_transform;
Projection cam_projection;
bool cam_orthogonal = false;
+ bool cam_frustum = false;
uint32_t camera_visible_layers = 0xFFFFFFFF;
// For billboards to cast correct shadows.
diff --git a/drivers/gles3/shaders/SCsub b/drivers/gles3/shaders/SCsub
index df2c515035..0207ba12b7 100644
--- a/drivers/gles3/shaders/SCsub
+++ b/drivers/gles3/shaders/SCsub
@@ -17,6 +17,7 @@ if "GLES3_GLSL" in env["BUILDERS"]:
# as we have a few, not yet, converted files we name the ones we want to include:
env.GLES3_GLSL("canvas.glsl")
+ env.GLES3_GLSL("feed.glsl")
env.GLES3_GLSL("scene.glsl")
env.GLES3_GLSL("sky.glsl")
env.GLES3_GLSL("canvas_occlusion.glsl")
diff --git a/drivers/gles3/shaders/feed.glsl b/drivers/gles3/shaders/feed.glsl
new file mode 100644
index 0000000000..9d89fc699d
--- /dev/null
+++ b/drivers/gles3/shaders/feed.glsl
@@ -0,0 +1,39 @@
+/* clang-format off */
+#[modes]
+
+mode_default =
+
+#[specializations]
+
+USE_EXTERNAL_SAMPLER = false
+
+#[vertex]
+
+layout(location = 0) in vec2 vertex_attrib;
+
+out vec2 uv_interp;
+
+
+void main() {
+ uv_interp = vertex_attrib * 0.5 + 0.5;
+ gl_Position = vec4(vertex_attrib, 1.0, 1.0);
+}
+
+/* clang-format off */
+#[fragment]
+
+layout(location = 0) out vec4 frag_color;
+in vec2 uv_interp;
+
+/* clang-format on */
+#ifdef USE_EXTERNAL_SAMPLER
+uniform samplerExternalOES sourceFeed; // texunit:0
+#else
+uniform sampler2D sourceFeed; // texunit:0
+#endif
+
+void main() {
+ vec4 color = texture(sourceFeed, uv_interp);
+
+ frag_color = color;
+}
diff --git a/drivers/gles3/storage/particles_storage.h b/drivers/gles3/storage/particles_storage.h
index 9bb2a7ed59..5c632ace02 100644
--- a/drivers/gles3/storage/particles_storage.h
+++ b/drivers/gles3/storage/particles_storage.h
@@ -398,7 +398,7 @@ public:
_FORCE_INLINE_ bool particles_has_collision(RID p_particles) {
Particles *particles = particles_owner.get_or_null(p_particles);
- ERR_FAIL_NULL_V(particles, 0);
+ ERR_FAIL_NULL_V(particles, false);
return particles->has_collision_cache;
}
diff --git a/drivers/png/SCsub b/drivers/png/SCsub
index c9fe84b221..e029ea9b49 100644
--- a/drivers/png/SCsub
+++ b/drivers/png/SCsub
@@ -46,18 +46,18 @@ if env["builtin_libpng"]:
if "S_compiler" in env:
env_neon["CC"] = env["S_compiler"]
neon_sources = []
- neon_sources.append(env_neon.Object(thirdparty_dir + "/arm/arm_init.c"))
- neon_sources.append(env_neon.Object(thirdparty_dir + "/arm/filter_neon_intrinsics.c"))
- neon_sources.append(env_neon.Object(thirdparty_dir + "/arm/filter_neon.S"))
- neon_sources.append(env_neon.Object(thirdparty_dir + "/arm/palette_neon_intrinsics.c"))
+ neon_sources.append(env_neon.Object(thirdparty_dir + "arm/arm_init.c"))
+ neon_sources.append(env_neon.Object(thirdparty_dir + "arm/filter_neon_intrinsics.c"))
+ neon_sources.append(env_neon.Object(thirdparty_dir + "arm/filter_neon.S"))
+ neon_sources.append(env_neon.Object(thirdparty_dir + "arm/palette_neon_intrinsics.c"))
thirdparty_obj += neon_sources
elif env["arch"].startswith("x86"):
env_thirdparty.Append(CPPDEFINES=["PNG_INTEL_SSE"])
- env_thirdparty.add_source_files(thirdparty_obj, thirdparty_dir + "/intel/intel_init.c")
- env_thirdparty.add_source_files(thirdparty_obj, thirdparty_dir + "/intel/filter_sse2_intrinsics.c")
+ env_thirdparty.add_source_files(thirdparty_obj, thirdparty_dir + "intel/intel_init.c")
+ env_thirdparty.add_source_files(thirdparty_obj, thirdparty_dir + "intel/filter_sse2_intrinsics.c")
elif env["arch"] == "ppc64":
- env_thirdparty.add_source_files(thirdparty_obj, thirdparty_dir + "/powerpc/powerpc_init.c")
- env_thirdparty.add_source_files(thirdparty_obj, thirdparty_dir + "/powerpc/filter_vsx_intrinsics.c")
+ env_thirdparty.add_source_files(thirdparty_obj, thirdparty_dir + "powerpc/powerpc_init.c")
+ env_thirdparty.add_source_files(thirdparty_obj, thirdparty_dir + "powerpc/filter_vsx_intrinsics.c")
env.drivers_sources += thirdparty_obj
diff --git a/drivers/vulkan/rendering_device_driver_vulkan.cpp b/drivers/vulkan/rendering_device_driver_vulkan.cpp
index 2b537a6720..4b102d773b 100644
--- a/drivers/vulkan/rendering_device_driver_vulkan.cpp
+++ b/drivers/vulkan/rendering_device_driver_vulkan.cpp
@@ -1521,7 +1521,7 @@ RDD::BufferID RenderingDeviceDriverVulkan::buffer_create(uint64_t p_size, BitFie
ERR_FAIL_COND_V_MSG(err, BufferID(), "Can't create buffer of size: " + itos(p_size) + ", error " + itos(err) + ".");
err = vmaAllocateMemoryForBuffer(allocator, vk_buffer, &alloc_create_info, &allocation, &alloc_info);
ERR_FAIL_COND_V_MSG(err, BufferID(), "Can't allocate memory for buffer of size: " + itos(p_size) + ", error " + itos(err) + ".");
- err = vmaBindBufferMemory2(allocator, allocation, 0, vk_buffer, NULL);
+ err = vmaBindBufferMemory2(allocator, allocation, 0, vk_buffer, nullptr);
ERR_FAIL_COND_V_MSG(err, BufferID(), "Can't bind memory to buffer of size: " + itos(p_size) + ", error " + itos(err) + ".");
// Bookkeep.
@@ -1747,7 +1747,7 @@ RDD::TextureID RenderingDeviceDriverVulkan::texture_create(const TextureFormat &
ERR_FAIL_COND_V_MSG(err, TextureID(), "vkCreateImage failed with error " + itos(err) + ".");
err = vmaAllocateMemoryForImage(allocator, vk_image, &alloc_create_info, &allocation, &alloc_info);
ERR_FAIL_COND_V_MSG(err, TextureID(), "Can't allocate memory for image, error: " + itos(err) + ".");
- err = vmaBindImageMemory2(allocator, allocation, 0, vk_image, NULL);
+ err = vmaBindImageMemory2(allocator, allocation, 0, vk_image, nullptr);
ERR_FAIL_COND_V_MSG(err, TextureID(), "Can't bind memory to image, error: " + itos(err) + ".");
// Create view.
@@ -2998,6 +2998,24 @@ Error RenderingDeviceDriverVulkan::swap_chain_resize(CommandQueueID p_cmd_queue,
swap_create_info.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
swap_create_info.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
swap_create_info.preTransform = surface_transform_bits;
+ switch (swap_create_info.preTransform) {
+ case VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR:
+ swap_chain->pre_transform_rotation_degrees = 0;
+ break;
+ case VK_SURFACE_TRANSFORM_ROTATE_90_BIT_KHR:
+ swap_chain->pre_transform_rotation_degrees = 90;
+ break;
+ case VK_SURFACE_TRANSFORM_ROTATE_180_BIT_KHR:
+ swap_chain->pre_transform_rotation_degrees = 180;
+ break;
+ case VK_SURFACE_TRANSFORM_ROTATE_270_BIT_KHR:
+ swap_chain->pre_transform_rotation_degrees = 270;
+ break;
+ default:
+ WARN_PRINT("Unexpected swap_create_info.preTransform = " + itos(swap_create_info.preTransform) + ".");
+ swap_chain->pre_transform_rotation_degrees = 0;
+ break;
+ }
swap_create_info.compositeAlpha = composite_alpha;
swap_create_info.presentMode = present_mode;
swap_create_info.clipped = true;
@@ -3169,6 +3187,13 @@ RDD::RenderPassID RenderingDeviceDriverVulkan::swap_chain_get_render_pass(SwapCh
return swap_chain->render_pass;
}
+int RenderingDeviceDriverVulkan::swap_chain_get_pre_rotation_degrees(SwapChainID p_swap_chain) {
+ DEV_ASSERT(p_swap_chain.id != 0);
+
+ SwapChain *swap_chain = (SwapChain *)(p_swap_chain.id);
+ return swap_chain->pre_transform_rotation_degrees;
+}
+
RDD::DataFormat RenderingDeviceDriverVulkan::swap_chain_get_format(SwapChainID p_swap_chain) {
DEV_ASSERT(p_swap_chain.id != 0);
@@ -4019,7 +4044,7 @@ RDD::UniformSetID RenderingDeviceDriverVulkan::uniform_set_create(VectorView<Bou
}
// Need a descriptor pool.
- DescriptorSetPools::Iterator pool_sets_it = {};
+ DescriptorSetPools::Iterator pool_sets_it;
VkDescriptorPool vk_pool = _descriptor_set_pool_find_or_create(pool_key, &pool_sets_it);
DEV_ASSERT(vk_pool);
pool_sets_it->value[vk_pool]++;
diff --git a/drivers/vulkan/rendering_device_driver_vulkan.h b/drivers/vulkan/rendering_device_driver_vulkan.h
index 63c5396cfa..ef6c09278b 100644
--- a/drivers/vulkan/rendering_device_driver_vulkan.h
+++ b/drivers/vulkan/rendering_device_driver_vulkan.h
@@ -361,6 +361,7 @@ private:
LocalVector<CommandQueue *> command_queues_acquired;
LocalVector<uint32_t> command_queues_acquired_semaphores;
RenderPassID render_pass;
+ int pre_transform_rotation_degrees = 0;
uint32_t image_index = 0;
#ifdef ANDROID_ENABLED
uint64_t refresh_duration = 0;
@@ -375,6 +376,7 @@ public:
virtual Error swap_chain_resize(CommandQueueID p_cmd_queue, SwapChainID p_swap_chain, uint32_t p_desired_framebuffer_count) override final;
virtual FramebufferID swap_chain_acquire_framebuffer(CommandQueueID p_cmd_queue, SwapChainID p_swap_chain, bool &r_resize_required) override final;
virtual RenderPassID swap_chain_get_render_pass(SwapChainID p_swap_chain) override final;
+ virtual int swap_chain_get_pre_rotation_degrees(SwapChainID p_swap_chain) override final;
virtual DataFormat swap_chain_get_format(SwapChainID p_swap_chain) override final;
virtual void swap_chain_set_max_fps(SwapChainID p_swap_chain, int p_max_fps) override final;
virtual void swap_chain_free(SwapChainID p_swap_chain) override final;
@@ -489,7 +491,7 @@ private:
struct UniformSetInfo {
VkDescriptorSet vk_descriptor_set = VK_NULL_HANDLE;
VkDescriptorPool vk_descriptor_pool = VK_NULL_HANDLE;
- DescriptorSetPools::Iterator pool_sets_it = {};
+ DescriptorSetPools::Iterator pool_sets_it;
};
public:
diff --git a/drivers/windows/dir_access_windows.cpp b/drivers/windows/dir_access_windows.cpp
index 335a1e6768..8c578e07de 100644
--- a/drivers/windows/dir_access_windows.cpp
+++ b/drivers/windows/dir_access_windows.cpp
@@ -284,7 +284,7 @@ Error DirAccessWindows::rename(String p_path, String p_new_path) {
uint64_t id = OS::get_singleton()->get_ticks_usec();
while (true) {
tmpfile_utf16 = (path + itos(id++) + ".tmp").utf16();
- HANDLE handle = CreateFileW((LPCWSTR)tmpfile_utf16.get_data(), GENERIC_WRITE, 0, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, 0);
+ HANDLE handle = CreateFileW((LPCWSTR)tmpfile_utf16.get_data(), GENERIC_WRITE, 0, nullptr, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, nullptr);
if (handle != INVALID_HANDLE_VALUE) {
CloseHandle(handle);
break;
diff --git a/drivers/windows/file_access_windows.cpp b/drivers/windows/file_access_windows.cpp
index 72cc942f6c..55e0a9b2e8 100644
--- a/drivers/windows/file_access_windows.cpp
+++ b/drivers/windows/file_access_windows.cpp
@@ -195,7 +195,7 @@ Error FileAccessWindows::open_internal(const String &p_path, int p_mode_flags) {
uint64_t id = OS::get_singleton()->get_ticks_usec();
while (true) {
tmpfile = path + itos(id++) + ".tmp";
- HANDLE handle = CreateFileW((LPCWSTR)tmpfile.utf16().get_data(), GENERIC_WRITE, 0, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, 0);
+ HANDLE handle = CreateFileW((LPCWSTR)tmpfile.utf16().get_data(), GENERIC_WRITE, 0, nullptr, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, nullptr);
if (handle != INVALID_HANDLE_VALUE) {
CloseHandle(handle);
break;
diff --git a/drivers/windows/file_access_windows_pipe.cpp b/drivers/windows/file_access_windows_pipe.cpp
index 840a737840..b931757398 100644
--- a/drivers/windows/file_access_windows_pipe.cpp
+++ b/drivers/windows/file_access_windows_pipe.cpp
@@ -42,7 +42,7 @@ Error FileAccessWindowsPipe::open_existing(HANDLE p_rfd, HANDLE p_wfd, bool p_bl
_close();
path_src = String();
- ERR_FAIL_COND_V_MSG(fd[0] != 0 || fd[1] != 0, ERR_ALREADY_IN_USE, "Pipe is already in use.");
+ ERR_FAIL_COND_V_MSG(fd[0] != nullptr || fd[1] != nullptr, ERR_ALREADY_IN_USE, "Pipe is already in use.");
fd[0] = p_rfd;
fd[1] = p_wfd;
@@ -60,18 +60,18 @@ Error FileAccessWindowsPipe::open_internal(const String &p_path, int p_mode_flag
_close();
path_src = p_path;
- ERR_FAIL_COND_V_MSG(fd[0] != 0 || fd[1] != 0, ERR_ALREADY_IN_USE, "Pipe is already in use.");
+ ERR_FAIL_COND_V_MSG(fd[0] != nullptr || fd[1] != nullptr, ERR_ALREADY_IN_USE, "Pipe is already in use.");
path = String("\\\\.\\pipe\\LOCAL\\") + p_path.replace("pipe://", "").replace("/", "_");
- HANDLE h = CreateFileW((LPCWSTR)path.utf16().get_data(), GENERIC_READ | GENERIC_WRITE, 0, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+ HANDLE h = CreateFileW((LPCWSTR)path.utf16().get_data(), GENERIC_READ | GENERIC_WRITE, 0, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
if (h == INVALID_HANDLE_VALUE) {
h = CreateNamedPipeW((LPCWSTR)path.utf16().get_data(), PIPE_ACCESS_DUPLEX, PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_NOWAIT, 1, 4096, 4096, 0, nullptr);
if (h == INVALID_HANDLE_VALUE) {
last_error = ERR_FILE_CANT_OPEN;
return last_error;
}
- ConnectNamedPipe(h, NULL);
+ ConnectNamedPipe(h, nullptr);
}
fd[0] = h;
fd[1] = h;
@@ -81,19 +81,19 @@ Error FileAccessWindowsPipe::open_internal(const String &p_path, int p_mode_flag
}
void FileAccessWindowsPipe::_close() {
- if (fd[0] == 0) {
+ if (fd[0] == nullptr) {
return;
}
if (fd[1] != fd[0]) {
CloseHandle(fd[1]);
}
CloseHandle(fd[0]);
- fd[0] = 0;
- fd[1] = 0;
+ fd[0] = nullptr;
+ fd[1] = nullptr;
}
bool FileAccessWindowsPipe::is_open() const {
- return (fd[0] != 0 || fd[1] != 0);
+ return (fd[0] != nullptr || fd[1] != nullptr);
}
String FileAccessWindowsPipe::get_path() const {
@@ -105,7 +105,7 @@ String FileAccessWindowsPipe::get_path_absolute() const {
}
uint64_t FileAccessWindowsPipe::get_buffer(uint8_t *p_dst, uint64_t p_length) const {
- ERR_FAIL_COND_V_MSG(fd[0] == 0, -1, "Pipe must be opened before use.");
+ ERR_FAIL_COND_V_MSG(fd[0] == nullptr, -1, "Pipe must be opened before use.");
ERR_FAIL_COND_V(!p_dst && p_length > 0, -1);
DWORD read = 0;
@@ -122,7 +122,7 @@ Error FileAccessWindowsPipe::get_error() const {
}
void FileAccessWindowsPipe::store_buffer(const uint8_t *p_src, uint64_t p_length) {
- ERR_FAIL_COND_MSG(fd[1] == 0, "Pipe must be opened before use.");
+ ERR_FAIL_COND_MSG(fd[1] == nullptr, "Pipe must be opened before use.");
ERR_FAIL_COND(!p_src && p_length > 0);
DWORD read = -1;
diff --git a/drivers/windows/file_access_windows_pipe.h b/drivers/windows/file_access_windows_pipe.h
index 6a7d1ddfb1..42eec59df3 100644
--- a/drivers/windows/file_access_windows_pipe.h
+++ b/drivers/windows/file_access_windows_pipe.h
@@ -41,7 +41,7 @@
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
class FileAccessWindowsPipe : public FileAccess {
- HANDLE fd[2] = { 0, 0 };
+ HANDLE fd[2] = { nullptr, nullptr };
mutable Error last_error = OK;
diff --git a/editor/animation_track_editor.cpp b/editor/animation_track_editor.cpp
index b4f936409e..2da4647b00 100644
--- a/editor/animation_track_editor.cpp
+++ b/editor/animation_track_editor.cpp
@@ -6339,8 +6339,9 @@ bool AnimationTrackEditor::_is_track_compatible(int p_target_track_idx, Variant:
}
if (path_valid) {
- if (is_source_bezier)
+ if (is_source_bezier) {
p_source_value_type = Variant::FLOAT;
+ }
return property_type == p_source_value_type;
} else {
if (animation->track_get_key_count(p_target_track_idx) > 0) {
diff --git a/editor/editor_audio_buses.cpp b/editor/editor_audio_buses.cpp
index f368e21347..fb3778ae42 100644
--- a/editor/editor_audio_buses.cpp
+++ b/editor/editor_audio_buses.cpp
@@ -93,17 +93,26 @@ void EditorAudioBus::_notification(int p_what) {
Color mute_color = EditorThemeManager::is_dark_theme() ? Color(1.0, 0.16, 0.16) : Color(2.35, 1.03, 1.03);
Color bypass_color = EditorThemeManager::is_dark_theme() ? Color(0.13, 0.8, 1.0) : Color(1.03, 2.04, 2.35);
float darkening_factor = EditorThemeManager::is_dark_theme() ? 0.15 : 0.65;
+ Color solo_color_darkened = solo_color.darkened(darkening_factor);
+ Color mute_color_darkened = mute_color.darkened(darkening_factor);
+ Color bypass_color_darkened = bypass_color.darkened(darkening_factor);
- Ref<StyleBoxFlat>(solo->get_theme_stylebox(SceneStringName(pressed)))->set_border_color(solo_color.darkened(darkening_factor));
- Ref<StyleBoxFlat>(mute->get_theme_stylebox(SceneStringName(pressed)))->set_border_color(mute_color.darkened(darkening_factor));
- Ref<StyleBoxFlat>(bypass->get_theme_stylebox(SceneStringName(pressed)))->set_border_color(bypass_color.darkened(darkening_factor));
+ Ref<StyleBoxFlat>(solo->get_theme_stylebox(SceneStringName(pressed)))->set_border_color(solo_color_darkened);
+ Ref<StyleBoxFlat>(mute->get_theme_stylebox(SceneStringName(pressed)))->set_border_color(mute_color_darkened);
+ Ref<StyleBoxFlat>(bypass->get_theme_stylebox(SceneStringName(pressed)))->set_border_color(bypass_color_darkened);
+ Ref<StyleBoxFlat>(solo->get_theme_stylebox("hover_pressed"))->set_border_color(solo_color_darkened);
+ Ref<StyleBoxFlat>(mute->get_theme_stylebox("hover_pressed"))->set_border_color(mute_color_darkened);
+ Ref<StyleBoxFlat>(bypass->get_theme_stylebox("hover_pressed"))->set_border_color(bypass_color_darkened);
solo->set_button_icon(get_editor_theme_icon(SNAME("AudioBusSolo")));
solo->add_theme_color_override("icon_pressed_color", solo_color);
+ solo->add_theme_color_override("icon_hover_pressed_color", solo_color_darkened);
mute->set_button_icon(get_editor_theme_icon(SNAME("AudioBusMute")));
mute->add_theme_color_override("icon_pressed_color", mute_color);
+ mute->add_theme_color_override("icon_hover_pressed_color", mute_color_darkened);
bypass->set_button_icon(get_editor_theme_icon(SNAME("AudioBusBypass")));
bypass->add_theme_color_override("icon_pressed_color", bypass_color);
+ bypass->add_theme_color_override("icon_hover_pressed_color", bypass_color_darkened);
bus_options->set_button_icon(get_editor_theme_icon(SNAME("GuiTabMenuHl")));
@@ -843,13 +852,18 @@ EditorAudioBus::EditorAudioBus(EditorAudioBuses *p_buses, bool p_is_master) {
child->begin_bulk_theme_override();
child->add_theme_style_override(CoreStringName(normal), sbempty);
child->add_theme_style_override("hover", sbempty);
+ child->add_theme_style_override("hover_mirrored", sbempty);
child->add_theme_style_override("focus", sbempty);
+ child->add_theme_style_override("focus_mirrored", sbempty);
Ref<StyleBoxFlat> sbflat = memnew(StyleBoxFlat);
sbflat->set_content_margin_all(0);
sbflat->set_bg_color(Color(1, 1, 1, 0));
sbflat->set_border_width(Side::SIDE_BOTTOM, Math::round(3 * EDSCALE));
child->add_theme_style_override(SceneStringName(pressed), sbflat);
+ child->add_theme_style_override("pressed_mirrored", sbflat);
+ child->add_theme_style_override("hover_pressed", sbflat);
+ child->add_theme_style_override("hover_pressed_mirrored", sbflat);
child->end_bulk_theme_override();
}
diff --git a/editor/editor_file_system.cpp b/editor/editor_file_system.cpp
index 0df709f9af..3f71784ada 100644
--- a/editor/editor_file_system.cpp
+++ b/editor/editor_file_system.cpp
@@ -2583,7 +2583,7 @@ Error EditorFileSystem::_reimport_group(const String &p_group_file, const Vector
EditorFileSystemDirectory *fs = nullptr;
int cpos = -1;
bool found = _find_file(file, &fs, cpos);
- ERR_FAIL_COND_V_MSG(!found, ERR_UNCONFIGURED, "Can't find file '" + file + "'.");
+ ERR_FAIL_COND_V_MSG(!found, ERR_UNCONFIGURED, vformat("Can't find file '%s' during group reimport.", file));
//update modified times, to avoid reimport
fs->files[cpos]->modified_time = FileAccess::get_modified_time(file);
@@ -2633,7 +2633,7 @@ Error EditorFileSystem::_reimport_file(const String &p_file, const HashMap<Strin
int cpos = -1;
if (p_update_file_system) {
bool found = _find_file(p_file, &fs, cpos);
- ERR_FAIL_COND_V_MSG(!found, ERR_FILE_NOT_FOUND, "Can't find file '" + p_file + "'.");
+ ERR_FAIL_COND_V_MSG(!found, ERR_FILE_NOT_FOUND, vformat("Can't find file '%s' during file reimport.", p_file));
}
//try to obtain existing params
diff --git a/editor/editor_node.h b/editor/editor_node.h
index 420f464cb0..3bd8b511bb 100644
--- a/editor/editor_node.h
+++ b/editor/editor_node.h
@@ -791,7 +791,7 @@ public:
struct AdditiveNodeEntry {
Node *node = nullptr;
- NodePath parent = NodePath();
+ NodePath parent;
Node *owner = nullptr;
int index = 0;
// Used if the original parent node is lost
diff --git a/editor/gui/editor_quick_open_dialog.cpp b/editor/gui/editor_quick_open_dialog.cpp
index 1157831dd5..f07c6d1477 100644
--- a/editor/gui/editor_quick_open_dialog.cpp
+++ b/editor/gui/editor_quick_open_dialog.cpp
@@ -651,8 +651,9 @@ QuickOpenDisplayMode QuickOpenResultContainer::get_adaptive_display_mode(const V
for (const StringName &type : grid_preferred_types) {
for (const StringName &base_type : p_base_types) {
- if (base_type == type || ClassDB::is_parent_class(base_type, type))
+ if (base_type == type || ClassDB::is_parent_class(base_type, type)) {
return QuickOpenDisplayMode::GRID;
+ }
}
}
diff --git a/editor/icons/MaterialPreviewQuad.svg b/editor/icons/MaterialPreviewQuad.svg
new file mode 100644
index 0000000000..9765a15df7
--- /dev/null
+++ b/editor/icons/MaterialPreviewQuad.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><path fill="none" stroke="#000" stroke-linejoin="round" stroke-opacity=".8" stroke-width="2" d="m2 1 12 1v11l-12 1z"/><path fill="#f9f9f9" d="m2 14 12-1v-11l-12-1z"/></svg> \ No newline at end of file
diff --git a/editor/import/3d/resource_importer_obj.cpp b/editor/import/3d/resource_importer_obj.cpp
index c1969faf42..6e26e228fd 100644
--- a/editor/import/3d/resource_importer_obj.cpp
+++ b/editor/import/3d/resource_importer_obj.cpp
@@ -537,8 +537,6 @@ static Error _parse_obj(const String &p_path, List<Ref<ImporterMesh>> &r_meshes,
}
}
- mesh->optimize_indices_for_cache();
-
if (p_generate_lods) {
// Use normal merge/split angles that match the defaults used for 3D scene importing.
mesh->generate_lods(60.0f, {});
@@ -548,6 +546,8 @@ static Error _parse_obj(const String &p_path, List<Ref<ImporterMesh>> &r_meshes,
mesh->create_shadow_mesh();
}
+ mesh->optimize_indices();
+
if (p_single_mesh && mesh->get_surface_count() > 0) {
r_meshes.push_back(mesh);
}
diff --git a/editor/import/3d/resource_importer_scene.cpp b/editor/import/3d/resource_importer_scene.cpp
index 1e5a7dc72b..c62a3b0d52 100644
--- a/editor/import/3d/resource_importer_scene.cpp
+++ b/editor/import/3d/resource_importer_scene.cpp
@@ -2569,8 +2569,6 @@ Node *ResourceImporterScene::_generate_meshes(Node *p_node, const Dictionary &p_
}
}
- src_mesh_node->get_mesh()->optimize_indices_for_cache();
-
if (generate_lods) {
Array skin_pose_transform_array = _get_skinned_pose_transforms(src_mesh_node);
src_mesh_node->get_mesh()->generate_lods(merge_angle, skin_pose_transform_array);
@@ -2580,6 +2578,8 @@ Node *ResourceImporterScene::_generate_meshes(Node *p_node, const Dictionary &p_
src_mesh_node->get_mesh()->create_shadow_mesh();
}
+ src_mesh_node->get_mesh()->optimize_indices();
+
if (!save_to_file.is_empty()) {
Ref<Mesh> existing = ResourceCache::get_ref(save_to_file);
if (existing.is_valid()) {
diff --git a/editor/plugins/bone_map_editor_plugin.cpp b/editor/plugins/bone_map_editor_plugin.cpp
index 14fede662e..4e05d25bc2 100644
--- a/editor/plugins/bone_map_editor_plugin.cpp
+++ b/editor/plugins/bone_map_editor_plugin.cpp
@@ -861,7 +861,7 @@ void BoneMapper::auto_mapping_process(Ref<BoneMap> &p_bone_map) {
// 4-1. Guess Finger
int tips_index = -1;
- bool thumb_tips_size = 0;
+ bool thumb_tips_size = false;
bool named_finger_is_found = false;
LocalVector<String> fingers;
fingers.push_back("thumb|pollex");
@@ -996,7 +996,7 @@ void BoneMapper::auto_mapping_process(Ref<BoneMap> &p_bone_map) {
}
tips_index = -1;
- thumb_tips_size = 0;
+ thumb_tips_size = false;
named_finger_is_found = false;
if (right_hand_or_palm != -1) {
LocalVector<LocalVector<String>> right_fingers_map;
diff --git a/editor/plugins/font_config_plugin.cpp b/editor/plugins/font_config_plugin.cpp
index 2222e84e85..17c490370c 100644
--- a/editor/plugins/font_config_plugin.cpp
+++ b/editor/plugins/font_config_plugin.cpp
@@ -472,7 +472,7 @@ void EditorPropertyOTVariation::update_property() {
Vector3i range = supported.get_value_at_index(i);
EditorPropertyInteger *prop = memnew(EditorPropertyInteger);
- prop->setup(range.x, range.y, false, 1, false, false);
+ prop->setup(range.x, range.y, false, true, false, false);
prop->set_object_and_property(object.ptr(), "keys/" + itos(name_tag));
String name = TS->tag_to_name(name_tag);
diff --git a/editor/plugins/material_editor_plugin.cpp b/editor/plugins/material_editor_plugin.cpp
index cfd93e667b..c59f83a787 100644
--- a/editor/plugins/material_editor_plugin.cpp
+++ b/editor/plugins/material_editor_plugin.cpp
@@ -58,9 +58,15 @@ void MaterialEditor::gui_input(const Ref<InputEvent> &p_event) {
if (mm.is_valid() && (mm->get_button_mask().has_flag(MouseButtonMask::LEFT))) {
rot.x -= mm->get_relative().y * 0.01;
rot.y -= mm->get_relative().x * 0.01;
-
- rot.x = CLAMP(rot.x, -Math_PI / 2, Math_PI / 2);
+ if (quad_instance->is_visible()) {
+ // Clamp rotation so the quad is always visible.
+ const real_t limit = Math::deg_to_rad(80.0);
+ rot = rot.clampf(-limit, limit);
+ } else {
+ rot.x = CLAMP(rot.x, -Math_PI / 2, Math_PI / 2);
+ }
_update_rotation();
+ _store_rotation_metadata();
}
}
@@ -72,6 +78,7 @@ void MaterialEditor::_update_theme_item_cache() {
theme_cache.sphere_icon = get_editor_theme_icon(SNAME("MaterialPreviewSphere"));
theme_cache.box_icon = get_editor_theme_icon(SNAME("MaterialPreviewCube"));
+ theme_cache.quad_icon = get_editor_theme_icon(SNAME("MaterialPreviewQuad"));
theme_cache.checkerboard = get_editor_theme_icon(SNAME("Checkerboard"));
}
@@ -84,6 +91,7 @@ void MaterialEditor::_notification(int p_what) {
sphere_switch->set_button_icon(theme_cache.sphere_icon);
box_switch->set_button_icon(theme_cache.box_icon);
+ quad_switch->set_button_icon(theme_cache.quad_icon);
error_label->add_theme_color_override(SceneStringName(font_color), get_theme_color(SNAME("error_color"), EditorStringName(Editor)));
} break;
@@ -97,6 +105,18 @@ void MaterialEditor::_notification(int p_what) {
}
}
+void MaterialEditor::_set_rotation(real_t p_x_degrees, real_t p_y_degrees) {
+ rot.x = Math::deg_to_rad(p_x_degrees);
+ rot.y = Math::deg_to_rad(p_y_degrees);
+ _update_rotation();
+}
+
+// Store the rotation so it can persist when switching between materials.
+void MaterialEditor::_store_rotation_metadata() {
+ Vector2 rotation_degrees = Vector2(Math::rad_to_deg(rot.x), Math::rad_to_deg(rot.y));
+ EditorSettings::get_singleton()->set_project_metadata("inspector_options", "material_preview_rotation", rotation_degrees);
+}
+
void MaterialEditor::_update_rotation() {
Transform3D t;
t.basis.rotate(Vector3(0, 1, 0), -rot.y);
@@ -126,6 +146,7 @@ void MaterialEditor::edit(Ref<Material> p_material, const Ref<Environment> &p_en
vc->show();
sphere_instance->set_material_override(material);
box_instance->set_material_override(material);
+ quad_instance->set_material_override(material);
break;
default:
layout_error->show();
@@ -138,10 +159,6 @@ void MaterialEditor::edit(Ref<Material> p_material, const Ref<Environment> &p_en
} else {
hide();
}
-
- rot.x = Math::deg_to_rad(-15.0);
- rot.y = Math::deg_to_rad(30.0);
- _update_rotation();
}
void MaterialEditor::_on_light_1_switch_pressed() {
@@ -153,19 +170,36 @@ void MaterialEditor::_on_light_2_switch_pressed() {
}
void MaterialEditor::_on_sphere_switch_pressed() {
- box_instance->hide();
sphere_instance->show();
+ box_instance->hide();
+ quad_instance->hide();
box_switch->set_pressed(false);
- sphere_switch->set_pressed(true);
- EditorSettings::get_singleton()->set_project_metadata("inspector_options", "material_preview_on_sphere", true);
+ quad_switch->set_pressed(false);
+ _set_rotation(-15.0, 30.0);
+ _store_rotation_metadata();
+ EditorSettings::get_singleton()->set_project_metadata("inspector_options", "material_preview_mesh", "sphere");
}
void MaterialEditor::_on_box_switch_pressed() {
+ sphere_instance->hide();
box_instance->show();
+ quad_instance->hide();
+ sphere_switch->set_pressed(false);
+ quad_switch->set_pressed(false);
+ _set_rotation(-15.0, 30.0);
+ _store_rotation_metadata();
+ EditorSettings::get_singleton()->set_project_metadata("inspector_options", "material_preview_mesh", "box");
+}
+
+void MaterialEditor::_on_quad_switch_pressed() {
sphere_instance->hide();
- box_switch->set_pressed(true);
+ box_instance->hide();
+ quad_instance->show();
sphere_switch->set_pressed(false);
- EditorSettings::get_singleton()->set_project_metadata("inspector_options", "material_preview_on_sphere", false);
+ box_switch->set_pressed(false);
+ _set_rotation(0.0, 0.0);
+ _store_rotation_metadata();
+ EditorSettings::get_singleton()->set_project_metadata("inspector_options", "material_preview_mesh", "quad");
}
MaterialEditor::MaterialEditor() {
@@ -215,7 +249,7 @@ MaterialEditor::MaterialEditor() {
viewport = memnew(SubViewport);
Ref<World3D> world_3d;
world_3d.instantiate();
- viewport->set_world_3d(world_3d); //use own world
+ viewport->set_world_3d(world_3d); // Use own world.
vc->add_child(viewport);
viewport->set_disable_input(true);
viewport->set_transparent_background(true);
@@ -223,7 +257,7 @@ MaterialEditor::MaterialEditor() {
camera = memnew(Camera3D);
camera->set_transform(Transform3D(Basis(), Vector3(0, 0, 1.1)));
- // Use low field of view so the sphere/box is fully encompassed within the preview,
+ // Use low field of view so the sphere/box/quad is fully encompassed within the preview,
// without much distortion.
camera->set_perspective(20, 0.1, 10);
camera->make_current();
@@ -251,13 +285,19 @@ MaterialEditor::MaterialEditor() {
box_instance = memnew(MeshInstance3D);
rotation->add_child(box_instance);
- box_instance->set_transform(Transform3D() * 0.25);
+ quad_instance = memnew(MeshInstance3D);
+ rotation->add_child(quad_instance);
+
sphere_instance->set_transform(Transform3D() * 0.375);
+ box_instance->set_transform(Transform3D() * 0.25);
+ quad_instance->set_transform(Transform3D() * 0.375);
sphere_mesh.instantiate();
sphere_instance->set_mesh(sphere_mesh);
box_mesh.instantiate();
box_instance->set_mesh(box_mesh);
+ quad_mesh.instantiate();
+ quad_instance->set_mesh(quad_mesh);
set_custom_minimum_size(Size2(1, 150) * EDSCALE);
@@ -271,17 +311,21 @@ MaterialEditor::MaterialEditor() {
sphere_switch = memnew(Button);
sphere_switch->set_theme_type_variation("PreviewLightButton");
sphere_switch->set_toggle_mode(true);
- sphere_switch->set_pressed(true);
vb_shape->add_child(sphere_switch);
sphere_switch->connect(SceneStringName(pressed), callable_mp(this, &MaterialEditor::_on_sphere_switch_pressed));
box_switch = memnew(Button);
box_switch->set_theme_type_variation("PreviewLightButton");
box_switch->set_toggle_mode(true);
- box_switch->set_pressed(false);
vb_shape->add_child(box_switch);
box_switch->connect(SceneStringName(pressed), callable_mp(this, &MaterialEditor::_on_box_switch_pressed));
+ quad_switch = memnew(Button);
+ quad_switch->set_theme_type_variation("PreviewLightButton");
+ quad_switch->set_toggle_mode(true);
+ vb_shape->add_child(quad_switch);
+ quad_switch->connect(SceneStringName(pressed), callable_mp(this, &MaterialEditor::_on_quad_switch_pressed));
+
layout_3d->add_spacer();
VBoxContainer *vb_light = memnew(VBoxContainer);
@@ -301,14 +345,23 @@ MaterialEditor::MaterialEditor() {
vb_light->add_child(light_2_switch);
light_2_switch->connect(SceneStringName(pressed), callable_mp(this, &MaterialEditor::_on_light_2_switch_pressed));
- if (EditorSettings::get_singleton()->get_project_metadata("inspector_options", "material_preview_on_sphere", true)) {
+ String shape = EditorSettings::get_singleton()->get_project_metadata("inspector_options", "material_preview_mesh", "sphere");
+ if (shape == "sphere") {
box_instance->hide();
+ quad_instance->hide();
+ sphere_switch->set_pressed_no_signal(true);
+ } else if (shape == "box") {
+ sphere_instance->hide();
+ quad_instance->hide();
+ box_switch->set_pressed_no_signal(true);
} else {
- box_instance->show();
sphere_instance->hide();
- box_switch->set_pressed(true);
- sphere_switch->set_pressed(false);
+ box_instance->hide();
+ quad_switch->set_pressed_no_signal(true);
}
+
+ Vector2 stored_rot = EditorSettings::get_singleton()->get_project_metadata("inspector_options", "material_preview_rotation", Vector2());
+ _set_rotation(stored_rot.x, stored_rot.y);
}
///////////////////////
diff --git a/editor/plugins/material_editor_plugin.h b/editor/plugins/material_editor_plugin.h
index f4c60c47af..453f6c982f 100644
--- a/editor/plugins/material_editor_plugin.h
+++ b/editor/plugins/material_editor_plugin.h
@@ -64,6 +64,7 @@ class MaterialEditor : public Control {
Node3D *rotation = nullptr;
MeshInstance3D *sphere_instance = nullptr;
MeshInstance3D *box_instance = nullptr;
+ MeshInstance3D *quad_instance = nullptr;
DirectionalLight3D *light1 = nullptr;
DirectionalLight3D *light2 = nullptr;
Camera3D *camera = nullptr;
@@ -71,6 +72,7 @@ class MaterialEditor : public Control {
Ref<SphereMesh> sphere_mesh;
Ref<BoxMesh> box_mesh;
+ Ref<QuadMesh> quad_mesh;
VBoxContainer *layout_error = nullptr;
Label *error_label = nullptr;
@@ -82,6 +84,7 @@ class MaterialEditor : public Control {
Button *sphere_switch = nullptr;
Button *box_switch = nullptr;
+ Button *quad_switch = nullptr;
Button *light_1_switch = nullptr;
Button *light_2_switch = nullptr;
@@ -90,6 +93,7 @@ class MaterialEditor : public Control {
Ref<Texture2D> light_2_icon;
Ref<Texture2D> sphere_icon;
Ref<Texture2D> box_icon;
+ Ref<Texture2D> quad_icon;
Ref<Texture2D> checkerboard;
} theme_cache;
@@ -97,11 +101,14 @@ class MaterialEditor : public Control {
void _on_light_2_switch_pressed();
void _on_sphere_switch_pressed();
void _on_box_switch_pressed();
+ void _on_quad_switch_pressed();
protected:
virtual void _update_theme_item_cache() override;
void _notification(int p_what);
void gui_input(const Ref<InputEvent> &p_event) override;
+ void _set_rotation(real_t p_x_degrees, real_t p_y_degrees);
+ void _store_rotation_metadata();
void _update_rotation();
public:
diff --git a/editor/plugins/tiles/tile_map_layer_editor.cpp b/editor/plugins/tiles/tile_map_layer_editor.cpp
index 3b482fceff..43800d63c8 100644
--- a/editor/plugins/tiles/tile_map_layer_editor.cpp
+++ b/editor/plugins/tiles/tile_map_layer_editor.cpp
@@ -1552,6 +1552,7 @@ int TileMapLayerEditorTilesPlugin::_get_transformed_alternative(int p_alternativ
case TRANSFORM_ROTATE_RIGHT: {
// A matrix with every possible flip/transpose combination, sorted by what comes next when you rotate.
const LocalVector<bool> rotation_matrix = {
+ // NOLINTBEGIN(modernize-use-bool-literals)
0, 0, 0,
0, 1, 1,
1, 1, 0,
@@ -1560,6 +1561,7 @@ int TileMapLayerEditorTilesPlugin::_get_transformed_alternative(int p_alternativ
0, 0, 1,
0, 1, 0,
1, 1, 1
+ // NOLINTEND(modernize-use-bool-literals)
};
for (int i = 0; i < 8; i++) {
diff --git a/editor/plugins/tiles/tile_set_atlas_source_editor.h b/editor/plugins/tiles/tile_set_atlas_source_editor.h
index 8fa308b1fe..9b95f19491 100644
--- a/editor/plugins/tiles/tile_set_atlas_source_editor.h
+++ b/editor/plugins/tiles/tile_set_atlas_source_editor.h
@@ -93,7 +93,7 @@ public:
TileSetAtlasSourceEditor *tiles_set_atlas_source_editor = nullptr;
Ref<TileSetAtlasSource> tile_set_atlas_source;
- RBSet<TileSelection> tiles = RBSet<TileSelection>();
+ RBSet<TileSelection> tiles;
protected:
bool _set(const StringName &p_name, const Variant &p_value);
diff --git a/editor/project_converter_3_to_4.cpp b/editor/project_converter_3_to_4.cpp
index ddcaa6008b..b46b93215a 100644
--- a/editor/project_converter_3_to_4.cpp
+++ b/editor/project_converter_3_to_4.cpp
@@ -718,8 +718,9 @@ Vector<String> ProjectConverter3To4::check_for_files() {
directories_to_check.append(current_dir.path_join(file_name) + "/");
} else {
bool proper_extension = false;
- if (file_name.ends_with(".gd") || file_name.ends_with(".shader") || file_name.ends_with(".gdshader") || file_name.ends_with(".tscn") || file_name.ends_with(".tres") || file_name.ends_with(".godot") || file_name.ends_with(".cs") || file_name.ends_with(".csproj") || file_name.ends_with(".import"))
+ if (file_name.ends_with(".gd") || file_name.ends_with(".shader") || file_name.ends_with(".gdshader") || file_name.ends_with(".tscn") || file_name.ends_with(".tres") || file_name.ends_with(".godot") || file_name.ends_with(".cs") || file_name.ends_with(".csproj") || file_name.ends_with(".import")) {
proper_extension = true;
+ }
if (proper_extension) {
collected_files.append(current_dir.path_join(file_name));
@@ -1323,8 +1324,9 @@ Vector<String> ProjectConverter3To4::parse_arguments(const String &line) {
break;
};
case '"': {
- if (previous_character != '\\')
+ if (previous_character != '\\') {
is_inside_string = !is_inside_string;
+ }
}
}
previous_character = character;
diff --git a/editor/themes/editor_color_map.cpp b/editor/themes/editor_color_map.cpp
index f92cbd4f14..8a3f6a80e6 100644
--- a/editor/themes/editor_color_map.cpp
+++ b/editor/themes/editor_color_map.cpp
@@ -175,6 +175,8 @@ void EditorColorMap::create() {
add_conversion_exception("OverbrightIndicator");
add_conversion_exception("MaterialPreviewCube");
add_conversion_exception("MaterialPreviewSphere");
+ add_conversion_exception("MaterialPreviewQuad");
+
add_conversion_exception("MaterialPreviewLight1");
add_conversion_exception("MaterialPreviewLight2");
diff --git a/methods.py b/methods.py
index 6bd93cb3f5..c26b2c0baa 100644
--- a/methods.py
+++ b/methods.py
@@ -73,21 +73,13 @@ def print_error(*values: object) -> None:
def add_source_files_orig(self, sources, files, allow_gen=False):
# Convert string to list of absolute paths (including expanding wildcard)
- if isinstance(files, (str, bytes)):
- # Keep SCons project-absolute path as they are (no wildcard support)
- if files.startswith("#"):
- if "*" in files:
- print_error("Wildcards can't be expanded in SCons project-absolute path: '{}'".format(files))
- return
- files = [files]
- else:
- # Exclude .gen.cpp files from globbing, to avoid including obsolete ones.
- # They should instead be added manually.
- skip_gen_cpp = "*" in files
- dir_path = self.Dir(".").abspath
- files = sorted(glob.glob(dir_path + "/" + files))
- if skip_gen_cpp and not allow_gen:
- files = [f for f in files if not f.endswith(".gen.cpp")]
+ if isinstance(files, str):
+ # Exclude .gen.cpp files from globbing, to avoid including obsolete ones.
+ # They should instead be added manually.
+ skip_gen_cpp = "*" in files
+ files = self.Glob(files)
+ if skip_gen_cpp and not allow_gen:
+ files = [f for f in files if not str(f).endswith(".gen.cpp")]
# Add each path as compiled Object following environment (self) configuration
for path in files:
@@ -575,40 +567,7 @@ def detect_visual_c_compiler_version(tools_env):
vc_chosen_compiler_index = -1
vc_chosen_compiler_str = ""
- # Start with Pre VS 2017 checks which uses VCINSTALLDIR:
- if "VCINSTALLDIR" in tools_env:
- # print("Checking VCINSTALLDIR")
-
- # find() works with -1 so big ifs below are needed... the simplest solution, in fact
- # First test if amd64 and amd64_x86 compilers are present in the path
- vc_amd64_compiler_detection_index = tools_env["PATH"].find(tools_env["VCINSTALLDIR"] + "BIN\\amd64;")
- if vc_amd64_compiler_detection_index > -1:
- vc_chosen_compiler_index = vc_amd64_compiler_detection_index
- vc_chosen_compiler_str = "amd64"
-
- vc_amd64_x86_compiler_detection_index = tools_env["PATH"].find(tools_env["VCINSTALLDIR"] + "BIN\\amd64_x86;")
- if vc_amd64_x86_compiler_detection_index > -1 and (
- vc_chosen_compiler_index == -1 or vc_chosen_compiler_index > vc_amd64_x86_compiler_detection_index
- ):
- vc_chosen_compiler_index = vc_amd64_x86_compiler_detection_index
- vc_chosen_compiler_str = "amd64_x86"
-
- # Now check the 32 bit compilers
- vc_x86_compiler_detection_index = tools_env["PATH"].find(tools_env["VCINSTALLDIR"] + "BIN;")
- if vc_x86_compiler_detection_index > -1 and (
- vc_chosen_compiler_index == -1 or vc_chosen_compiler_index > vc_x86_compiler_detection_index
- ):
- vc_chosen_compiler_index = vc_x86_compiler_detection_index
- vc_chosen_compiler_str = "x86"
-
- vc_x86_amd64_compiler_detection_index = tools_env["PATH"].find(tools_env["VCINSTALLDIR"] + "BIN\\x86_amd64;")
- if vc_x86_amd64_compiler_detection_index > -1 and (
- vc_chosen_compiler_index == -1 or vc_chosen_compiler_index > vc_x86_amd64_compiler_detection_index
- ):
- vc_chosen_compiler_index = vc_x86_amd64_compiler_detection_index
- vc_chosen_compiler_str = "x86_amd64"
-
- # and for VS 2017 and newer we check VCTOOLSINSTALLDIR:
+ # VS 2017 and newer should set VCTOOLSINSTALLDIR
if "VCTOOLSINSTALLDIR" in tools_env:
# Newer versions have a different path available
vc_amd64_compiler_detection_index = (
diff --git a/modules/betsy/image_compress_betsy.h b/modules/betsy/image_compress_betsy.h
index 5b5ac51410..fbb23671dd 100644
--- a/modules/betsy/image_compress_betsy.h
+++ b/modules/betsy/image_compress_betsy.h
@@ -93,10 +93,10 @@ class BetsyCompressor : public Object {
RenderingDevice *compress_rd = nullptr;
RenderingContextDriver *compress_rcd = nullptr;
HashMap<String, BetsyShader> cached_shaders;
- RID src_sampler = RID();
+ RID src_sampler;
// Format-specific resources.
- RID dxt1_encoding_table_buffer = RID();
+ RID dxt1_encoding_table_buffer;
void _init();
void _assign_mt_ids(WorkerThreadPool::TaskID p_pump_task_id);
diff --git a/modules/camera/camera_feed_linux.cpp b/modules/camera/camera_feed_linux.cpp
index 51299d1d14..d764f6cb73 100644
--- a/modules/camera/camera_feed_linux.cpp
+++ b/modules/camera/camera_feed_linux.cpp
@@ -147,7 +147,7 @@ bool CameraFeedLinux::_request_buffers() {
}
buffers[i].length = buffer.length;
- buffers[i].start = mmap(NULL, buffer.length, PROT_READ | PROT_WRITE, MAP_SHARED, file_descriptor, buffer.m.offset);
+ buffers[i].start = mmap(nullptr, buffer.length, PROT_READ | PROT_WRITE, MAP_SHARED, file_descriptor, buffer.m.offset);
if (buffers[i].start == MAP_FAILED) {
for (unsigned int b = 0; b < i; b++) {
diff --git a/modules/fbx/fbx_document.cpp b/modules/fbx/fbx_document.cpp
index 4eef166d9b..40cabba207 100644
--- a/modules/fbx/fbx_document.cpp
+++ b/modules/fbx/fbx_document.cpp
@@ -371,21 +371,25 @@ Error FBXDocument::_parse_nodes(Ref<FBXState> p_state) {
// all skin clusters connected to the bone.
for (const ufbx_connection &child_conn : fbx_node->element.connections_src) {
ufbx_skin_cluster *child_cluster = ufbx_as_skin_cluster(child_conn.dst);
- if (!child_cluster)
+ if (!child_cluster) {
continue;
+ }
ufbx_skin_deformer *child_deformer = _find_skin_deformer(child_cluster);
- if (!child_deformer)
+ if (!child_deformer) {
continue;
+ }
// Found a skin cluster: Now iterate through all the skin clusters of the parent and
// try to find one that used by the same deformer.
for (const ufbx_connection &parent_conn : fbx_node->parent->element.connections_src) {
ufbx_skin_cluster *parent_cluster = ufbx_as_skin_cluster(parent_conn.dst);
- if (!parent_cluster)
+ if (!parent_cluster) {
continue;
+ }
ufbx_skin_deformer *parent_deformer = _find_skin_deformer(parent_cluster);
- if (parent_deformer != child_deformer)
+ if (parent_deformer != child_deformer) {
continue;
+ }
// Success: Found two skin clusters from the same deformer, now we can resolve the
// local bind pose from the difference between the two world-space bind poses.
@@ -1391,7 +1395,7 @@ Error FBXDocument::_parse_animations(Ref<FBXState> p_state) {
for (const ufbx_baked_node &fbx_baked_node : fbx_baked_anim->nodes) {
const GLTFNodeIndex node = fbx_baked_node.typed_id;
- GLTFAnimation::Track &track = animation->get_tracks()[node];
+ GLTFAnimation::NodeTrack &track = animation->get_node_tracks()[node];
for (const ufbx_baked_vec3 &key : fbx_baked_node.translation_keys) {
track.position_track.times.push_back(float(key.time));
@@ -1781,8 +1785,8 @@ void FBXDocument::_import_animation(Ref<FBXState> p_state, AnimationPlayer *p_an
double anim_start_offset = p_trimming ? double(additional_animation_data["time_begin"]) : 0.0;
- for (const KeyValue<int, GLTFAnimation::Track> &track_i : anim->get_tracks()) {
- const GLTFAnimation::Track &track = track_i.value;
+ for (const KeyValue<int, GLTFAnimation::NodeTrack> &track_i : anim->get_node_tracks()) {
+ const GLTFAnimation::NodeTrack &track = track_i.value;
//need to find the path: for skeletons, weight tracks will affect the mesh
NodePath node_path;
//for skeletons, transform tracks always affect bones
diff --git a/modules/gdscript/gdscript_disassembler.cpp b/modules/gdscript/gdscript_disassembler.cpp
index a829f516b9..7d72c6ea2c 100644
--- a/modules/gdscript/gdscript_disassembler.cpp
+++ b/modules/gdscript/gdscript_disassembler.cpp
@@ -792,8 +792,9 @@ void GDScriptFunction::disassemble(const Vector<String> &p_code_lines) const {
text += method->get_name();
text += "(";
for (int i = 0; i < argc; i++) {
- if (i > 0)
+ if (i > 0) {
text += ", ";
+ }
text += DADDR(1 + i);
}
text += ")";
@@ -835,8 +836,9 @@ void GDScriptFunction::disassemble(const Vector<String> &p_code_lines) const {
text += method->get_name();
text += "(";
for (int i = 0; i < argc; i++) {
- if (i > 0)
+ if (i > 0) {
text += ", ";
+ }
text += DADDR(1 + i);
}
text += ")";
diff --git a/modules/gltf/config.py b/modules/gltf/config.py
index 67233db579..823b8dbec2 100644
--- a/modules/gltf/config.py
+++ b/modules/gltf/config.py
@@ -20,6 +20,7 @@ def get_doc_classes():
"GLTFLight",
"GLTFMesh",
"GLTFNode",
+ "GLTFObjectModelProperty",
"GLTFPhysicsBody",
"GLTFPhysicsShape",
"GLTFSkeleton",
diff --git a/modules/gltf/doc_classes/GLTFAccessor.xml b/modules/gltf/doc_classes/GLTFAccessor.xml
index bc142797a3..04fa2a9835 100644
--- a/modules/gltf/doc_classes/GLTFAccessor.xml
+++ b/modules/gltf/doc_classes/GLTFAccessor.xml
@@ -22,7 +22,7 @@
The offset relative to the start of the buffer view in bytes.
</member>
<member name="component_type" type="int" setter="set_component_type" getter="get_component_type" default="0">
- The glTF component type as an enum. Possible values are 5120 for "BYTE", 5121 for "UNSIGNED_BYTE", 5122 for "SHORT", 5123 for "UNSIGNED_SHORT", 5125 for "UNSIGNED_INT", and 5126 for "FLOAT". A value of 5125 or "UNSIGNED_INT" must not be used for any accessor that is not referenced by mesh.primitive.indices.
+ The glTF component type as an enum. See [enum GLTFComponentType] for possible values. Within the core glTF specification, a value of 5125 or "UNSIGNED_INT" must not be used for any accessor that is not referenced by mesh.primitive.indices.
</member>
<member name="count" type="int" setter="set_count" getter="get_count" default="0">
The number of elements referenced by this accessor.
@@ -80,5 +80,41 @@
<constant name="TYPE_MAT4" value="6" enum="GLTFAccessorType">
Accessor type "MAT4". For the glTF object model, this maps to "float4x4", represented in the glTF JSON as an array of sixteen floats.
</constant>
+ <constant name="COMPONENT_TYPE_NONE" value="0" enum="GLTFComponentType">
+ Component type "NONE". This is not a valid component type, and is used to indicate that the component type is not set.
+ </constant>
+ <constant name="COMPONENT_TYPE_SIGNED_BYTE" value="5120" enum="GLTFComponentType">
+ Component type "BYTE". The value is [code]0x1400[/code] which comes from OpenGL. This indicates data is stored in 1-byte or 8-bit signed integers. This is a core part of the glTF specification.
+ </constant>
+ <constant name="COMPONENT_TYPE_UNSIGNED_BYTE" value="5121" enum="GLTFComponentType">
+ Component type "UNSIGNED_BYTE". The value is [code]0x1401[/code] which comes from OpenGL. This indicates data is stored in 1-byte or 8-bit unsigned integers. This is a core part of the glTF specification.
+ </constant>
+ <constant name="COMPONENT_TYPE_SIGNED_SHORT" value="5122" enum="GLTFComponentType">
+ Component type "SHORT". The value is [code]0x1402[/code] which comes from OpenGL. This indicates data is stored in 2-byte or 16-bit signed integers. This is a core part of the glTF specification.
+ </constant>
+ <constant name="COMPONENT_TYPE_UNSIGNED_SHORT" value="5123" enum="GLTFComponentType">
+ Component type "UNSIGNED_SHORT". The value is [code]0x1403[/code] which comes from OpenGL. This indicates data is stored in 2-byte or 16-bit unsigned integers. This is a core part of the glTF specification.
+ </constant>
+ <constant name="COMPONENT_TYPE_SIGNED_INT" value="5124" enum="GLTFComponentType">
+ Component type "INT". The value is [code]0x1404[/code] which comes from OpenGL. This indicates data is stored in 4-byte or 32-bit signed integers. This is NOT a core part of the glTF specification, and may not be supported by all glTF importers. May be used by some extensions including [code]KHR_interactivity[/code].
+ </constant>
+ <constant name="COMPONENT_TYPE_UNSIGNED_INT" value="5125" enum="GLTFComponentType">
+ Component type "UNSIGNED_INT". The value is [code]0x1405[/code] which comes from OpenGL. This indicates data is stored in 4-byte or 32-bit unsigned integers. This is a core part of the glTF specification.
+ </constant>
+ <constant name="COMPONENT_TYPE_SINGLE_FLOAT" value="5126" enum="GLTFComponentType">
+ Component type "FLOAT". The value is [code]0x1406[/code] which comes from OpenGL. This indicates data is stored in 4-byte or 32-bit floating point numbers. This is a core part of the glTF specification.
+ </constant>
+ <constant name="COMPONENT_TYPE_DOUBLE_FLOAT" value="5130" enum="GLTFComponentType">
+ Component type "DOUBLE". The value is [code]0x140A[/code] which comes from OpenGL. This indicates data is stored in 8-byte or 64-bit floating point numbers. This is NOT a core part of the glTF specification, and may not be supported by all glTF importers. May be used by some extensions including [code]KHR_interactivity[/code].
+ </constant>
+ <constant name="COMPONENT_TYPE_HALF_FLOAT" value="5131" enum="GLTFComponentType">
+ Component type "HALF_FLOAT". The value is [code]0x140B[/code] which comes from OpenGL. This indicates data is stored in 2-byte or 16-bit floating point numbers. This is NOT a core part of the glTF specification, and may not be supported by all glTF importers. May be used by some extensions including [code]KHR_interactivity[/code].
+ </constant>
+ <constant name="COMPONENT_TYPE_SIGNED_LONG" value="5134" enum="GLTFComponentType">
+ Component type "LONG". The value is [code]0x140E[/code] which comes from OpenGL. This indicates data is stored in 8-byte or 64-bit signed integers. This is NOT a core part of the glTF specification, and may not be supported by all glTF importers. May be used by some extensions including [code]KHR_interactivity[/code].
+ </constant>
+ <constant name="COMPONENT_TYPE_UNSIGNED_LONG" value="5135" enum="GLTFComponentType">
+ Component type "UNSIGNED_LONG". The value is [code]0x140F[/code] which comes from OpenGL. This indicates data is stored in 8-byte or 64-bit unsigned integers. This is NOT a core part of the glTF specification, and may not be supported by all glTF importers. May be used by some extensions including [code]KHR_interactivity[/code].
+ </constant>
</constants>
</class>
diff --git a/modules/gltf/doc_classes/GLTFDocument.xml b/modules/gltf/doc_classes/GLTFDocument.xml
index 10534594d3..ffc3ab926c 100644
--- a/modules/gltf/doc_classes/GLTFDocument.xml
+++ b/modules/gltf/doc_classes/GLTFDocument.xml
@@ -45,6 +45,16 @@
Takes a Godot Engine scene node and exports it and its descendants to the given [GLTFState] object through the [param state] parameter.
</description>
</method>
+ <method name="export_object_model_property" qualifiers="static">
+ <return type="GLTFObjectModelProperty" />
+ <param index="0" name="state" type="GLTFState" />
+ <param index="1" name="node_path" type="NodePath" />
+ <param index="2" name="godot_node" type="Node" />
+ <param index="3" name="gltf_node_index" type="int" />
+ <description>
+ Determines a mapping between the given Godot [param node_path] and the corresponding glTF Object Model JSON pointer(s) in the generated glTF file. The details of this mapping are returned in a [GLTFObjectModelProperty] object. Additional mappings can be supplied via the [method GLTFDocumentExtension._import_object_model_property] callback method.
+ </description>
+ </method>
<method name="generate_buffer">
<return type="PackedByteArray" />
<param index="0" name="state" type="GLTFState" />
@@ -70,6 +80,14 @@
[b]Note:[/b] If this method is run before a GLTFDocumentExtension is registered, its extensions won't be included in the list. Be sure to only run this method after all extensions are registered. If you run this when the engine starts, consider waiting a frame before calling this method to ensure all extensions are registered.
</description>
</method>
+ <method name="import_object_model_property" qualifiers="static">
+ <return type="GLTFObjectModelProperty" />
+ <param index="0" name="state" type="GLTFState" />
+ <param index="1" name="json_pointer" type="String" />
+ <description>
+ Determines a mapping between the given glTF Object Model [param json_pointer] and the corresponding Godot node path(s) in the generated Godot scene. The details of this mapping are returned in a [GLTFObjectModelProperty] object. Additional mappings can be supplied via the [method GLTFDocumentExtension._export_object_model_property] callback method.
+ </description>
+ </method>
<method name="register_gltf_document_extension" qualifiers="static">
<return type="void" />
<param index="0" name="extension" type="GLTFDocumentExtension" />
diff --git a/modules/gltf/doc_classes/GLTFDocumentExtension.xml b/modules/gltf/doc_classes/GLTFDocumentExtension.xml
index b33e296e1c..8fcb925a48 100644
--- a/modules/gltf/doc_classes/GLTFDocumentExtension.xml
+++ b/modules/gltf/doc_classes/GLTFDocumentExtension.xml
@@ -33,6 +33,20 @@
This method can be used to modify the final JSON of each node. Data should be primarily stored in [param gltf_node] prior to serializing the JSON, but the original Godot [param node] is also provided if available. The node may be null if not available, such as when exporting glTF data not generated from a Godot scene.
</description>
</method>
+ <method name="_export_object_model_property" qualifiers="virtual">
+ <return type="GLTFObjectModelProperty" />
+ <param index="0" name="state" type="GLTFState" />
+ <param index="1" name="node_path" type="NodePath" />
+ <param index="2" name="godot_node" type="Node" />
+ <param index="3" name="gltf_node_index" type="int" />
+ <param index="4" name="target_object" type="Object" />
+ <param index="5" name="target_depth" type="int" />
+ <description>
+ Part of the export process. Allows GLTFDocumentExtension classes to provide mappings for properties of nodes in the Godot scene tree, to JSON pointers to glTF properties, as defined by the glTF object model.
+ Returns a [GLTFObjectModelProperty] instance that defines how the property should be mapped. If your extension can't handle the property, return null, or an instance without any JSON pointers (see [method GLTFObjectModelProperty.has_json_pointers]). You should use [method GLTFObjectModelProperty.set_types] to set the types, and set the JSON pointer(s) using the [member GLTFObjectModelProperty.json_pointers] property.
+ The parameters provide context for the property, including the NodePath, the Godot node, the GLTF node index, and the target object. The [param target_object] will be equal to [param godot_node] if no sub-object can be found, otherwise it will point to a sub-object. For example, if the path is [code]^"A/B/C/MeshInstance3D:mesh:surface_0/material:emission_intensity"[/code], it will get the node, then the mesh, and then the material, so [param target_object] will be the [Material] resource, and [param target_depth] will be 2 because 2 levels were traversed to get to the target.
+ </description>
+ </method>
<method name="_export_post" qualifiers="virtual">
<return type="int" enum="Error" />
<param index="0" name="state" type="GLTFState" />
@@ -109,6 +123,17 @@
This method can be used to make modifications to each of the generated Godot scene nodes.
</description>
</method>
+ <method name="_import_object_model_property" qualifiers="virtual">
+ <return type="GLTFObjectModelProperty" />
+ <param index="0" name="state" type="GLTFState" />
+ <param index="1" name="split_json_pointer" type="PackedStringArray" />
+ <param index="2" name="partial_paths" type="NodePath[]" />
+ <description>
+ Part of the import process. Allows GLTFDocumentExtension classes to provide mappings for JSON pointers to glTF properties, as defined by the glTF object model, to properties of nodes in the Godot scene tree.
+ Returns a [GLTFObjectModelProperty] instance that defines how the property should be mapped. If your extension can't handle the property, return null, or an instance without any NodePaths (see [method GLTFObjectModelProperty.has_node_paths]). You should use [method GLTFObjectModelProperty.set_types] to set the types, and [method GLTFObjectModelProperty.append_path_to_property] function is useful for most simple cases.
+ In many cases, [param partial_paths] will contain the start of a path, allowing the extension to complete the path. For example, for [code]/nodes/3/extensions/MY_ext/prop[/code], Godot will pass you a NodePath that leads to node 3, so the GLTFDocumentExtension class only needs to resolve the last [code]MY_ext/prop[/code] part of the path. In this example, the extension should check [code]split.size() &gt; 4 and split[0] == "nodes" and split[2] == "extensions" and split[3] == "MY_ext"[/code] at the start of the function to check if this JSON pointer applies to it, then it can use [param partial_paths] and handle [code]split[4][/code].
+ </description>
+ </method>
<method name="_import_post" qualifiers="virtual">
<return type="int" enum="Error" />
<param index="0" name="state" type="GLTFState" />
diff --git a/modules/gltf/doc_classes/GLTFNode.xml b/modules/gltf/doc_classes/GLTFNode.xml
index a242a0d1d8..eb92723a06 100644
--- a/modules/gltf/doc_classes/GLTFNode.xml
+++ b/modules/gltf/doc_classes/GLTFNode.xml
@@ -27,6 +27,15 @@
The argument should be the [GLTFDocumentExtension] name (does not have to match the extension name in the glTF file), and the return value can be anything you set. If nothing was set, the return value is null.
</description>
</method>
+ <method name="get_scene_node_path">
+ <return type="NodePath" />
+ <param index="0" name="gltf_state" type="GLTFState" />
+ <param index="1" name="handle_skeletons" type="bool" default="true" />
+ <description>
+ Returns the [NodePath] that this GLTF node will have in the Godot scene tree after being imported. This is useful when importing glTF object model pointers with [GLTFObjectModelProperty], for handling extensions such as [code]KHR_animation_pointer[/code] or [code]KHR_interactivity[/code].
+ If [param handle_skeletons] is true, paths to skeleton bone glTF nodes will be resolved properly. For example, a path that would be [code]^"A/B/C/Bone1/Bone2/Bone3"[/code] if false will become [code]^"A/B/C/Skeleton3D:Bone3"[/code].
+ </description>
+ </method>
<method name="set_additional_data">
<return type="void" />
<param index="0" name="extension_name" type="StringName" />
diff --git a/modules/gltf/doc_classes/GLTFObjectModelProperty.xml b/modules/gltf/doc_classes/GLTFObjectModelProperty.xml
new file mode 100644
index 0000000000..e983269ccc
--- /dev/null
+++ b/modules/gltf/doc_classes/GLTFObjectModelProperty.xml
@@ -0,0 +1,114 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="GLTFObjectModelProperty" inherits="RefCounted" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
+ <brief_description>
+ Describes how to access a property as defined in the glTF object model.
+ </brief_description>
+ <description>
+ GLTFObjectModelProperty defines a mapping between a property in the glTF object model and a NodePath in the Godot scene tree. This can be used to animate properties in a glTF file using the [code]KHR_animation_pointer[/code] extension, or to access them through an engine-agnostic script such as a behavior graph as defined by the [code]KHR_interactivity[/code] extension.
+ The glTF property is identified by JSON pointer(s) stored in [member json_pointers], while the Godot property it maps to is defined by [member node_paths]. In most cases [member json_pointers] and [member node_paths] will each only have one item, but in some cases a single glTF JSON pointer will map to multiple Godot properties, or a single Godot property will be mapped to multiple glTF JSON pointers, or it might be a many-to-many relationship.
+ [Expression] objects can be used to define conversions between the data, such as when glTF defines an angle in radians and Godot uses degrees. The [member object_model_type] property defines the type of data stored in the glTF file as defined by the object model, see [enum GLTFObjectModelType] for possible values.
+ </description>
+ <tutorials>
+ <link title="GLTF Object Model">https://github.com/KhronosGroup/glTF/blob/main/specification/2.0/ObjectModel.adoc</link>
+ <link title="KHR_animation_pointer GLTF extension">https://github.com/KhronosGroup/glTF/tree/main/extensions/2.0/Khronos/KHR_animation_pointer</link>
+ </tutorials>
+ <methods>
+ <method name="append_node_path">
+ <return type="void" />
+ <param index="0" name="node_path" type="NodePath" />
+ <description>
+ Appends a [NodePath] to [member node_paths]. This can be used by [GLTFDocumentExtension] classes to define how a glTF object model property maps to a Godot property, or multiple Godot properties. Prefer using [method append_path_to_property] for simple cases. Be sure to also call [method set_types] once (the order does not matter).
+ </description>
+ </method>
+ <method name="append_path_to_property">
+ <return type="void" />
+ <param index="0" name="node_path" type="NodePath" />
+ <param index="1" name="prop_name" type="StringName" />
+ <description>
+ High-level wrapper over [method append_node_path] that handles the most common cases. It constructs a new [NodePath] using [param node_path] as a base and appends [param prop_name] to the subpath. Be sure to also call [method set_types] once (the order does not matter).
+ </description>
+ </method>
+ <method name="get_accessor_type" qualifiers="const">
+ <return type="int" enum="GLTFAccessor.GLTFAccessorType" />
+ <description>
+ The GLTF accessor type associated with this property's [member object_model_type]. See [member GLTFAccessor.accessor_type] for possible values, and see [enum GLTFObjectModelType] for how the object model type maps to accessor types.
+ </description>
+ </method>
+ <method name="has_json_pointers" qualifiers="const">
+ <return type="bool" />
+ <description>
+ Returns [code]true[/code] if [member json_pointers] is not empty. This is used during export to determine if a [GLTFObjectModelProperty] can handle converting a Godot property to a glTF object model property.
+ </description>
+ </method>
+ <method name="has_node_paths" qualifiers="const">
+ <return type="bool" />
+ <description>
+ Returns [code]true[/code] if [member node_paths] is not empty. This is used during import to determine if a [GLTFObjectModelProperty] can handle converting a glTF object model property to a Godot property.
+ </description>
+ </method>
+ <method name="set_types">
+ <return type="void" />
+ <param index="0" name="variant_type" type="int" enum="Variant.Type" />
+ <param index="1" name="obj_model_type" type="int" enum="GLTFObjectModelProperty.GLTFObjectModelType" />
+ <description>
+ Sets the [member variant_type] and [member object_model_type] properties. This is a convenience method to set both properties at once, since they are almost always known at the same time. This method should be called once. Calling it again with the same values will have no effect.
+ </description>
+ </method>
+ </methods>
+ <members>
+ <member name="gltf_to_godot_expression" type="Expression" setter="set_gltf_to_godot_expression" getter="get_gltf_to_godot_expression">
+ If set, this [Expression] will be used to convert the property value from the glTF object model to the value expected by the Godot property. This is useful when the glTF object model uses a different unit system, or when the data needs to be transformed in some way. If [code]null[/code], the value will be copied as-is.
+ </member>
+ <member name="godot_to_gltf_expression" type="Expression" setter="set_godot_to_gltf_expression" getter="get_godot_to_gltf_expression">
+ If set, this [Expression] will be used to convert the property value from the Godot property to the value expected by the glTF object model. This is useful when the glTF object model uses a different unit system, or when the data needs to be transformed in some way. If [code]null[/code], the value will be copied as-is.
+ </member>
+ <member name="json_pointers" type="PackedStringArray[]" setter="set_json_pointers" getter="get_json_pointers" default="[]">
+ The glTF object model JSON pointers used to identify the property in the glTF object model. In most cases, there will be only one item in this array, but niche cases may require multiple pointers. The items are themselves arrays which represent the JSON pointer split into its components.
+ </member>
+ <member name="node_paths" type="NodePath[]" setter="set_node_paths" getter="get_node_paths" default="[]">
+ An array of [NodePath]s that point to a property, or multiple properties, in the Godot scene tree. On import, this will either be set by [GLTFDocument], or by a [GLTFDocumentExtension] class. For simple cases, use [method append_path_to_property] to add properties to this array.
+ In most cases [member node_paths] will only have one item, but in some cases a single glTF JSON pointer will map to multiple Godot properties. For example, a [GLTFCamera] or [GLTFLight] used on multiple glTF nodes will be represented by multiple Godot nodes.
+ </member>
+ <member name="object_model_type" type="int" setter="set_object_model_type" getter="get_object_model_type" enum="GLTFObjectModelProperty.GLTFObjectModelType" default="0">
+ The type of data stored in the glTF file as defined by the object model. This is a superset of the available accessor types, and determines the accessor type. See [enum GLTFObjectModelType] for possible values.
+ </member>
+ <member name="variant_type" type="int" setter="set_variant_type" getter="get_variant_type" enum="Variant.Type" default="0">
+ The type of data stored in the Godot property. This is the type of the property that the [member node_paths] point to.
+ </member>
+ </members>
+ <constants>
+ <constant name="GLTF_OBJECT_MODEL_TYPE_UNKNOWN" value="0" enum="GLTFObjectModelType">
+ Unknown or not set object model type. If the object model type is set to this value, the real type still needs to be determined.
+ </constant>
+ <constant name="GLTF_OBJECT_MODEL_TYPE_BOOL" value="1" enum="GLTFObjectModelType">
+ Object model type "bool". Represented in the glTF JSON as a boolean, and encoded in a [GLTFAccessor] as "SCALAR". When encoded in an accessor, a value of 0 is false, and any other value is true.
+ </constant>
+ <constant name="GLTF_OBJECT_MODEL_TYPE_FLOAT" value="2" enum="GLTFObjectModelType">
+ Object model type "float". Represented in the glTF JSON as a number, and encoded in a [GLTFAccessor] as "SCALAR".
+ </constant>
+ <constant name="GLTF_OBJECT_MODEL_TYPE_FLOAT_ARRAY" value="3" enum="GLTFObjectModelType">
+ Object model type "float[lb][rb]". Represented in the glTF JSON as an array of numbers, and encoded in a [GLTFAccessor] as "SCALAR".
+ </constant>
+ <constant name="GLTF_OBJECT_MODEL_TYPE_FLOAT2" value="4" enum="GLTFObjectModelType">
+ Object model type "float2". Represented in the glTF JSON as an array of two numbers, and encoded in a [GLTFAccessor] as "VEC2".
+ </constant>
+ <constant name="GLTF_OBJECT_MODEL_TYPE_FLOAT3" value="5" enum="GLTFObjectModelType">
+ Object model type "float3". Represented in the glTF JSON as an array of three numbers, and encoded in a [GLTFAccessor] as "VEC3".
+ </constant>
+ <constant name="GLTF_OBJECT_MODEL_TYPE_FLOAT4" value="6" enum="GLTFObjectModelType">
+ Object model type "float4". Represented in the glTF JSON as an array of four numbers, and encoded in a [GLTFAccessor] as "VEC4".
+ </constant>
+ <constant name="GLTF_OBJECT_MODEL_TYPE_FLOAT2X2" value="7" enum="GLTFObjectModelType">
+ Object model type "float2x2". Represented in the glTF JSON as an array of four numbers, and encoded in a [GLTFAccessor] as "MAT2".
+ </constant>
+ <constant name="GLTF_OBJECT_MODEL_TYPE_FLOAT3X3" value="8" enum="GLTFObjectModelType">
+ Object model type "float3x3". Represented in the glTF JSON as an array of nine numbers, and encoded in a [GLTFAccessor] as "MAT3".
+ </constant>
+ <constant name="GLTF_OBJECT_MODEL_TYPE_FLOAT4X4" value="9" enum="GLTFObjectModelType">
+ Object model type "float4x4". Represented in the glTF JSON as an array of sixteen numbers, and encoded in a [GLTFAccessor] as "MAT4".
+ </constant>
+ <constant name="GLTF_OBJECT_MODEL_TYPE_INT" value="10" enum="GLTFObjectModelType">
+ Object model type "int". Represented in the glTF JSON as a number, and encoded in a [GLTFAccessor] as "SCALAR". The range of values is limited to signed integers. For [code]KHR_interactivity[/code], only 32-bit integers are supported.
+ </constant>
+ </constants>
+</class>
diff --git a/modules/gltf/editor/editor_scene_importer_blend.cpp b/modules/gltf/editor/editor_scene_importer_blend.cpp
index 95739ef4b7..d3c853774f 100644
--- a/modules/gltf/editor/editor_scene_importer_blend.cpp
+++ b/modules/gltf/editor/editor_scene_importer_blend.cpp
@@ -321,6 +321,8 @@ Node *EditorSceneFormatImporterBlend::import_scene(const String &p_path, uint32_
state->set_import_as_skeleton_bones(true);
}
state->set_scene_name(blend_basename);
+ state->set_extract_path(p_path.get_base_dir());
+ state->set_extract_prefix(blend_basename);
err = gltf->append_from_file(sink.get_basename() + ".gltf", state, p_flags, base_dir);
if (err != OK) {
if (r_err) {
diff --git a/modules/gltf/extensions/gltf_document_extension.cpp b/modules/gltf/extensions/gltf_document_extension.cpp
index df98a1fa1a..6faab3a8b2 100644
--- a/modules/gltf/extensions/gltf_document_extension.cpp
+++ b/modules/gltf/extensions/gltf_document_extension.cpp
@@ -40,6 +40,7 @@ void GLTFDocumentExtension::_bind_methods() {
GDVIRTUAL_BIND(_parse_image_data, "state", "image_data", "mime_type", "ret_image");
GDVIRTUAL_BIND(_get_image_file_extension);
GDVIRTUAL_BIND(_parse_texture_json, "state", "texture_json", "ret_gltf_texture");
+ GDVIRTUAL_BIND(_import_object_model_property, "state", "split_json_pointer", "partial_paths");
GDVIRTUAL_BIND(_import_post_parse, "state");
GDVIRTUAL_BIND(_import_pre_generate, "state");
GDVIRTUAL_BIND(_generate_scene_node, "state", "gltf_node", "scene_parent");
@@ -50,6 +51,7 @@ void GLTFDocumentExtension::_bind_methods() {
GDVIRTUAL_BIND(_convert_scene_node, "state", "gltf_node", "scene_node");
GDVIRTUAL_BIND(_export_post_convert, "state", "root");
GDVIRTUAL_BIND(_export_preserialize, "state");
+ GDVIRTUAL_BIND(_export_object_model_property, "state", "node_path", "godot_node", "gltf_node_index", "target_object", "target_depth");
GDVIRTUAL_BIND(_get_saveable_image_formats);
GDVIRTUAL_BIND(_serialize_image_to_bytes, "state", "image", "image_dict", "image_format", "lossy_quality");
GDVIRTUAL_BIND(_save_image_at_path, "state", "image", "file_path", "image_format", "lossy_quality");
@@ -102,6 +104,13 @@ Error GLTFDocumentExtension::parse_texture_json(Ref<GLTFState> p_state, const Di
return err;
}
+Ref<GLTFObjectModelProperty> GLTFDocumentExtension::import_object_model_property(Ref<GLTFState> p_state, const PackedStringArray &p_split_json_pointer, const TypedArray<NodePath> &p_partial_paths) {
+ Ref<GLTFObjectModelProperty> ret;
+ ERR_FAIL_COND_V(p_state.is_null(), ret);
+ GDVIRTUAL_CALL(_import_object_model_property, p_state, p_split_json_pointer, p_partial_paths, ret);
+ return ret;
+}
+
Error GLTFDocumentExtension::import_post_parse(Ref<GLTFState> p_state) {
ERR_FAIL_COND_V(p_state.is_null(), ERR_INVALID_PARAMETER);
Error err = OK;
@@ -171,6 +180,15 @@ Error GLTFDocumentExtension::export_preserialize(Ref<GLTFState> p_state) {
return err;
}
+Ref<GLTFObjectModelProperty> GLTFDocumentExtension::export_object_model_property(Ref<GLTFState> p_state, const NodePath &p_node_path, const Node *p_godot_node, GLTFNodeIndex p_gltf_node_index, const Object *p_target_object, int p_target_depth) {
+ Ref<GLTFObjectModelProperty> ret;
+ ERR_FAIL_COND_V(p_state.is_null(), ret);
+ ERR_FAIL_NULL_V(p_godot_node, ret);
+ ERR_FAIL_NULL_V(p_target_object, ret);
+ GDVIRTUAL_CALL(_export_object_model_property, p_state, p_node_path, p_godot_node, p_gltf_node_index, p_target_object, p_target_depth, ret);
+ return ret;
+}
+
Vector<String> GLTFDocumentExtension::get_saveable_image_formats() {
Vector<String> ret;
GDVIRTUAL_CALL(_get_saveable_image_formats, ret);
diff --git a/modules/gltf/extensions/gltf_document_extension.h b/modules/gltf/extensions/gltf_document_extension.h
index 49405ec384..917f7e8a71 100644
--- a/modules/gltf/extensions/gltf_document_extension.h
+++ b/modules/gltf/extensions/gltf_document_extension.h
@@ -51,6 +51,7 @@ public:
virtual Error parse_image_data(Ref<GLTFState> p_state, const PackedByteArray &p_image_data, const String &p_mime_type, Ref<Image> r_image);
virtual String get_image_file_extension();
virtual Error parse_texture_json(Ref<GLTFState> p_state, const Dictionary &p_texture_json, Ref<GLTFTexture> r_gltf_texture);
+ virtual Ref<GLTFObjectModelProperty> import_object_model_property(Ref<GLTFState> p_state, const PackedStringArray &p_split_json_pointer, const TypedArray<NodePath> &p_partial_paths);
virtual Error import_post_parse(Ref<GLTFState> p_state);
virtual Error import_pre_generate(Ref<GLTFState> p_state);
virtual Node3D *generate_scene_node(Ref<GLTFState> p_state, Ref<GLTFNode> p_gltf_node, Node *p_scene_parent);
@@ -61,6 +62,7 @@ public:
virtual void convert_scene_node(Ref<GLTFState> p_state, Ref<GLTFNode> p_gltf_node, Node *p_scene_node);
virtual Error export_post_convert(Ref<GLTFState> p_state, Node *p_root);
virtual Error export_preserialize(Ref<GLTFState> p_state);
+ virtual Ref<GLTFObjectModelProperty> export_object_model_property(Ref<GLTFState> p_state, const NodePath &p_node_path, const Node *p_godot_node, GLTFNodeIndex p_gltf_node_index, const Object *p_target_object, int p_target_depth);
virtual Vector<String> get_saveable_image_formats();
virtual PackedByteArray serialize_image_to_bytes(Ref<GLTFState> p_state, Ref<Image> p_image, Dictionary p_image_dict, const String &p_image_format, float p_lossy_quality);
virtual Error save_image_at_path(Ref<GLTFState> p_state, Ref<Image> p_image, const String &p_file_path, const String &p_image_format, float p_lossy_quality);
@@ -75,6 +77,7 @@ public:
GDVIRTUAL4R(Error, _parse_image_data, Ref<GLTFState>, PackedByteArray, String, Ref<Image>);
GDVIRTUAL0R(String, _get_image_file_extension);
GDVIRTUAL3R(Error, _parse_texture_json, Ref<GLTFState>, Dictionary, Ref<GLTFTexture>);
+ GDVIRTUAL3R(Ref<GLTFObjectModelProperty>, _import_object_model_property, Ref<GLTFState>, PackedStringArray, TypedArray<NodePath>);
GDVIRTUAL1R(Error, _import_post_parse, Ref<GLTFState>);
GDVIRTUAL1R(Error, _import_pre_generate, Ref<GLTFState>);
GDVIRTUAL3R(Node3D *, _generate_scene_node, Ref<GLTFState>, Ref<GLTFNode>, Node *);
@@ -85,6 +88,7 @@ public:
GDVIRTUAL3(_convert_scene_node, Ref<GLTFState>, Ref<GLTFNode>, Node *);
GDVIRTUAL2R(Error, _export_post_convert, Ref<GLTFState>, Node *);
GDVIRTUAL1R(Error, _export_preserialize, Ref<GLTFState>);
+ GDVIRTUAL6R(Ref<GLTFObjectModelProperty>, _export_object_model_property, Ref<GLTFState>, NodePath, const Node *, GLTFNodeIndex, const Object *, int);
GDVIRTUAL0R(Vector<String>, _get_saveable_image_formats);
GDVIRTUAL5R(PackedByteArray, _serialize_image_to_bytes, Ref<GLTFState>, Ref<Image>, Dictionary, String, float);
GDVIRTUAL5R(Error, _save_image_at_path, Ref<GLTFState>, Ref<Image>, String, String, float);
diff --git a/modules/gltf/extensions/gltf_light.cpp b/modules/gltf/extensions/gltf_light.cpp
index 5cafe00ca1..bfb47293c2 100644
--- a/modules/gltf/extensions/gltf_light.cpp
+++ b/modules/gltf/extensions/gltf_light.cpp
@@ -32,6 +32,7 @@
#include "gltf_light.h"
+#include "../structures/gltf_object_model_property.h"
#include "scene/3d/light_3d.h"
void GLTFLight::_bind_methods() {
@@ -64,6 +65,21 @@ void GLTFLight::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "outer_cone_angle"), "set_outer_cone_angle", "get_outer_cone_angle"); // float
}
+void GLTFLight::set_cone_inner_attenuation_conversion_expressions(Ref<GLTFObjectModelProperty> &r_obj_model_prop) {
+ // Expression to convert glTF innerConeAngle to Godot spot_angle_attenuation.
+ Ref<Expression> gltf_to_godot_expr;
+ gltf_to_godot_expr.instantiate();
+ PackedStringArray gltf_to_godot_args = { "inner_cone_angle" };
+ gltf_to_godot_expr->parse("0.2 / (1.0 - inner_cone_angle / spot_angle) - 0.1", gltf_to_godot_args);
+ r_obj_model_prop->set_gltf_to_godot_expression(gltf_to_godot_expr);
+ // Expression to convert Godot spot_angle_attenuation to glTF innerConeAngle.
+ Ref<Expression> godot_to_gltf_expr;
+ godot_to_gltf_expr.instantiate();
+ PackedStringArray godot_to_gltf_args = { "godot_spot_angle_att" };
+ godot_to_gltf_expr->parse("spot_angle * maxf(0.0, 1.0 - (0.2 / (0.1 + godot_spot_angle_att)))", godot_to_gltf_args);
+ r_obj_model_prop->set_godot_to_gltf_expression(godot_to_gltf_expr);
+}
+
Color GLTFLight::get_color() {
return color;
}
diff --git a/modules/gltf/extensions/gltf_light.h b/modules/gltf/extensions/gltf_light.h
index 8650f8235a..e23491a0b4 100644
--- a/modules/gltf/extensions/gltf_light.h
+++ b/modules/gltf/extensions/gltf_light.h
@@ -35,6 +35,7 @@
#include "core/io/resource.h"
+class GLTFObjectModelProperty;
class Light3D;
// https://github.com/KhronosGroup/glTF/blob/main/extensions/2.0/Khronos/KHR_lights_punctual
@@ -56,6 +57,8 @@ private:
Dictionary additional_data;
public:
+ static void set_cone_inner_attenuation_conversion_expressions(Ref<GLTFObjectModelProperty> &r_obj_model_prop);
+
Color get_color();
void set_color(Color p_color);
diff --git a/modules/gltf/extensions/physics/gltf_document_extension_physics.cpp b/modules/gltf/extensions/physics/gltf_document_extension_physics.cpp
index 538d84536d..60ea9651f2 100644
--- a/modules/gltf/extensions/physics/gltf_document_extension_physics.cpp
+++ b/modules/gltf/extensions/physics/gltf_document_extension_physics.cpp
@@ -33,8 +33,11 @@
#include "gltf_document_extension_physics.h"
#include "scene/3d/physics/area_3d.h"
+#include "scene/3d/physics/rigid_body_3d.h"
#include "scene/3d/physics/static_body_3d.h"
+using GLTFShapeIndex = int64_t;
+
// Import process.
Error GLTFDocumentExtensionPhysics::import_preflight(Ref<GLTFState> p_state, Vector<String> p_extensions) {
if (!p_extensions.has("OMI_collider") && !p_extensions.has("OMI_physics_body") && !p_extensions.has("OMI_physics_shape")) {
@@ -107,6 +110,7 @@ Error GLTFDocumentExtensionPhysics::parse_node_extensions(Ref<GLTFState> p_state
Array state_shapes = p_state->get_additional_data(StringName("GLTFPhysicsShapes"));
ERR_FAIL_INDEX_V_MSG(node_shape_index, state_shapes.size(), Error::ERR_FILE_CORRUPT, "glTF Physics: On node " + p_gltf_node->get_name() + ", the shape index " + itos(node_shape_index) + " is not in the state shapes (size: " + itos(state_shapes.size()) + ").");
p_gltf_node->set_additional_data(StringName("GLTFPhysicsColliderShape"), state_shapes[node_shape_index]);
+ p_gltf_node->set_additional_data(StringName("GLTFPhysicsColliderShapeIndex"), node_shape_index);
} else {
// If this node is a collider but does not have a collider
// shape, then it only serves to combine together shapes.
@@ -121,6 +125,7 @@ Error GLTFDocumentExtensionPhysics::parse_node_extensions(Ref<GLTFState> p_state
Array state_shapes = p_state->get_additional_data(StringName("GLTFPhysicsShapes"));
ERR_FAIL_INDEX_V_MSG(node_shape_index, state_shapes.size(), Error::ERR_FILE_CORRUPT, "glTF Physics: On node " + p_gltf_node->get_name() + ", the shape index " + itos(node_shape_index) + " is not in the state shapes (size: " + itos(state_shapes.size()) + ").");
p_gltf_node->set_additional_data(StringName("GLTFPhysicsTriggerShape"), state_shapes[node_shape_index]);
+ p_gltf_node->set_additional_data(StringName("GLTFPhysicsTriggerShapeIndex"), node_shape_index);
} else {
// If this node is a trigger but does not have a trigger shape,
// then it's a trigger body, what Godot calls an Area3D node.
@@ -131,8 +136,8 @@ Error GLTFDocumentExtensionPhysics::parse_node_extensions(Ref<GLTFState> p_state
}
// If this node defines explicit member shape nodes, save this information.
if (node_trigger.has("nodes")) {
- Array node_trigger_nodes = node_trigger["nodes"];
- p_gltf_node->set_additional_data(StringName("GLTFPhysicsCompoundTriggerNodes"), node_trigger_nodes);
+ Array compound_trigger_nodes = node_trigger["nodes"];
+ p_gltf_node->set_additional_data(StringName("GLTFPhysicsCompoundTriggerNodes"), compound_trigger_nodes);
}
}
if (physics_body_ext.has("motion") || physics_body_ext.has("type")) {
@@ -142,6 +147,144 @@ Error GLTFDocumentExtensionPhysics::parse_node_extensions(Ref<GLTFState> p_state
return OK;
}
+bool _will_gltf_shape_become_subnode(Ref<GLTFState> p_state, const Ref<GLTFNode> p_gltf_node, GLTFNodeIndex p_gltf_node_index) {
+ if (p_gltf_node->has_additional_data(StringName("GLTFPhysicsBody"))) {
+ return true;
+ }
+ const TypedArray<GLTFNode> state_gltf_nodes = p_state->get_nodes();
+ const GLTFNodeIndex parent_index = p_gltf_node->get_parent();
+ if (parent_index == -1 || parent_index >= state_gltf_nodes.size()) {
+ return true;
+ }
+ const Ref<GLTFNode> parent_gltf_node = state_gltf_nodes[parent_index];
+ const Variant parent_body_maybe = parent_gltf_node->get_additional_data(StringName("GLTFPhysicsBody"));
+ if (parent_body_maybe.get_type() != Variant::NIL) {
+ Ref<GLTFPhysicsBody> parent_body = parent_body_maybe;
+ // If the parent matches the triggerness, then this node will be generated as a shape (CollisionShape3D).
+ // Otherwise, if there is a mismatch, a body will be generated for this node, and a subnode will also be generated for the shape.
+ if (parent_body->get_body_type() == "trigger") {
+ return p_gltf_node->has_additional_data(StringName("GLTFPhysicsColliderShape"));
+ } else {
+ return p_gltf_node->has_additional_data(StringName("GLTFPhysicsTriggerShape"));
+ }
+ }
+ if (parent_gltf_node->has_additional_data(StringName("GLTFPhysicsColliderShape"))) {
+ return false;
+ }
+ if (parent_gltf_node->has_additional_data(StringName("GLTFPhysicsTriggerShape"))) {
+ return false;
+ }
+ Variant compound_trigger_maybe = parent_gltf_node->has_additional_data(StringName("GLTFPhysicsCompoundTriggerNodes"));
+ if (compound_trigger_maybe.get_type() != Variant::NIL) {
+ Array compound_trigger_nodes = compound_trigger_maybe;
+ // Remember, JSON only has numbers, not integers, so must cast to double.
+ return !compound_trigger_nodes.has((double)p_gltf_node_index);
+ }
+ return true;
+}
+
+NodePath _get_scene_node_path_for_shape_index(Ref<GLTFState> p_state, const GLTFNodeIndex p_shape_index) {
+ TypedArray<GLTFNode> state_gltf_nodes = p_state->get_nodes();
+ for (GLTFNodeIndex node_index = 0; node_index < state_gltf_nodes.size(); node_index++) {
+ const Ref<GLTFNode> gltf_node = state_gltf_nodes[node_index];
+ ERR_CONTINUE(gltf_node.is_null());
+ // Check if this node has a shape index and if it matches the one we are looking for.
+ Variant shape_index_maybe = gltf_node->get_additional_data(StringName("GLTFPhysicsColliderShapeIndex"));
+ if (shape_index_maybe.get_type() != Variant::INT) {
+ shape_index_maybe = gltf_node->get_additional_data(StringName("GLTFPhysicsTriggerShapeIndex"));
+ if (shape_index_maybe.get_type() != Variant::INT) {
+ continue;
+ }
+ }
+ const GLTFShapeIndex shape_index = shape_index_maybe;
+ if (shape_index != p_shape_index) {
+ continue;
+ }
+ NodePath node_path = gltf_node->get_scene_node_path(p_state);
+ // At this point, we have found a node with the shape index we were looking for.
+ if (_will_gltf_shape_become_subnode(p_state, gltf_node, node_index)) {
+ Vector<StringName> sname_path = node_path.get_names();
+ sname_path.append(gltf_node->get_name() + "Shape");
+ node_path = NodePath(sname_path, false);
+ }
+ return node_path;
+ }
+ return NodePath();
+}
+
+Ref<GLTFObjectModelProperty> GLTFDocumentExtensionPhysics::import_object_model_property(Ref<GLTFState> p_state, const PackedStringArray &p_split_json_pointer, const TypedArray<NodePath> &p_partial_paths) {
+ Ref<GLTFObjectModelProperty> ret;
+ if (p_split_json_pointer.size() != 6) {
+ // The only properties this class cares about are exactly 6 levels deep.
+ return ret;
+ }
+ ret.instantiate();
+ const String &prop_name = p_split_json_pointer[5];
+ if (p_split_json_pointer[0] == "extensions" && p_split_json_pointer[2] == "shapes") {
+ if (p_split_json_pointer[1] == "OMI_physics_shape" || p_split_json_pointer[1] == "KHR_collision_shapes") {
+ const GLTFNodeIndex shape_index = p_split_json_pointer[3].to_int();
+ NodePath node_path = _get_scene_node_path_for_shape_index(p_state, shape_index);
+ if (node_path.is_empty()) {
+ return ret;
+ }
+ String godot_prop_name = prop_name;
+ if (prop_name == "size") {
+ ret->set_types(Variant::VECTOR3, GLTFObjectModelProperty::GLTF_OBJECT_MODEL_TYPE_FLOAT3);
+ } else if (prop_name == "height" || prop_name == "radius") {
+ ret->set_types(Variant::FLOAT, GLTFObjectModelProperty::GLTF_OBJECT_MODEL_TYPE_FLOAT);
+ } else if (prop_name == "radiusBottom" || prop_name == "radiusTop") {
+ godot_prop_name = "radius";
+ ret->set_types(Variant::FLOAT, GLTFObjectModelProperty::GLTF_OBJECT_MODEL_TYPE_FLOAT);
+ } else {
+ // Not something we handle, return without appending a NodePath.
+ return ret;
+ }
+ // Example: `A/B/C/CollisionShape3D:shape:radius`.
+ Vector<StringName> subnames;
+ subnames.append("shape");
+ subnames.append(godot_prop_name);
+ node_path = NodePath(node_path.get_names(), subnames, false);
+ ret->append_node_path(node_path);
+ }
+ } else if (p_split_json_pointer[0] == "nodes" && p_split_json_pointer[2] == "extensions" && p_split_json_pointer[4] == "motion") {
+ if (p_split_json_pointer[3] == "OMI_physics_body" || p_split_json_pointer[3] == "KHR_physics_rigid_bodies") {
+ const GLTFNodeIndex node_index = p_split_json_pointer[1].to_int();
+ const TypedArray<GLTFNode> all_gltf_nodes = p_state->get_nodes();
+ ERR_FAIL_INDEX_V_MSG(node_index, all_gltf_nodes.size(), ret, "GLTF Physics: The node index " + itos(node_index) + " is not in the state nodes (size: " + itos(all_gltf_nodes.size()) + ").");
+ const Ref<GLTFNode> gltf_node = all_gltf_nodes[node_index];
+ NodePath node_path;
+ if (p_partial_paths.is_empty()) {
+ node_path = gltf_node->get_scene_node_path(p_state);
+ } else {
+ // The path is already computed for us, just grab it.
+ node_path = p_partial_paths[0];
+ }
+ if (prop_name == "mass") {
+ ret->append_path_to_property(node_path, "mass");
+ ret->set_types(Variant::FLOAT, GLTFObjectModelProperty::GLTF_OBJECT_MODEL_TYPE_FLOAT);
+ } else if (prop_name == "linearVelocity") {
+ ret->append_path_to_property(node_path, "linear_velocity");
+ ret->set_types(Variant::VECTOR3, GLTFObjectModelProperty::GLTF_OBJECT_MODEL_TYPE_FLOAT3);
+ } else if (prop_name == "angularVelocity") {
+ ret->append_path_to_property(node_path, "angular_velocity");
+ ret->set_types(Variant::VECTOR3, GLTFObjectModelProperty::GLTF_OBJECT_MODEL_TYPE_FLOAT3);
+ } else if (prop_name == "centerOfMass") {
+ ret->append_path_to_property(node_path, "center_of_mass");
+ ret->set_types(Variant::VECTOR3, GLTFObjectModelProperty::GLTF_OBJECT_MODEL_TYPE_FLOAT3);
+ } else if (prop_name == "inertiaDiagonal") {
+ ret->append_path_to_property(node_path, "inertia");
+ ret->set_types(Variant::VECTOR3, GLTFObjectModelProperty::GLTF_OBJECT_MODEL_TYPE_FLOAT3);
+ } else if (prop_name == "inertiaOrientation") {
+ WARN_PRINT("GLTF Physics: The 'inertiaOrientation' property is not supported by Godot.");
+ } else {
+ // Not something we handle, return without appending a NodePath.
+ return ret;
+ }
+ }
+ }
+ return ret;
+}
+
void _setup_shape_mesh_resource_from_index_if_needed(Ref<GLTFState> p_state, Ref<GLTFPhysicsShape> p_gltf_shape) {
GLTFMeshIndex shape_mesh_index = p_gltf_shape->get_mesh_index();
if (shape_mesh_index == -1) {
@@ -436,24 +579,126 @@ Array _get_or_create_state_shapes_in_state(Ref<GLTFState> p_state) {
return state_shapes;
}
-Dictionary _export_node_shape(Ref<GLTFState> p_state, Ref<GLTFPhysicsShape> p_physics_shape) {
+GLTFShapeIndex _export_node_shape(Ref<GLTFState> p_state, Ref<GLTFPhysicsShape> p_physics_shape) {
Array state_shapes = _get_or_create_state_shapes_in_state(p_state);
- int size = state_shapes.size();
+ GLTFShapeIndex size = state_shapes.size();
Dictionary shape_property;
Dictionary shape_dict = p_physics_shape->to_dictionary();
- for (int i = 0; i < size; i++) {
+ for (GLTFShapeIndex i = 0; i < size; i++) {
Dictionary other = state_shapes[i];
if (other == shape_dict) {
// De-duplication: If we already have an identical shape,
// set the shape index to the existing one and return.
- shape_property["shape"] = i;
- return shape_property;
+ return i;
}
}
// If we don't have an identical shape, add it to the array.
state_shapes.push_back(shape_dict);
- shape_property["shape"] = size;
- return shape_property;
+ return size;
+}
+
+Error GLTFDocumentExtensionPhysics::export_preserialize(Ref<GLTFState> p_state) {
+ // Note: Need to do _export_node_shape before exporting animations, so export_node is too late.
+ TypedArray<GLTFNode> state_gltf_nodes = p_state->get_nodes();
+ for (Ref<GLTFNode> gltf_node : state_gltf_nodes) {
+ Ref<GLTFPhysicsShape> collider_shape = gltf_node->get_additional_data(StringName("GLTFPhysicsColliderShape"));
+ if (collider_shape.is_valid()) {
+ GLTFShapeIndex collider_shape_index = _export_node_shape(p_state, collider_shape);
+ gltf_node->set_additional_data(StringName("GLTFPhysicsColliderShapeIndex"), collider_shape_index);
+ }
+ Ref<GLTFPhysicsShape> trigger_shape = gltf_node->get_additional_data(StringName("GLTFPhysicsTriggerShape"));
+ if (trigger_shape.is_valid()) {
+ GLTFShapeIndex trigger_shape_index = _export_node_shape(p_state, trigger_shape);
+ gltf_node->set_additional_data(StringName("GLTFPhysicsTriggerShapeIndex"), trigger_shape_index);
+ }
+ }
+ return OK;
+}
+
+Ref<GLTFObjectModelProperty> GLTFDocumentExtensionPhysics::export_object_model_property(Ref<GLTFState> p_state, const NodePath &p_node_path, const Node *p_godot_node, GLTFNodeIndex p_gltf_node_index, const Object *p_target_object, int p_target_depth) {
+ Ref<GLTFObjectModelProperty> ret;
+ const Vector<StringName> &path_subnames = p_node_path.get_subnames();
+ if (path_subnames.is_empty()) {
+ return ret;
+ }
+ ret.instantiate();
+ const StringName &node_prop = path_subnames[0];
+ if (Object::cast_to<RigidBody3D>(p_target_object)) {
+ if (path_subnames.size() != 1) {
+ return ret;
+ }
+ // Example: `/nodes/0/extensions/OMI_physics_body/motion/mass`
+ PackedStringArray split_json_pointer;
+ split_json_pointer.append("nodes");
+ split_json_pointer.append(itos(p_gltf_node_index));
+ split_json_pointer.append("extensions");
+ split_json_pointer.append("OMI_physics_body");
+ split_json_pointer.append("motion");
+ if (node_prop == StringName("mass")) {
+ split_json_pointer.append("mass");
+ ret->set_types(Variant::FLOAT, GLTFObjectModelProperty::GLTF_OBJECT_MODEL_TYPE_FLOAT);
+ } else if (node_prop == StringName("linear_velocity")) {
+ split_json_pointer.append("linearVelocity");
+ ret->set_types(Variant::VECTOR3, GLTFObjectModelProperty::GLTF_OBJECT_MODEL_TYPE_FLOAT3);
+ } else if (node_prop == StringName("angular_velocity")) {
+ split_json_pointer.append("angularVelocity");
+ ret->set_types(Variant::VECTOR3, GLTFObjectModelProperty::GLTF_OBJECT_MODEL_TYPE_FLOAT3);
+ } else if (node_prop == StringName("center_of_mass")) {
+ split_json_pointer.append("centerOfMass");
+ ret->set_types(Variant::VECTOR3, GLTFObjectModelProperty::GLTF_OBJECT_MODEL_TYPE_FLOAT3);
+ } else if (node_prop == StringName("inertia")) {
+ split_json_pointer.append("inertiaDiagonal");
+ ret->set_types(Variant::VECTOR3, GLTFObjectModelProperty::GLTF_OBJECT_MODEL_TYPE_FLOAT3);
+ } else {
+ // Not something we handle, return without setting the JSON pointer.
+ return ret;
+ }
+ ret->set_json_pointers({ split_json_pointer });
+ } else if (Object::cast_to<CollisionShape3D>(p_godot_node)) {
+ if (path_subnames.size() != 2) {
+ return ret;
+ }
+ // Example: `/extensions/OMI_physics_shape/shapes/0/box/size`
+ PackedStringArray split_json_pointer;
+ split_json_pointer.append("extensions");
+ split_json_pointer.append("OMI_physics_shape");
+ split_json_pointer.append("shapes");
+ TypedArray<GLTFNode> state_gltf_nodes = p_state->get_nodes();
+ ERR_FAIL_INDEX_V(p_gltf_node_index, state_gltf_nodes.size(), ret);
+ Ref<GLTFNode> gltf_node = state_gltf_nodes[p_gltf_node_index];
+ Variant shape_index_maybe = gltf_node->get_additional_data(StringName("GLTFPhysicsColliderShapeIndex"));
+ String shape_type;
+ if (shape_index_maybe.get_type() == Variant::INT) {
+ Ref<GLTFPhysicsShape> collider_shape = gltf_node->get_additional_data(StringName("GLTFPhysicsColliderShape"));
+ shape_type = collider_shape->get_shape_type();
+ } else {
+ shape_index_maybe = gltf_node->get_additional_data(StringName("GLTFPhysicsTriggerShapeIndex"));
+ if (shape_index_maybe.get_type() == Variant::INT) {
+ Ref<GLTFPhysicsShape> trigger_shape = gltf_node->get_additional_data(StringName("GLTFPhysicsTriggerShape"));
+ shape_type = trigger_shape->get_shape_type();
+ }
+ }
+ ERR_FAIL_COND_V(shape_index_maybe.get_type() != Variant::INT, ret);
+ GLTFShapeIndex shape_index = shape_index_maybe;
+ split_json_pointer.append(itos(shape_index));
+ split_json_pointer.append(shape_type);
+ const StringName &shape_prop = path_subnames[1];
+ if (shape_prop == StringName("size")) {
+ split_json_pointer.append("size");
+ ret->set_types(Variant::VECTOR3, GLTFObjectModelProperty::GLTF_OBJECT_MODEL_TYPE_FLOAT3);
+ } else if (shape_prop == StringName("radius")) {
+ split_json_pointer.append("radius");
+ ret->set_types(Variant::FLOAT, GLTFObjectModelProperty::GLTF_OBJECT_MODEL_TYPE_FLOAT);
+ } else if (shape_prop == StringName("height")) {
+ split_json_pointer.append("height");
+ ret->set_types(Variant::FLOAT, GLTFObjectModelProperty::GLTF_OBJECT_MODEL_TYPE_FLOAT);
+ } else {
+ // Not something we handle, return without setting the JSON pointer.
+ return ret;
+ }
+ ret->set_json_pointers({ split_json_pointer });
+ }
+ return ret;
}
Error GLTFDocumentExtensionPhysics::export_node(Ref<GLTFState> p_state, Ref<GLTFNode> p_gltf_node, Dictionary &r_node_json, Node *p_node) {
@@ -467,13 +712,16 @@ Error GLTFDocumentExtensionPhysics::export_node(Ref<GLTFState> p_state, Ref<GLTF
trigger_property["nodes"] = compound_trigger_nodes;
}
}
- Ref<GLTFPhysicsShape> collider_shape = p_gltf_node->get_additional_data(StringName("GLTFPhysicsColliderShape"));
- if (collider_shape.is_valid()) {
- physics_body_ext["collider"] = _export_node_shape(p_state, collider_shape);
+ Variant collider_shape_index = p_gltf_node->get_additional_data(StringName("GLTFPhysicsColliderShapeIndex"));
+ if (collider_shape_index.get_type() == Variant::INT) {
+ Dictionary collider_dict;
+ collider_dict["shape"] = collider_shape_index;
+ physics_body_ext["collider"] = collider_dict;
}
- Ref<GLTFPhysicsShape> trigger_shape = p_gltf_node->get_additional_data(StringName("GLTFPhysicsTriggerShape"));
- if (trigger_shape.is_valid()) {
- physics_body_ext["trigger"] = _export_node_shape(p_state, trigger_shape);
+ Variant trigger_shape_index = p_gltf_node->get_additional_data(StringName("GLTFPhysicsTriggerShapeIndex"));
+ if (trigger_shape_index.get_type() == Variant::INT) {
+ Dictionary trigger_dict = physics_body_ext.get_or_add("trigger", {});
+ trigger_dict["shape"] = trigger_shape_index;
}
if (!physics_body_ext.is_empty()) {
Dictionary node_extensions = r_node_json["extensions"];
diff --git a/modules/gltf/extensions/physics/gltf_document_extension_physics.h b/modules/gltf/extensions/physics/gltf_document_extension_physics.h
index 0916e422f1..cf80c37e8e 100644
--- a/modules/gltf/extensions/physics/gltf_document_extension_physics.h
+++ b/modules/gltf/extensions/physics/gltf_document_extension_physics.h
@@ -45,9 +45,12 @@ public:
Error import_preflight(Ref<GLTFState> p_state, Vector<String> p_extensions) override;
Vector<String> get_supported_extensions() override;
Error parse_node_extensions(Ref<GLTFState> p_state, Ref<GLTFNode> p_gltf_node, Dictionary &p_extensions) override;
+ Ref<GLTFObjectModelProperty> import_object_model_property(Ref<GLTFState> p_state, const PackedStringArray &p_split_json_pointer, const TypedArray<NodePath> &p_partial_paths) override;
Node3D *generate_scene_node(Ref<GLTFState> p_state, Ref<GLTFNode> p_gltf_node, Node *p_scene_parent) override;
// Export process.
void convert_scene_node(Ref<GLTFState> p_state, Ref<GLTFNode> p_gltf_node, Node *p_scene_node) override;
+ Error export_preserialize(Ref<GLTFState> p_state) override;
+ Ref<GLTFObjectModelProperty> export_object_model_property(Ref<GLTFState> p_state, const NodePath &p_node_path, const Node *p_godot_node, GLTFNodeIndex p_gltf_node_index, const Object *p_target_object, int p_target_depth) override;
Error export_node(Ref<GLTFState> p_state, Ref<GLTFNode> p_gltf_node, Dictionary &r_node_json, Node *p_scene_node) override;
};
diff --git a/modules/gltf/extensions/physics/gltf_physics_body.cpp b/modules/gltf/extensions/physics/gltf_physics_body.cpp
index ac6d4283b6..a109a02c9d 100644
--- a/modules/gltf/extensions/physics/gltf_physics_body.cpp
+++ b/modules/gltf/extensions/physics/gltf_physics_body.cpp
@@ -195,9 +195,6 @@ Ref<GLTFPhysicsBody> GLTFPhysicsBody::from_node(const CollisionObject3D *p_body_
physics_body->angular_velocity = body->get_angular_velocity();
physics_body->center_of_mass = body->get_center_of_mass();
physics_body->inertia_diagonal = body->get_inertia();
- if (body->get_center_of_mass() != Vector3()) {
- WARN_PRINT("GLTFPhysicsBody: This rigid body has a center of mass offset from the origin, which will be ignored when exporting to glTF.");
- }
if (cast_to<VehicleBody3D>(p_body_node)) {
physics_body->body_type = PhysicsBodyType::VEHICLE;
} else {
diff --git a/modules/gltf/gltf_defines.h b/modules/gltf/gltf_defines.h
index 2b6e44edd7..83b4aca37e 100644
--- a/modules/gltf/gltf_defines.h
+++ b/modules/gltf/gltf_defines.h
@@ -45,6 +45,7 @@ class GLTFDocumentExtension;
class GLTFLight;
class GLTFMesh;
class GLTFNode;
+class GLTFObjectModelProperty;
class GLTFSkeleton;
class GLTFSkin;
class GLTFSpecGloss;
diff --git a/modules/gltf/gltf_document.cpp b/modules/gltf/gltf_document.cpp
index b962d30c54..8a67a92ee2 100644
--- a/modules/gltf/gltf_document.cpp
+++ b/modules/gltf/gltf_document.cpp
@@ -71,6 +71,10 @@
#include <stdlib.h>
#include <cstdint>
+constexpr int COMPONENT_COUNT_FOR_ACCESSOR_TYPE[7] = {
+ 1, 2, 3, 4, 4, 9, 16
+};
+
static void _attach_extras_to_meta(const Dictionary &p_extras, Ref<Resource> p_node) {
if (!p_extras.is_empty()) {
p_node->set_meta("extras", p_extras);
@@ -1015,7 +1019,7 @@ Error GLTFDocument::_parse_accessors(Ref<GLTFState> p_state) {
accessor.instantiate();
ERR_FAIL_COND_V(!d.has("componentType"), ERR_PARSE_ERROR);
- accessor->component_type = d["componentType"];
+ accessor->component_type = (GLTFAccessor::GLTFComponentType)(int32_t)d["componentType"];
ERR_FAIL_COND_V(!d.has("count"), ERR_PARSE_ERROR);
accessor->count = d["count"];
ERR_FAIL_COND_V(!d.has("type"), ERR_PARSE_ERROR);
@@ -1052,7 +1056,7 @@ Error GLTFDocument::_parse_accessors(Ref<GLTFState> p_state) {
ERR_FAIL_COND_V(!si.has("bufferView"), ERR_PARSE_ERROR);
accessor->sparse_indices_buffer_view = si["bufferView"];
ERR_FAIL_COND_V(!si.has("componentType"), ERR_PARSE_ERROR);
- accessor->sparse_indices_component_type = si["componentType"];
+ accessor->sparse_indices_component_type = (GLTFAccessor::GLTFComponentType)(int32_t)si["componentType"];
if (si.has("byteOffset")) {
accessor->sparse_indices_byte_offset = si["byteOffset"];
@@ -1084,31 +1088,39 @@ double GLTFDocument::_filter_number(double p_float) {
return (double)(float)p_float;
}
-String GLTFDocument::_get_component_type_name(const uint32_t p_component) {
+String GLTFDocument::_get_component_type_name(const GLTFAccessor::GLTFComponentType p_component) {
switch (p_component) {
- case GLTFDocument::COMPONENT_TYPE_BYTE:
+ case GLTFAccessor::COMPONENT_TYPE_NONE:
+ return "None";
+ case GLTFAccessor::COMPONENT_TYPE_SIGNED_BYTE:
return "Byte";
- case GLTFDocument::COMPONENT_TYPE_UNSIGNED_BYTE:
+ case GLTFAccessor::COMPONENT_TYPE_UNSIGNED_BYTE:
return "UByte";
- case GLTFDocument::COMPONENT_TYPE_SHORT:
+ case GLTFAccessor::COMPONENT_TYPE_SIGNED_SHORT:
return "Short";
- case GLTFDocument::COMPONENT_TYPE_UNSIGNED_SHORT:
+ case GLTFAccessor::COMPONENT_TYPE_UNSIGNED_SHORT:
return "UShort";
- case GLTFDocument::COMPONENT_TYPE_INT:
+ case GLTFAccessor::COMPONENT_TYPE_SIGNED_INT:
return "Int";
- case GLTFDocument::COMPONENT_TYPE_FLOAT:
+ case GLTFAccessor::COMPONENT_TYPE_UNSIGNED_INT:
+ return "UInt";
+ case GLTFAccessor::COMPONENT_TYPE_SINGLE_FLOAT:
return "Float";
+ case GLTFAccessor::COMPONENT_TYPE_DOUBLE_FLOAT:
+ return "Double";
+ case GLTFAccessor::COMPONENT_TYPE_HALF_FLOAT:
+ return "Half";
+ case GLTFAccessor::COMPONENT_TYPE_SIGNED_LONG:
+ return "Long";
+ case GLTFAccessor::COMPONENT_TYPE_UNSIGNED_LONG:
+ return "ULong";
}
return "<Error>";
}
-Error GLTFDocument::_encode_buffer_view(Ref<GLTFState> p_state, const double *p_src, const int p_count, const GLTFAccessor::GLTFAccessorType p_accessor_type, const int p_component_type, const bool p_normalized, const int p_byte_offset, const bool p_for_vertex, GLTFBufferViewIndex &r_accessor, const bool p_for_vertex_indices) {
- const int component_count_for_type[7] = {
- 1, 2, 3, 4, 4, 9, 16
- };
-
- const int component_count = component_count_for_type[p_accessor_type];
+Error GLTFDocument::_encode_buffer_view(Ref<GLTFState> p_state, const double *p_src, const int p_count, const GLTFAccessor::GLTFAccessorType p_accessor_type, const GLTFAccessor::GLTFComponentType p_component_type, const bool p_normalized, const int p_byte_offset, const bool p_for_vertex, GLTFBufferViewIndex &r_accessor, const bool p_for_vertex_indices) {
+ const int component_count = COMPONENT_COUNT_FOR_ACCESSOR_TYPE[p_accessor_type];
const int component_size = _get_component_type_size(p_component_type);
ERR_FAIL_COND_V(component_size == 0, FAILED);
@@ -1116,8 +1128,8 @@ Error GLTFDocument::_encode_buffer_view(Ref<GLTFState> p_state, const double *p_
int skip_bytes = 0;
//special case of alignments, as described in spec
switch (p_component_type) {
- case COMPONENT_TYPE_BYTE:
- case COMPONENT_TYPE_UNSIGNED_BYTE: {
+ case GLTFAccessor::COMPONENT_TYPE_SIGNED_BYTE:
+ case GLTFAccessor::COMPONENT_TYPE_UNSIGNED_BYTE: {
if (p_accessor_type == GLTFAccessor::TYPE_MAT2) {
skip_every = 2;
skip_bytes = 2;
@@ -1127,8 +1139,8 @@ Error GLTFDocument::_encode_buffer_view(Ref<GLTFState> p_state, const double *p_
skip_bytes = 1;
}
} break;
- case COMPONENT_TYPE_SHORT:
- case COMPONENT_TYPE_UNSIGNED_SHORT: {
+ case GLTFAccessor::COMPONENT_TYPE_SIGNED_SHORT:
+ case GLTFAccessor::COMPONENT_TYPE_UNSIGNED_SHORT: {
if (p_accessor_type == GLTFAccessor::TYPE_MAT3) {
skip_every = 6;
skip_bytes = 4;
@@ -1163,7 +1175,10 @@ Error GLTFDocument::_encode_buffer_view(Ref<GLTFState> p_state, const double *p_
}
switch (p_component_type) {
- case COMPONENT_TYPE_BYTE: {
+ case GLTFAccessor::COMPONENT_TYPE_NONE: {
+ ERR_FAIL_V_MSG(ERR_INVALID_DATA, "glTF: Failed to encode buffer view, component type not set.");
+ }
+ case GLTFAccessor::COMPONENT_TYPE_SIGNED_BYTE: {
Vector<int8_t> buffer;
buffer.resize(p_count * component_count);
int32_t dst_i = 0;
@@ -1187,7 +1202,7 @@ Error GLTFDocument::_encode_buffer_view(Ref<GLTFState> p_state, const double *p_
memcpy(gltf_buffer.ptrw() + old_size, buffer.ptrw(), buffer.size() * sizeof(int8_t));
bv->byte_length = buffer.size() * sizeof(int8_t);
} break;
- case COMPONENT_TYPE_UNSIGNED_BYTE: {
+ case GLTFAccessor::COMPONENT_TYPE_UNSIGNED_BYTE: {
Vector<uint8_t> buffer;
buffer.resize(p_count * component_count);
int32_t dst_i = 0;
@@ -1209,7 +1224,7 @@ Error GLTFDocument::_encode_buffer_view(Ref<GLTFState> p_state, const double *p_
gltf_buffer.append_array(buffer);
bv->byte_length = buffer.size() * sizeof(uint8_t);
} break;
- case COMPONENT_TYPE_SHORT: {
+ case GLTFAccessor::COMPONENT_TYPE_SIGNED_SHORT: {
Vector<int16_t> buffer;
buffer.resize(p_count * component_count);
int32_t dst_i = 0;
@@ -1233,7 +1248,7 @@ Error GLTFDocument::_encode_buffer_view(Ref<GLTFState> p_state, const double *p_
memcpy(gltf_buffer.ptrw() + old_size, buffer.ptrw(), buffer.size() * sizeof(int16_t));
bv->byte_length = buffer.size() * sizeof(int16_t);
} break;
- case COMPONENT_TYPE_UNSIGNED_SHORT: {
+ case GLTFAccessor::COMPONENT_TYPE_UNSIGNED_SHORT: {
Vector<uint16_t> buffer;
buffer.resize(p_count * component_count);
int32_t dst_i = 0;
@@ -1257,8 +1272,28 @@ Error GLTFDocument::_encode_buffer_view(Ref<GLTFState> p_state, const double *p_
memcpy(gltf_buffer.ptrw() + old_size, buffer.ptrw(), buffer.size() * sizeof(uint16_t));
bv->byte_length = buffer.size() * sizeof(uint16_t);
} break;
- case COMPONENT_TYPE_INT: {
- Vector<int> buffer;
+ case GLTFAccessor::COMPONENT_TYPE_SIGNED_INT: {
+ Vector<int32_t> buffer;
+ buffer.resize(p_count * component_count);
+ int32_t dst_i = 0;
+ for (int i = 0; i < p_count; i++) {
+ for (int j = 0; j < component_count; j++) {
+ if (skip_every && j > 0 && (j % skip_every) == 0) {
+ dst_i += skip_bytes;
+ }
+ double d = *p_src;
+ buffer.write[dst_i] = d;
+ p_src++;
+ dst_i++;
+ }
+ }
+ int64_t old_size = gltf_buffer.size();
+ gltf_buffer.resize(old_size + (buffer.size() * sizeof(uint32_t)));
+ memcpy(gltf_buffer.ptrw() + old_size, buffer.ptrw(), buffer.size() * sizeof(uint32_t));
+ bv->byte_length = buffer.size() * sizeof(uint32_t);
+ } break;
+ case GLTFAccessor::COMPONENT_TYPE_UNSIGNED_INT: {
+ Vector<uint32_t> buffer;
buffer.resize(p_count * component_count);
int32_t dst_i = 0;
for (int i = 0; i < p_count; i++) {
@@ -1273,11 +1308,11 @@ Error GLTFDocument::_encode_buffer_view(Ref<GLTFState> p_state, const double *p_
}
}
int64_t old_size = gltf_buffer.size();
- gltf_buffer.resize(old_size + (buffer.size() * sizeof(int32_t)));
- memcpy(gltf_buffer.ptrw() + old_size, buffer.ptrw(), buffer.size() * sizeof(int32_t));
- bv->byte_length = buffer.size() * sizeof(int32_t);
+ gltf_buffer.resize(old_size + (buffer.size() * sizeof(uint32_t)));
+ memcpy(gltf_buffer.ptrw() + old_size, buffer.ptrw(), buffer.size() * sizeof(uint32_t));
+ bv->byte_length = buffer.size() * sizeof(uint32_t);
} break;
- case COMPONENT_TYPE_FLOAT: {
+ case GLTFAccessor::COMPONENT_TYPE_SINGLE_FLOAT: {
Vector<float> buffer;
buffer.resize(p_count * component_count);
int32_t dst_i = 0;
@@ -1297,6 +1332,71 @@ Error GLTFDocument::_encode_buffer_view(Ref<GLTFState> p_state, const double *p_
memcpy(gltf_buffer.ptrw() + old_size, buffer.ptrw(), buffer.size() * sizeof(float));
bv->byte_length = buffer.size() * sizeof(float);
} break;
+ case GLTFAccessor::COMPONENT_TYPE_DOUBLE_FLOAT: {
+ Vector<double> buffer;
+ buffer.resize(p_count * component_count);
+ int32_t dst_i = 0;
+ for (int i = 0; i < p_count; i++) {
+ for (int j = 0; j < component_count; j++) {
+ if (skip_every && j > 0 && (j % skip_every) == 0) {
+ dst_i += skip_bytes;
+ }
+ double d = *p_src;
+ buffer.write[dst_i] = d;
+ p_src++;
+ dst_i++;
+ }
+ }
+ int64_t old_size = gltf_buffer.size();
+ gltf_buffer.resize(old_size + (buffer.size() * sizeof(double)));
+ memcpy(gltf_buffer.ptrw() + old_size, buffer.ptrw(), buffer.size() * sizeof(double));
+ bv->byte_length = buffer.size() * sizeof(double);
+ } break;
+ case GLTFAccessor::COMPONENT_TYPE_HALF_FLOAT: {
+ ERR_FAIL_V_MSG(ERR_UNAVAILABLE, "glTF: Half float not supported yet.");
+ } break;
+ case GLTFAccessor::COMPONENT_TYPE_SIGNED_LONG: {
+ Vector<int64_t> buffer;
+ buffer.resize(p_count * component_count);
+ int32_t dst_i = 0;
+ for (int i = 0; i < p_count; i++) {
+ for (int j = 0; j < component_count; j++) {
+ if (skip_every && j > 0 && (j % skip_every) == 0) {
+ dst_i += skip_bytes;
+ }
+ // FIXME: This can result in precision loss because int64_t can store some values that double can't.
+ double d = *p_src;
+ buffer.write[dst_i] = d;
+ p_src++;
+ dst_i++;
+ }
+ }
+ int64_t old_size = gltf_buffer.size();
+ gltf_buffer.resize(old_size + (buffer.size() * sizeof(int64_t)));
+ memcpy(gltf_buffer.ptrw() + old_size, buffer.ptrw(), buffer.size() * sizeof(int64_t));
+ bv->byte_length = buffer.size() * sizeof(int64_t);
+ } break;
+ case GLTFAccessor::COMPONENT_TYPE_UNSIGNED_LONG: {
+ Vector<uint64_t> buffer;
+ buffer.resize(p_count * component_count);
+ int32_t dst_i = 0;
+ for (int i = 0; i < p_count; i++) {
+ for (int j = 0; j < component_count; j++) {
+ if (skip_every && j > 0 && (j % skip_every) == 0) {
+ dst_i += skip_bytes;
+ }
+ // FIXME: This can result in precision loss because int64_t can store some values that double can't.
+ double d = *p_src;
+ buffer.write[dst_i] = d;
+ p_src++;
+ dst_i++;
+ }
+ }
+ int64_t old_size = gltf_buffer.size();
+ gltf_buffer.resize(old_size + (buffer.size() * sizeof(uint64_t)));
+ memcpy(gltf_buffer.ptrw() + old_size, buffer.ptrw(), buffer.size() * sizeof(uint64_t));
+ bv->byte_length = buffer.size() * sizeof(uint64_t);
+ } break;
}
ERR_FAIL_COND_V(buffer_end > bv->byte_length, ERR_INVALID_DATA);
@@ -1311,7 +1411,7 @@ Error GLTFDocument::_encode_buffer_view(Ref<GLTFState> p_state, const double *p_
return OK;
}
-Error GLTFDocument::_decode_buffer_view(Ref<GLTFState> p_state, double *p_dst, const GLTFBufferViewIndex p_buffer_view, const int p_skip_every, const int p_skip_bytes, const int p_element_size, const int p_count, const GLTFAccessor::GLTFAccessorType p_accessor_type, const int p_component_count, const int p_component_type, const int p_component_size, const bool p_normalized, const int p_byte_offset, const bool p_for_vertex) {
+Error GLTFDocument::_decode_buffer_view(Ref<GLTFState> p_state, double *p_dst, const GLTFBufferViewIndex p_buffer_view, const int p_skip_every, const int p_skip_bytes, const int p_element_size, const int p_count, const GLTFAccessor::GLTFAccessorType p_accessor_type, const int p_component_count, const GLTFAccessor::GLTFComponentType p_component_type, const int p_component_size, const bool p_normalized, const int p_byte_offset, const bool p_for_vertex) {
const Ref<GLTFBufferView> bv = p_state->buffer_views[p_buffer_view];
int stride = p_element_size;
@@ -1350,7 +1450,10 @@ Error GLTFDocument::_decode_buffer_view(Ref<GLTFState> p_state, double *p_dst, c
double d = 0;
switch (p_component_type) {
- case COMPONENT_TYPE_BYTE: {
+ case GLTFAccessor::COMPONENT_TYPE_NONE: {
+ ERR_FAIL_V_MSG(ERR_INVALID_DATA, "glTF: Failed to decode buffer view, component type not set.");
+ } break;
+ case GLTFAccessor::COMPONENT_TYPE_SIGNED_BYTE: {
int8_t b = int8_t(*src);
if (p_normalized) {
d = (double(b) / 128.0);
@@ -1358,7 +1461,7 @@ Error GLTFDocument::_decode_buffer_view(Ref<GLTFState> p_state, double *p_dst, c
d = double(b);
}
} break;
- case COMPONENT_TYPE_UNSIGNED_BYTE: {
+ case GLTFAccessor::COMPONENT_TYPE_UNSIGNED_BYTE: {
uint8_t b = *src;
if (p_normalized) {
d = (double(b) / 255.0);
@@ -1366,7 +1469,7 @@ Error GLTFDocument::_decode_buffer_view(Ref<GLTFState> p_state, double *p_dst, c
d = double(b);
}
} break;
- case COMPONENT_TYPE_SHORT: {
+ case GLTFAccessor::COMPONENT_TYPE_SIGNED_SHORT: {
int16_t s = *(int16_t *)src;
if (p_normalized) {
d = (double(s) / 32768.0);
@@ -1374,7 +1477,7 @@ Error GLTFDocument::_decode_buffer_view(Ref<GLTFState> p_state, double *p_dst, c
d = double(s);
}
} break;
- case COMPONENT_TYPE_UNSIGNED_SHORT: {
+ case GLTFAccessor::COMPONENT_TYPE_UNSIGNED_SHORT: {
uint16_t s = *(uint16_t *)src;
if (p_normalized) {
d = (double(s) / 65535.0);
@@ -1382,12 +1485,27 @@ Error GLTFDocument::_decode_buffer_view(Ref<GLTFState> p_state, double *p_dst, c
d = double(s);
}
} break;
- case COMPONENT_TYPE_INT: {
- d = *(int *)src;
+ case GLTFAccessor::COMPONENT_TYPE_SIGNED_INT: {
+ d = *(int32_t *)src;
+ } break;
+ case GLTFAccessor::COMPONENT_TYPE_UNSIGNED_INT: {
+ d = *(uint32_t *)src;
} break;
- case COMPONENT_TYPE_FLOAT: {
+ case GLTFAccessor::COMPONENT_TYPE_SINGLE_FLOAT: {
d = *(float *)src;
} break;
+ case GLTFAccessor::COMPONENT_TYPE_DOUBLE_FLOAT: {
+ d = *(double *)src;
+ } break;
+ case GLTFAccessor::COMPONENT_TYPE_HALF_FLOAT: {
+ ERR_FAIL_V_MSG(ERR_UNAVAILABLE, "glTF: Half float not supported yet.");
+ } break;
+ case GLTFAccessor::COMPONENT_TYPE_SIGNED_LONG: {
+ d = *(int64_t *)src;
+ } break;
+ case GLTFAccessor::COMPONENT_TYPE_UNSIGNED_LONG: {
+ d = *(uint64_t *)src;
+ } break;
}
*p_dst++ = d;
@@ -1398,25 +1516,27 @@ Error GLTFDocument::_decode_buffer_view(Ref<GLTFState> p_state, double *p_dst, c
return OK;
}
-int GLTFDocument::_get_component_type_size(const int p_component_type) {
+int GLTFDocument::_get_component_type_size(const GLTFAccessor::GLTFComponentType p_component_type) {
switch (p_component_type) {
- case COMPONENT_TYPE_BYTE:
- case COMPONENT_TYPE_UNSIGNED_BYTE:
+ case GLTFAccessor::COMPONENT_TYPE_NONE:
+ ERR_FAIL_V(0);
+ case GLTFAccessor::COMPONENT_TYPE_SIGNED_BYTE:
+ case GLTFAccessor::COMPONENT_TYPE_UNSIGNED_BYTE:
return 1;
- break;
- case COMPONENT_TYPE_SHORT:
- case COMPONENT_TYPE_UNSIGNED_SHORT:
+ case GLTFAccessor::COMPONENT_TYPE_SIGNED_SHORT:
+ case GLTFAccessor::COMPONENT_TYPE_UNSIGNED_SHORT:
+ case GLTFAccessor::COMPONENT_TYPE_HALF_FLOAT:
return 2;
- break;
- case COMPONENT_TYPE_INT:
- case COMPONENT_TYPE_FLOAT:
+ case GLTFAccessor::COMPONENT_TYPE_SIGNED_INT:
+ case GLTFAccessor::COMPONENT_TYPE_UNSIGNED_INT:
+ case GLTFAccessor::COMPONENT_TYPE_SINGLE_FLOAT:
return 4;
- break;
- default: {
- ERR_FAIL_V(0);
- }
+ case GLTFAccessor::COMPONENT_TYPE_DOUBLE_FLOAT:
+ case GLTFAccessor::COMPONENT_TYPE_SIGNED_LONG:
+ case GLTFAccessor::COMPONENT_TYPE_UNSIGNED_LONG:
+ return 8;
}
- return 0;
+ ERR_FAIL_V(0);
}
Vector<double> GLTFDocument::_decode_accessor(Ref<GLTFState> p_state, const GLTFAccessorIndex p_accessor, const bool p_for_vertex) {
@@ -1427,11 +1547,7 @@ Vector<double> GLTFDocument::_decode_accessor(Ref<GLTFState> p_state, const GLTF
const Ref<GLTFAccessor> a = p_state->accessors[p_accessor];
- const int component_count_for_type[7] = {
- 1, 2, 3, 4, 4, 9, 16
- };
-
- const int component_count = component_count_for_type[a->accessor_type];
+ const int component_count = COMPONENT_COUNT_FOR_ACCESSOR_TYPE[a->accessor_type];
const int component_size = _get_component_type_size(a->component_type);
ERR_FAIL_COND_V(component_size == 0, Vector<double>());
int element_size = component_count * component_size;
@@ -1440,8 +1556,8 @@ Vector<double> GLTFDocument::_decode_accessor(Ref<GLTFState> p_state, const GLTF
int skip_bytes = 0;
//special case of alignments, as described in spec
switch (a->component_type) {
- case COMPONENT_TYPE_BYTE:
- case COMPONENT_TYPE_UNSIGNED_BYTE: {
+ case GLTFAccessor::COMPONENT_TYPE_SIGNED_BYTE:
+ case GLTFAccessor::COMPONENT_TYPE_UNSIGNED_BYTE: {
if (a->accessor_type == GLTFAccessor::TYPE_MAT2) {
skip_every = 2;
skip_bytes = 2;
@@ -1453,8 +1569,8 @@ Vector<double> GLTFDocument::_decode_accessor(Ref<GLTFState> p_state, const GLTF
element_size = 12; //override for this case
}
} break;
- case COMPONENT_TYPE_SHORT:
- case COMPONENT_TYPE_UNSIGNED_SHORT: {
+ case GLTFAccessor::COMPONENT_TYPE_SIGNED_SHORT:
+ case GLTFAccessor::COMPONENT_TYPE_UNSIGNED_SHORT: {
if (a->accessor_type == GLTFAccessor::TYPE_MAT3) {
skip_every = 6;
skip_bytes = 4;
@@ -1552,11 +1668,11 @@ GLTFAccessorIndex GLTFDocument::_encode_accessor_as_ints(Ref<GLTFState> p_state,
}
int64_t size = p_state->buffers[0].size();
const GLTFAccessor::GLTFAccessorType accessor_type = GLTFAccessor::TYPE_SCALAR;
- int component_type;
+ GLTFAccessor::GLTFComponentType component_type;
if (max_index > 65535 || p_for_vertex) {
- component_type = GLTFDocument::COMPONENT_TYPE_INT;
+ component_type = GLTFAccessor::COMPONENT_TYPE_UNSIGNED_INT;
} else {
- component_type = GLTFDocument::COMPONENT_TYPE_UNSIGNED_SHORT;
+ component_type = GLTFAccessor::COMPONENT_TYPE_UNSIGNED_SHORT;
}
accessor->max = type_max;
@@ -1666,7 +1782,7 @@ GLTFAccessorIndex GLTFDocument::_encode_accessor_as_vec2(Ref<GLTFState> p_state,
}
int64_t size = p_state->buffers[0].size();
const GLTFAccessor::GLTFAccessorType accessor_type = GLTFAccessor::TYPE_VEC2;
- const int component_type = GLTFDocument::COMPONENT_TYPE_FLOAT;
+ const GLTFAccessor::GLTFComponentType component_type = GLTFAccessor::COMPONENT_TYPE_SINGLE_FLOAT;
accessor->max = type_max;
accessor->min = type_min;
@@ -1719,7 +1835,7 @@ GLTFAccessorIndex GLTFDocument::_encode_accessor_as_color(Ref<GLTFState> p_state
}
int64_t size = p_state->buffers[0].size();
const GLTFAccessor::GLTFAccessorType accessor_type = GLTFAccessor::TYPE_VEC4;
- const int component_type = GLTFDocument::COMPONENT_TYPE_FLOAT;
+ const GLTFAccessor::GLTFComponentType component_type = GLTFAccessor::COMPONENT_TYPE_SINGLE_FLOAT;
accessor->max = type_max;
accessor->min = type_min;
@@ -1786,7 +1902,7 @@ GLTFAccessorIndex GLTFDocument::_encode_accessor_as_weights(Ref<GLTFState> p_sta
}
int64_t size = p_state->buffers[0].size();
const GLTFAccessor::GLTFAccessorType accessor_type = GLTFAccessor::TYPE_VEC4;
- const int component_type = GLTFDocument::COMPONENT_TYPE_FLOAT;
+ const GLTFAccessor::GLTFComponentType component_type = GLTFAccessor::COMPONENT_TYPE_SINGLE_FLOAT;
accessor->max = type_max;
accessor->min = type_min;
@@ -1837,7 +1953,7 @@ GLTFAccessorIndex GLTFDocument::_encode_accessor_as_joints(Ref<GLTFState> p_stat
}
int64_t size = p_state->buffers[0].size();
const GLTFAccessor::GLTFAccessorType accessor_type = GLTFAccessor::TYPE_VEC4;
- const int component_type = GLTFDocument::COMPONENT_TYPE_UNSIGNED_SHORT;
+ const GLTFAccessor::GLTFComponentType component_type = GLTFAccessor::COMPONENT_TYPE_UNSIGNED_SHORT;
accessor->max = type_max;
accessor->min = type_min;
@@ -1890,7 +2006,7 @@ GLTFAccessorIndex GLTFDocument::_encode_accessor_as_quaternions(Ref<GLTFState> p
}
int64_t size = p_state->buffers[0].size();
const GLTFAccessor::GLTFAccessorType accessor_type = GLTFAccessor::TYPE_VEC4;
- const int component_type = GLTFDocument::COMPONENT_TYPE_FLOAT;
+ const GLTFAccessor::GLTFComponentType component_type = GLTFAccessor::COMPONENT_TYPE_SINGLE_FLOAT;
accessor->max = type_max;
accessor->min = type_min;
@@ -1934,7 +2050,7 @@ Vector<Vector2> GLTFDocument::_decode_accessor_as_vec2(Ref<GLTFState> p_state, c
return ret;
}
-GLTFAccessorIndex GLTFDocument::_encode_accessor_as_floats(Ref<GLTFState> p_state, const Vector<real_t> p_attribs, const bool p_for_vertex) {
+GLTFAccessorIndex GLTFDocument::_encode_accessor_as_floats(Ref<GLTFState> p_state, const Vector<double> p_attribs, const bool p_for_vertex) {
if (p_attribs.size() == 0) {
return -1;
}
@@ -1965,7 +2081,7 @@ GLTFAccessorIndex GLTFDocument::_encode_accessor_as_floats(Ref<GLTFState> p_stat
}
int64_t size = p_state->buffers[0].size();
const GLTFAccessor::GLTFAccessorType accessor_type = GLTFAccessor::TYPE_SCALAR;
- const int component_type = GLTFDocument::COMPONENT_TYPE_FLOAT;
+ const GLTFAccessor::GLTFComponentType component_type = GLTFAccessor::COMPONENT_TYPE_SINGLE_FLOAT;
accessor->max = type_max;
accessor->min = type_min;
@@ -2015,7 +2131,7 @@ GLTFAccessorIndex GLTFDocument::_encode_accessor_as_vec3(Ref<GLTFState> p_state,
}
int64_t size = p_state->buffers[0].size();
const GLTFAccessor::GLTFAccessorType accessor_type = GLTFAccessor::TYPE_VEC3;
- const int component_type = GLTFDocument::COMPONENT_TYPE_FLOAT;
+ const GLTFAccessor::GLTFComponentType component_type = GLTFAccessor::COMPONENT_TYPE_SINGLE_FLOAT;
accessor->max = type_max;
accessor->min = type_min;
@@ -2091,7 +2207,7 @@ GLTFAccessorIndex GLTFDocument::_encode_sparse_accessor_as_vec3(Ref<GLTFState> p
}
int64_t size = p_state->buffers[0].size();
const GLTFAccessor::GLTFAccessorType accessor_type = GLTFAccessor::TYPE_VEC3;
- const int component_type = GLTFDocument::COMPONENT_TYPE_FLOAT;
+ const GLTFAccessor::GLTFComponentType component_type = GLTFAccessor::COMPONENT_TYPE_SINGLE_FLOAT;
sparse_accessor->normalized = false;
sparse_accessor->count = p_attribs.size();
@@ -2114,9 +2230,9 @@ GLTFAccessorIndex GLTFDocument::_encode_sparse_accessor_as_vec3(Ref<GLTFState> p
GLTFBufferIndex buffer_view_i_indices = -1;
GLTFBufferIndex buffer_view_i_values = -1;
if (sparse_accessor_index_stride == 4) {
- sparse_accessor->sparse_indices_component_type = GLTFDocument::COMPONENT_TYPE_INT;
+ sparse_accessor->sparse_indices_component_type = GLTFAccessor::COMPONENT_TYPE_UNSIGNED_INT;
} else {
- sparse_accessor->sparse_indices_component_type = GLTFDocument::COMPONENT_TYPE_UNSIGNED_SHORT;
+ sparse_accessor->sparse_indices_component_type = GLTFAccessor::COMPONENT_TYPE_UNSIGNED_SHORT;
}
if (_encode_buffer_view(p_state, changed_indices.ptr(), changed_indices.size(), GLTFAccessor::TYPE_SCALAR, sparse_accessor->sparse_indices_component_type, sparse_accessor->normalized, sparse_accessor->sparse_indices_byte_offset, false, buffer_view_i_indices) != OK) {
return -1;
@@ -2196,7 +2312,7 @@ GLTFAccessorIndex GLTFDocument::_encode_accessor_as_xform(Ref<GLTFState> p_state
}
int64_t size = p_state->buffers[0].size();
const GLTFAccessor::GLTFAccessorType accessor_type = GLTFAccessor::TYPE_MAT4;
- const int component_type = GLTFDocument::COMPONENT_TYPE_FLOAT;
+ const GLTFAccessor::GLTFComponentType component_type = GLTFAccessor::COMPONENT_TYPE_SINGLE_FLOAT;
accessor->max = type_max;
accessor->min = type_min;
@@ -2345,6 +2461,325 @@ Vector<Transform3D> GLTFDocument::_decode_accessor_as_xform(Ref<GLTFState> p_sta
return ret;
}
+Vector<Variant> GLTFDocument::_decode_accessor_as_variant(Ref<GLTFState> p_state, const GLTFAccessorIndex p_accessor, Variant::Type p_variant_type, GLTFAccessor::GLTFAccessorType p_accessor_type) {
+ const Vector<double> attribs = _decode_accessor(p_state, p_accessor, false);
+ Vector<Variant> ret;
+ ERR_FAIL_COND_V_MSG(attribs.is_empty(), ret, "glTF: The accessor was empty.");
+ const int component_count = COMPONENT_COUNT_FOR_ACCESSOR_TYPE[p_accessor_type];
+ ERR_FAIL_COND_V_MSG(attribs.size() % component_count != 0, ret, "glTF: The accessor size was not a multiple of the component count.");
+ const int ret_size = attribs.size() / component_count;
+ ret.resize(ret_size);
+ for (int i = 0; i < ret_size; i++) {
+ switch (p_variant_type) {
+ case Variant::BOOL: {
+ ret.write[i] = attribs[i * component_count] != 0.0;
+ } break;
+ case Variant::INT: {
+ ret.write[i] = (int64_t)attribs[i * component_count];
+ } break;
+ case Variant::FLOAT: {
+ ret.write[i] = attribs[i * component_count];
+ } break;
+ case Variant::VECTOR2:
+ case Variant::RECT2:
+ case Variant::VECTOR3:
+ case Variant::VECTOR4:
+ case Variant::PLANE:
+ case Variant::QUATERNION: {
+ // General-purpose code for importing glTF accessor data with any component count into structs up to 4 `real_t`s in size.
+ Variant v;
+ switch (component_count) {
+ case 1: {
+ v = Vector4(attribs[i * component_count], 0.0f, 0.0f, 0.0f);
+ } break;
+ case 2: {
+ v = Vector4(attribs[i * component_count], attribs[i * component_count + 1], 0.0f, 0.0f);
+ } break;
+ case 3: {
+ v = Vector4(attribs[i * component_count], attribs[i * component_count + 1], attribs[i * component_count + 2], 0.0f);
+ } break;
+ default: {
+ v = Vector4(attribs[i * component_count], attribs[i * component_count + 1], attribs[i * component_count + 2], attribs[i * component_count + 3]);
+ } break;
+ }
+ // Evil hack that relies on the structure of Variant, but it's the
+ // only way to accomplish this without a ton of code duplication.
+ *(Variant::Type *)&v = p_variant_type;
+ ret.write[i] = v;
+ } break;
+ case Variant::VECTOR2I:
+ case Variant::RECT2I:
+ case Variant::VECTOR3I:
+ case Variant::VECTOR4I: {
+ // General-purpose code for importing glTF accessor data with any component count into structs up to 4 `int32_t`s in size.
+ Variant v;
+ switch (component_count) {
+ case 1: {
+ v = Vector4i((int32_t)attribs[i * component_count], 0, 0, 0);
+ } break;
+ case 2: {
+ v = Vector4i((int32_t)attribs[i * component_count], (int32_t)attribs[i * component_count + 1], 0, 0);
+ } break;
+ case 3: {
+ v = Vector4i((int32_t)attribs[i * component_count], (int32_t)attribs[i * component_count + 1], (int32_t)attribs[i * component_count + 2], 0);
+ } break;
+ default: {
+ v = Vector4i((int32_t)attribs[i * component_count], (int32_t)attribs[i * component_count + 1], (int32_t)attribs[i * component_count + 2], (int32_t)attribs[i * component_count + 3]);
+ } break;
+ }
+ // Evil hack that relies on the structure of Variant, but it's the
+ // only way to accomplish this without a ton of code duplication.
+ *(Variant::Type *)&v = p_variant_type;
+ ret.write[i] = v;
+ } break;
+ // No more generalized hacks, each of the below types needs a lot of repetitive code.
+ case Variant::COLOR: {
+ Variant v;
+ switch (component_count) {
+ case 1: {
+ v = Color(attribs[i * component_count], 0.0f, 0.0f, 1.0f);
+ } break;
+ case 2: {
+ v = Color(attribs[i * component_count], attribs[i * component_count + 1], 0.0f, 1.0f);
+ } break;
+ case 3: {
+ v = Color(attribs[i * component_count], attribs[i * component_count + 1], attribs[i * component_count + 2], 1.0f);
+ } break;
+ default: {
+ v = Color(attribs[i * component_count], attribs[i * component_count + 1], attribs[i * component_count + 2], attribs[i * component_count + 3]);
+ } break;
+ }
+ ret.write[i] = v;
+ } break;
+ case Variant::TRANSFORM2D: {
+ Transform2D t;
+ switch (component_count) {
+ case 4: {
+ t.columns[0] = Vector2(attribs[i * component_count + 0], attribs[i * component_count + 1]);
+ t.columns[1] = Vector2(attribs[i * component_count + 2], attribs[i * component_count + 3]);
+ } break;
+ case 9: {
+ t.columns[0] = Vector2(attribs[i * component_count + 0], attribs[i * component_count + 1]);
+ t.columns[1] = Vector2(attribs[i * component_count + 3], attribs[i * component_count + 4]);
+ t.columns[2] = Vector2(attribs[i * component_count + 6], attribs[i * component_count + 7]);
+ } break;
+ case 16: {
+ t.columns[0] = Vector2(attribs[i * component_count + 0], attribs[i * component_count + 1]);
+ t.columns[1] = Vector2(attribs[i * component_count + 4], attribs[i * component_count + 5]);
+ t.columns[2] = Vector2(attribs[i * component_count + 12], attribs[i * component_count + 13]);
+ } break;
+ }
+ ret.write[i] = t;
+ } break;
+ case Variant::BASIS: {
+ Basis b;
+ switch (component_count) {
+ case 4: {
+ b.rows[0] = Vector3(attribs[i * component_count + 0], attribs[i * component_count + 2], 0.0f);
+ b.rows[1] = Vector3(attribs[i * component_count + 1], attribs[i * component_count + 3], 0.0f);
+ } break;
+ case 9: {
+ b.rows[0] = Vector3(attribs[i * component_count + 0], attribs[i * component_count + 3], attribs[i * component_count + 6]);
+ b.rows[1] = Vector3(attribs[i * component_count + 1], attribs[i * component_count + 4], attribs[i * component_count + 7]);
+ b.rows[2] = Vector3(attribs[i * component_count + 2], attribs[i * component_count + 5], attribs[i * component_count + 8]);
+ } break;
+ case 16: {
+ b.rows[0] = Vector3(attribs[i * component_count + 0], attribs[i * component_count + 4], attribs[i * component_count + 8]);
+ b.rows[1] = Vector3(attribs[i * component_count + 1], attribs[i * component_count + 5], attribs[i * component_count + 9]);
+ b.rows[2] = Vector3(attribs[i * component_count + 2], attribs[i * component_count + 6], attribs[i * component_count + 10]);
+ } break;
+ }
+ ret.write[i] = b;
+ } break;
+ case Variant::TRANSFORM3D: {
+ Transform3D t;
+ switch (component_count) {
+ case 4: {
+ t.basis.rows[0] = Vector3(attribs[i * component_count + 0], attribs[i * component_count + 2], 0.0f);
+ t.basis.rows[1] = Vector3(attribs[i * component_count + 1], attribs[i * component_count + 3], 0.0f);
+ } break;
+ case 9: {
+ t.basis.rows[0] = Vector3(attribs[i * component_count + 0], attribs[i * component_count + 3], attribs[i * component_count + 6]);
+ t.basis.rows[1] = Vector3(attribs[i * component_count + 1], attribs[i * component_count + 4], attribs[i * component_count + 7]);
+ t.basis.rows[2] = Vector3(attribs[i * component_count + 2], attribs[i * component_count + 5], attribs[i * component_count + 8]);
+ } break;
+ case 16: {
+ t.basis.rows[0] = Vector3(attribs[i * component_count + 0], attribs[i * component_count + 4], attribs[i * component_count + 8]);
+ t.basis.rows[1] = Vector3(attribs[i * component_count + 1], attribs[i * component_count + 5], attribs[i * component_count + 9]);
+ t.basis.rows[2] = Vector3(attribs[i * component_count + 2], attribs[i * component_count + 6], attribs[i * component_count + 10]);
+ t.origin = Vector3(attribs[i * component_count + 12], attribs[i * component_count + 13], attribs[i * component_count + 14]);
+ } break;
+ }
+ ret.write[i] = t;
+ } break;
+ case Variant::PROJECTION: {
+ Projection p;
+ switch (component_count) {
+ case 4: {
+ p.columns[0] = Vector4(attribs[i * component_count + 0], attribs[i * component_count + 1], 0.0f, 0.0f);
+ p.columns[1] = Vector4(attribs[i * component_count + 4], attribs[i * component_count + 5], 0.0f, 0.0f);
+ } break;
+ case 9: {
+ p.columns[0] = Vector4(attribs[i * component_count + 0], attribs[i * component_count + 1], attribs[i * component_count + 2], 0.0f);
+ p.columns[1] = Vector4(attribs[i * component_count + 4], attribs[i * component_count + 5], attribs[i * component_count + 6], 0.0f);
+ p.columns[2] = Vector4(attribs[i * component_count + 8], attribs[i * component_count + 9], attribs[i * component_count + 10], 0.0f);
+ } break;
+ case 16: {
+ p.columns[0] = Vector4(attribs[i * component_count + 0], attribs[i * component_count + 1], attribs[i * component_count + 2], attribs[i * component_count + 3]);
+ p.columns[1] = Vector4(attribs[i * component_count + 4], attribs[i * component_count + 5], attribs[i * component_count + 6], attribs[i * component_count + 7]);
+ p.columns[2] = Vector4(attribs[i * component_count + 8], attribs[i * component_count + 9], attribs[i * component_count + 10], attribs[i * component_count + 11]);
+ p.columns[3] = Vector4(attribs[i * component_count + 12], attribs[i * component_count + 13], attribs[i * component_count + 14], attribs[i * component_count + 15]);
+ } break;
+ }
+ ret.write[i] = p;
+ } break;
+ default: {
+ ERR_FAIL_V_MSG(ret, "glTF: Cannot decode accessor as Variant of type " + Variant::get_type_name(p_variant_type) + ".");
+ }
+ }
+ }
+ return ret;
+}
+
+GLTFAccessorIndex GLTFDocument::_encode_accessor_as_variant(Ref<GLTFState> p_state, Vector<Variant> p_attribs, Variant::Type p_variant_type, GLTFAccessor::GLTFAccessorType p_accessor_type, GLTFAccessor::GLTFComponentType p_component_type) {
+ const int accessor_component_count = COMPONENT_COUNT_FOR_ACCESSOR_TYPE[p_accessor_type];
+ Vector<double> encoded_attribs;
+ for (const Variant &v : p_attribs) {
+ switch (p_variant_type) {
+ case Variant::NIL:
+ case Variant::BOOL:
+ case Variant::INT:
+ case Variant::FLOAT: {
+ // For scalar values, just append them. Variant can convert all of these to double. Some padding may also be needed.
+ encoded_attribs.append(v);
+ if (unlikely(accessor_component_count > 1)) {
+ for (int i = 1; i < accessor_component_count; i++) {
+ encoded_attribs.append(0.0);
+ }
+ }
+ } break;
+ case Variant::VECTOR2:
+ case Variant::VECTOR2I:
+ case Variant::VECTOR3:
+ case Variant::VECTOR3I:
+ case Variant::VECTOR4:
+ case Variant::VECTOR4I: {
+ // Variant can handle converting Vector2/2i/3/3i/4/4i to Vector4 for us.
+ Vector4 vec = v;
+ if (likely(accessor_component_count < 5)) {
+ for (int i = 0; i < accessor_component_count; i++) {
+ encoded_attribs.append(vec[i]);
+ }
+ }
+ } break;
+ case Variant::PLANE: {
+ Plane p = v;
+ if (likely(accessor_component_count == 4)) {
+ encoded_attribs.append(p.normal.x);
+ encoded_attribs.append(p.normal.y);
+ encoded_attribs.append(p.normal.z);
+ encoded_attribs.append(p.d);
+ }
+ } break;
+ case Variant::QUATERNION: {
+ Quaternion q = v;
+ if (likely(accessor_component_count < 5)) {
+ for (int i = 0; i < accessor_component_count; i++) {
+ encoded_attribs.append(q[i]);
+ }
+ }
+ } break;
+ case Variant::COLOR: {
+ Color c = v;
+ if (likely(accessor_component_count < 5)) {
+ for (int i = 0; i < accessor_component_count; i++) {
+ encoded_attribs.append(c[i]);
+ }
+ }
+ } break;
+ case Variant::RECT2:
+ case Variant::RECT2I: {
+ // Variant can handle converting Rect2i to Rect2 for us.
+ Rect2 r = v;
+ if (likely(accessor_component_count == 4)) {
+ encoded_attribs.append(r.position.x);
+ encoded_attribs.append(r.position.y);
+ encoded_attribs.append(r.size.x);
+ encoded_attribs.append(r.size.y);
+ }
+ } break;
+ case Variant::TRANSFORM2D:
+ case Variant::BASIS:
+ case Variant::TRANSFORM3D:
+ case Variant::PROJECTION: {
+ // Variant can handle converting Transform2D/Transform3D/Basis to Projection for us.
+ Projection p = v;
+ if (accessor_component_count == 16) {
+ for (int i = 0; i < 4; i++) {
+ encoded_attribs.append(p.columns[i][0]);
+ encoded_attribs.append(p.columns[i][1]);
+ encoded_attribs.append(p.columns[i][2]);
+ encoded_attribs.append(p.columns[i][3]);
+ }
+ } else if (accessor_component_count == 9) {
+ for (int i = 0; i < 3; i++) {
+ encoded_attribs.append(p.columns[i][0]);
+ encoded_attribs.append(p.columns[i][1]);
+ encoded_attribs.append(p.columns[i][2]);
+ }
+ } else if (accessor_component_count == 4) {
+ encoded_attribs.append(p.columns[0][0]);
+ encoded_attribs.append(p.columns[0][1]);
+ encoded_attribs.append(p.columns[1][0]);
+ encoded_attribs.append(p.columns[1][1]);
+ }
+ } break;
+ default: {
+ ERR_FAIL_V_MSG(-1, "glTF: Cannot encode accessor from Variant of type " + Variant::get_type_name(p_variant_type) + ".");
+ }
+ }
+ }
+ // Determine the min and max values for the accessor.
+ Vector<double> type_max;
+ type_max.resize(accessor_component_count);
+ Vector<double> type_min;
+ type_min.resize(accessor_component_count);
+ for (int i = 0; i < encoded_attribs.size(); i++) {
+ if (Math::is_zero_approx(encoded_attribs[i])) {
+ encoded_attribs.write[i] = 0.0;
+ } else {
+ encoded_attribs.write[i] = _filter_number(encoded_attribs[i]);
+ }
+ }
+ for (int i = 0; i < p_attribs.size(); i++) {
+ _calc_accessor_min_max(i, accessor_component_count, type_max, encoded_attribs, type_min);
+ }
+ _round_min_max_components(type_min, type_max);
+ // Encode the data in a buffer view.
+ GLTFBufferIndex buffer_view_index = 0;
+ if (p_state->buffers.is_empty()) {
+ p_state->buffers.push_back(Vector<uint8_t>());
+ }
+ const int64_t buffer_size = p_state->buffers[buffer_view_index].size();
+ Error err = _encode_buffer_view(p_state, encoded_attribs.ptr(), p_attribs.size(), p_accessor_type, p_component_type, false, buffer_size, false, buffer_view_index);
+ if (err != OK) {
+ return -1;
+ }
+ // Create the accessor and fill it with the data.
+ Ref<GLTFAccessor> accessor;
+ accessor.instantiate();
+ accessor->max = type_max;
+ accessor->min = type_min;
+ accessor->count = p_attribs.size();
+ accessor->accessor_type = p_accessor_type;
+ accessor->component_type = p_component_type;
+ accessor->byte_offset = 0;
+ accessor->buffer_view = buffer_view_index;
+ const GLTFAccessorIndex new_accessor_index = p_state->accessors.size();
+ p_state->accessors.push_back(accessor);
+ return new_accessor_index;
+}
+
Error GLTFDocument::_serialize_meshes(Ref<GLTFState> p_state) {
Array meshes;
for (GLTFMeshIndex gltf_mesh_i = 0; gltf_mesh_i < p_state->meshes.size(); gltf_mesh_i++) {
@@ -2780,41 +3215,42 @@ Error GLTFDocument::_parse_meshes(Ref<GLTFState> p_state) {
Array meshes = p_state->json["meshes"];
for (GLTFMeshIndex i = 0; i < meshes.size(); i++) {
print_verbose("glTF: Parsing mesh: " + itos(i));
- Dictionary d = meshes[i];
+ Dictionary mesh_dict = meshes[i];
Ref<GLTFMesh> mesh;
mesh.instantiate();
bool has_vertex_color = false;
- ERR_FAIL_COND_V(!d.has("primitives"), ERR_PARSE_ERROR);
+ ERR_FAIL_COND_V(!mesh_dict.has("primitives"), ERR_PARSE_ERROR);
- Array primitives = d["primitives"];
- const Dictionary &extras = d.has("extras") ? (Dictionary)d["extras"] : Dictionary();
+ Array primitives = mesh_dict["primitives"];
+ const Dictionary &extras = mesh_dict.has("extras") ? (Dictionary)mesh_dict["extras"] : Dictionary();
_attach_extras_to_meta(extras, mesh);
Ref<ImporterMesh> import_mesh;
import_mesh.instantiate();
String mesh_name = "mesh";
- if (d.has("name") && !String(d["name"]).is_empty()) {
- mesh_name = d["name"];
+ if (mesh_dict.has("name") && !String(mesh_dict["name"]).is_empty()) {
+ mesh_name = mesh_dict["name"];
mesh->set_original_name(mesh_name);
}
import_mesh->set_name(_gen_unique_name(p_state, vformat("%s_%s", p_state->scene_name, mesh_name)));
mesh->set_name(import_mesh->get_name());
+ TypedArray<Material> instance_materials;
for (int j = 0; j < primitives.size(); j++) {
uint64_t flags = RS::ARRAY_FLAG_COMPRESS_ATTRIBUTES;
- Dictionary p = primitives[j];
+ Dictionary mesh_prim = primitives[j];
Array array;
array.resize(Mesh::ARRAY_MAX);
- ERR_FAIL_COND_V(!p.has("attributes"), ERR_PARSE_ERROR);
+ ERR_FAIL_COND_V(!mesh_prim.has("attributes"), ERR_PARSE_ERROR);
- Dictionary a = p["attributes"];
+ Dictionary a = mesh_prim["attributes"];
Mesh::PrimitiveType primitive = Mesh::PRIMITIVE_TRIANGLES;
- if (p.has("mode")) {
- const int mode = p["mode"];
+ if (mesh_prim.has("mode")) {
+ const int mode = mesh_prim["mode"];
ERR_FAIL_INDEX_V(mode, 7, ERR_FILE_CORRUPT);
// Convert mesh.primitive.mode to Godot Mesh enum. See:
// https://www.khronos.org/registry/glTF/specs/2.0/glTF-2.0.html#_mesh_primitive_mode
@@ -2845,8 +3281,8 @@ Error GLTFDocument::_parse_meshes(Ref<GLTFState> p_state) {
Vector<int> indices_mapping;
Vector<int> indices_rev_mapping;
Vector<int> indices_vec4_mapping;
- if (p.has("indices")) {
- indices = _decode_accessor_as_ints(p_state, p["indices"], false);
+ if (mesh_prim.has("indices")) {
+ indices = _decode_accessor_as_ints(p_state, mesh_prim["indices"], false);
const int is = indices.size();
if (primitive == Mesh::PRIMITIVE_TRIANGLES) {
@@ -3104,7 +3540,7 @@ Error GLTFDocument::_parse_meshes(Ref<GLTFState> p_state) {
}
}
- if (p_state->force_disable_compression || is_mesh_2d || !a.has("POSITION") || !a.has("NORMAL") || p.has("targets") || (a.has("JOINTS_0") || a.has("JOINTS_1"))) {
+ if (p_state->force_disable_compression || is_mesh_2d || !a.has("POSITION") || !a.has("NORMAL") || mesh_prim.has("targets") || (a.has("JOINTS_0") || a.has("JOINTS_1"))) {
flags &= ~RS::ARRAY_FLAG_COMPRESS_ATTRIBUTES;
}
@@ -3136,9 +3572,9 @@ Error GLTFDocument::_parse_meshes(Ref<GLTFState> p_state) {
Array morphs;
// Blend shapes
- if (p.has("targets")) {
+ if (mesh_prim.has("targets")) {
print_verbose("glTF: Mesh has targets");
- const Array &targets = p["targets"];
+ const Array &targets = mesh_prim["targets"];
import_mesh->set_blend_shape_mode(Mesh::BLEND_SHAPE_MODE_NORMALIZED);
@@ -3269,8 +3705,8 @@ Error GLTFDocument::_parse_meshes(Ref<GLTFState> p_state) {
Ref<Material> mat;
String mat_name;
if (!p_state->discard_meshes_and_materials) {
- if (p.has("material")) {
- const int material = p["material"];
+ if (mesh_prim.has("material")) {
+ const int material = mesh_prim["material"];
ERR_FAIL_INDEX_V(material, p_state->materials.size(), ERR_FILE_CORRUPT);
Ref<Material> mat3d = p_state->materials[material];
ERR_FAIL_COND_V(mat3d.is_null(), ERR_FILE_CORRUPT);
@@ -3290,6 +3726,7 @@ Error GLTFDocument::_parse_meshes(Ref<GLTFState> p_state) {
mat = mat3d;
}
ERR_FAIL_COND_V(mat.is_null(), ERR_FILE_CORRUPT);
+ instance_materials.append(mat);
mat_name = mat->get_name();
}
import_mesh->add_surface(primitive, array, morphs,
@@ -3302,8 +3739,8 @@ Error GLTFDocument::_parse_meshes(Ref<GLTFState> p_state) {
blend_weights.write[weight_i] = 0.0f;
}
- if (d.has("weights")) {
- const Array &weights = d["weights"];
+ if (mesh_dict.has("weights")) {
+ const Array &weights = mesh_dict["weights"];
for (int j = 0; j < weights.size(); j++) {
if (j >= blend_weights.size()) {
break;
@@ -3312,6 +3749,7 @@ Error GLTFDocument::_parse_meshes(Ref<GLTFState> p_state) {
}
}
mesh->set_blend_weights(blend_weights);
+ mesh->set_instance_materials(instance_materials);
mesh->set_mesh(import_mesh);
p_state->meshes.push_back(mesh);
@@ -3503,18 +3941,19 @@ void GLTFDocument::_parse_image_save_image(Ref<GLTFState> p_state, const Vector<
}
#ifdef TOOLS_ENABLED
if (Engine::get_singleton()->is_editor_hint() && handling == GLTFState::GLTFHandleBinary::HANDLE_BINARY_EXTRACT_TEXTURES) {
- if (p_state->base_path.is_empty()) {
- p_state->images.push_back(Ref<Texture2D>());
- p_state->source_images.push_back(Ref<Image>());
- } else if (p_image->get_name().is_empty()) {
- WARN_PRINT(vformat("glTF: Image index '%d' couldn't be named. Skipping it.", p_index));
- p_state->images.push_back(Ref<Texture2D>());
- p_state->source_images.push_back(Ref<Image>());
+ if (p_state->extract_path.is_empty()) {
+ WARN_PRINT("glTF: Couldn't extract image because the base and extract paths are empty. It will be loaded directly instead, uncompressed.");
+ } else if (p_state->extract_path.begins_with("res://.godot/imported")) {
+ WARN_PRINT(vformat("glTF: Extract path is in the imported directory. Image index '%d' will be loaded directly, uncompressed.", p_index));
} else {
+ if (p_image->get_name().is_empty()) {
+ WARN_PRINT(vformat("glTF: Image index '%d' did not have a name. It will be automatically given a name based on its index.", p_index));
+ p_image->set_name(itos(p_index));
+ }
bool must_import = true;
Vector<uint8_t> img_data = p_image->get_data();
Dictionary generator_parameters;
- String file_path = p_state->get_base_path().path_join(p_state->filename.get_basename() + "_" + p_image->get_name());
+ String file_path = p_state->get_extract_path().path_join(p_state->get_extract_prefix() + "_" + p_image->get_name());
file_path += p_file_extension.is_empty() ? ".png" : p_file_extension;
if (FileAccess::exists(file_path + ".import")) {
Ref<ConfigFile> config;
@@ -3561,14 +4000,11 @@ void GLTFDocument::_parse_image_save_image(Ref<GLTFState> p_state, const Vector<
if (saved_image.is_valid()) {
p_state->images.push_back(saved_image);
p_state->source_images.push_back(saved_image->get_image());
+ return;
} else {
- WARN_PRINT(vformat("glTF: Image index '%d' couldn't be loaded with the name: %s. Skipping it.", p_index, p_image->get_name()));
- // Placeholder to keep count.
- p_state->images.push_back(Ref<Texture2D>());
- p_state->source_images.push_back(Ref<Image>());
+ WARN_PRINT(vformat("glTF: Image index '%d' with the name '%s' couldn't be imported. It will be loaded directly instead, uncompressed.", p_index, p_image->get_name()));
}
}
- return;
}
#endif // TOOLS_ENABLED
if (handling == GLTFState::GLTFHandleBinary::HANDLE_BINARY_EMBED_AS_BASISU) {
@@ -3651,16 +4087,19 @@ Error GLTFDocument::_parse_images(Ref<GLTFState> p_state, const String &p_base_p
ERR_FAIL_COND_V(p_base_path.is_empty(), ERR_INVALID_PARAMETER);
uri = uri.uri_decode();
uri = p_base_path.path_join(uri).replace("\\", "/"); // Fix for Windows.
- // ResourceLoader will rely on the file extension to use the relevant loader.
- // The spec says that if mimeType is defined, it should take precedence (e.g.
- // there could be a `.png` image which is actually JPEG), but there's no easy
- // API for that in Godot, so we'd have to load as a buffer (i.e. embedded in
- // the material), so we only do that only as fallback.
- Ref<Texture2D> texture = ResourceLoader::load(uri);
- if (texture.is_valid()) {
- p_state->images.push_back(texture);
- p_state->source_images.push_back(texture->get_image());
- continue;
+ // If the image is in the .godot/imported directory, we can't use ResourceLoader.
+ if (!p_base_path.begins_with("res://.godot/imported")) {
+ // ResourceLoader will rely on the file extension to use the relevant loader.
+ // The spec says that if mimeType is defined, it should take precedence (e.g.
+ // there could be a `.png` image which is actually JPEG), but there's no easy
+ // API for that in Godot, so we'd have to load as a buffer (i.e. embedded in
+ // the material), so we only do that only as fallback.
+ Ref<Texture2D> texture = ResourceLoader::load(uri, "Texture2D");
+ if (texture.is_valid()) {
+ p_state->images.push_back(texture);
+ p_state->source_images.push_back(texture->get_image());
+ continue;
+ }
}
// mimeType is optional, but if we have it in the file extension, let's use it.
// If the mimeType does not match with the file extension, either it should be
@@ -4783,7 +5222,7 @@ Error GLTFDocument::_serialize_animations(Ref<GLTFState> p_state) {
for (GLTFAnimationIndex animation_i = 0; animation_i < p_state->animations.size(); animation_i++) {
Dictionary d;
Ref<GLTFAnimation> gltf_animation = p_state->animations[animation_i];
- if (!gltf_animation->get_tracks().size()) {
+ if (gltf_animation->is_empty_of_tracks()) {
continue;
}
@@ -4792,18 +5231,18 @@ Error GLTFDocument::_serialize_animations(Ref<GLTFState> p_state) {
}
Array channels;
Array samplers;
-
- for (KeyValue<int, GLTFAnimation::Track> &track_i : gltf_animation->get_tracks()) {
- GLTFAnimation::Track track = track_i.value;
+ // Serialize glTF node tracks with the vanilla glTF animation system.
+ for (KeyValue<int, GLTFAnimation::NodeTrack> &track_i : gltf_animation->get_node_tracks()) {
+ GLTFAnimation::NodeTrack track = track_i.value;
if (track.position_track.times.size()) {
Dictionary t;
t["sampler"] = samplers.size();
Dictionary s;
s["interpolation"] = interpolation_to_string(track.position_track.interpolation);
- Vector<real_t> times = Variant(track.position_track.times);
+ Vector<double> times = track.position_track.times;
s["input"] = _encode_accessor_as_floats(p_state, times, false);
- Vector<Vector3> values = Variant(track.position_track.values);
+ Vector<Vector3> values = track.position_track.values;
s["output"] = _encode_accessor_as_vec3(p_state, values, false);
samplers.push_back(s);
@@ -4821,7 +5260,7 @@ Error GLTFDocument::_serialize_animations(Ref<GLTFState> p_state) {
Dictionary s;
s["interpolation"] = interpolation_to_string(track.rotation_track.interpolation);
- Vector<real_t> times = Variant(track.rotation_track.times);
+ Vector<double> times = track.rotation_track.times;
s["input"] = _encode_accessor_as_floats(p_state, times, false);
Vector<Quaternion> values = track.rotation_track.values;
s["output"] = _encode_accessor_as_quaternions(p_state, values, false);
@@ -4841,9 +5280,9 @@ Error GLTFDocument::_serialize_animations(Ref<GLTFState> p_state) {
Dictionary s;
s["interpolation"] = interpolation_to_string(track.scale_track.interpolation);
- Vector<real_t> times = Variant(track.scale_track.times);
+ Vector<double> times = track.scale_track.times;
s["input"] = _encode_accessor_as_floats(p_state, times, false);
- Vector<Vector3> values = Variant(track.scale_track.values);
+ Vector<Vector3> values = track.scale_track.values;
s["output"] = _encode_accessor_as_vec3(p_state, values, false);
samplers.push_back(s);
@@ -4866,7 +5305,7 @@ Error GLTFDocument::_serialize_animations(Ref<GLTFState> p_state) {
Dictionary t;
t["sampler"] = samplers.size();
Dictionary s;
- Vector<real_t> times;
+ Vector<double> times;
const double increment = 1.0 / p_state->get_bake_fps();
{
double time = 0.0;
@@ -4907,8 +5346,8 @@ Error GLTFDocument::_serialize_animations(Ref<GLTFState> p_state) {
track.weight_tracks.write[track_idx].values = weight_track;
}
- Vector<real_t> all_track_times = times;
- Vector<real_t> all_track_values;
+ Vector<double> all_track_times = times;
+ Vector<double> all_track_values;
int32_t values_size = track.weight_tracks[0].values.size();
int32_t weight_tracks_size = track.weight_tracks.size();
all_track_values.resize(weight_tracks_size * values_size);
@@ -4935,6 +5374,33 @@ Error GLTFDocument::_serialize_animations(Ref<GLTFState> p_state) {
channels.push_back(t);
}
}
+ if (!gltf_animation->get_pointer_tracks().is_empty()) {
+ // Serialize glTF pointer tracks with the KHR_animation_pointer extension.
+ if (!p_state->extensions_used.has("KHR_animation_pointer")) {
+ p_state->extensions_used.push_back("KHR_animation_pointer");
+ }
+ for (KeyValue<String, GLTFAnimation::Channel<Variant>> &pointer_track_iter : gltf_animation->get_pointer_tracks()) {
+ const String &json_pointer = pointer_track_iter.key;
+ const GLTFAnimation::Channel<Variant> &pointer_track = pointer_track_iter.value;
+ const Ref<GLTFObjectModelProperty> &obj_model_prop = p_state->object_model_properties[json_pointer];
+ Dictionary channel;
+ channel["sampler"] = samplers.size();
+ Dictionary channel_target;
+ channel_target["path"] = "pointer";
+ Dictionary channel_target_ext;
+ Dictionary channel_target_ext_khr_anim_ptr;
+ channel_target_ext_khr_anim_ptr["pointer"] = json_pointer;
+ channel_target_ext["KHR_animation_pointer"] = channel_target_ext_khr_anim_ptr;
+ channel_target["extensions"] = channel_target_ext;
+ channel["target"] = channel_target;
+ channels.push_back(channel);
+ Dictionary sampler;
+ sampler["input"] = _encode_accessor_as_floats(p_state, pointer_track.times, false);
+ sampler["interpolation"] = interpolation_to_string(pointer_track.interpolation);
+ sampler["output"] = _encode_accessor_as_variant(p_state, pointer_track.values, obj_model_prop->get_variant_type(), obj_model_prop->get_accessor_type());
+ samplers.push_back(sampler);
+ }
+ }
if (channels.size() && samplers.size()) {
d["channels"] = channels;
d["samplers"] = samplers;
@@ -4959,21 +5425,21 @@ Error GLTFDocument::_parse_animations(Ref<GLTFState> p_state) {
const Array &animations = p_state->json["animations"];
- for (GLTFAnimationIndex i = 0; i < animations.size(); i++) {
- const Dictionary &d = animations[i];
+ for (GLTFAnimationIndex anim_index = 0; anim_index < animations.size(); anim_index++) {
+ const Dictionary &anim_dict = animations[anim_index];
Ref<GLTFAnimation> animation;
animation.instantiate();
- if (!d.has("channels") || !d.has("samplers")) {
+ if (!anim_dict.has("channels") || !anim_dict.has("samplers")) {
continue;
}
- Array channels = d["channels"];
- Array samplers = d["samplers"];
+ Array channels = anim_dict["channels"];
+ Array samplers = anim_dict["samplers"];
- if (d.has("name")) {
- const String anim_name = d["name"];
+ if (anim_dict.has("name")) {
+ const String anim_name = anim_dict["name"];
const String anim_name_lower = anim_name.to_lower();
if (anim_name_lower.begins_with("loop") || anim_name_lower.ends_with("loop") || anim_name_lower.begins_with("cycle") || anim_name_lower.ends_with("cycle")) {
animation->set_loop(true);
@@ -4982,46 +5448,22 @@ Error GLTFDocument::_parse_animations(Ref<GLTFState> p_state) {
animation->set_name(_gen_unique_animation_name(p_state, anim_name));
}
- for (int j = 0; j < channels.size(); j++) {
- const Dictionary &c = channels[j];
- if (!c.has("target")) {
- continue;
- }
-
- const Dictionary &t = c["target"];
- if (!t.has("node") || !t.has("path")) {
- continue;
- }
-
- ERR_FAIL_COND_V(!c.has("sampler"), ERR_PARSE_ERROR);
- const int sampler = c["sampler"];
- ERR_FAIL_INDEX_V(sampler, samplers.size(), ERR_PARSE_ERROR);
-
- GLTFNodeIndex node = t["node"];
- String path = t["path"];
-
- ERR_FAIL_INDEX_V(node, p_state->nodes.size(), ERR_PARSE_ERROR);
-
- GLTFAnimation::Track *track = nullptr;
-
- if (!animation->get_tracks().has(node)) {
- animation->get_tracks()[node] = GLTFAnimation::Track();
- }
-
- track = &animation->get_tracks()[node];
-
- const Dictionary &s = samplers[sampler];
-
- ERR_FAIL_COND_V(!s.has("input"), ERR_PARSE_ERROR);
- ERR_FAIL_COND_V(!s.has("output"), ERR_PARSE_ERROR);
-
- const int input = s["input"];
- const int output = s["output"];
-
+ for (int channel_index = 0; channel_index < channels.size(); channel_index++) {
+ const Dictionary &anim_channel = channels[channel_index];
+ ERR_FAIL_COND_V_MSG(!anim_channel.has("sampler"), ERR_PARSE_ERROR, "glTF: Animation channel missing required 'sampler' property.");
+ ERR_FAIL_COND_V_MSG(!anim_channel.has("target"), ERR_PARSE_ERROR, "glTF: Animation channel missing required 'target' property.");
+ // Parse sampler.
+ const int sampler_index = anim_channel["sampler"];
+ ERR_FAIL_INDEX_V(sampler_index, samplers.size(), ERR_PARSE_ERROR);
+ const Dictionary &sampler_dict = samplers[sampler_index];
+ ERR_FAIL_COND_V(!sampler_dict.has("input"), ERR_PARSE_ERROR);
+ ERR_FAIL_COND_V(!sampler_dict.has("output"), ERR_PARSE_ERROR);
+ const int input_time_accessor_index = sampler_dict["input"];
+ const int output_value_accessor_index = sampler_dict["output"];
GLTFAnimation::Interpolation interp = GLTFAnimation::INTERP_LINEAR;
int output_count = 1;
- if (s.has("interpolation")) {
- const String &in = s["interpolation"];
+ if (sampler_dict.has("interpolation")) {
+ const String &in = sampler_dict["interpolation"];
if (in == "STEP") {
interp = GLTFAnimation::INTERP_STEP;
} else if (in == "LINEAR") {
@@ -5034,52 +5476,83 @@ Error GLTFDocument::_parse_animations(Ref<GLTFState> p_state) {
output_count = 3;
}
}
+ const Vector<double> times = _decode_accessor(p_state, input_time_accessor_index, false);
+ // Parse target.
+ const Dictionary &anim_target = anim_channel["target"];
+ ERR_FAIL_COND_V_MSG(!anim_target.has("path"), ERR_PARSE_ERROR, "glTF: Animation channel target missing required 'path' property.");
+ String path = anim_target["path"];
+ if (path == "pointer") {
+ ERR_FAIL_COND_V(!anim_target.has("extensions"), ERR_PARSE_ERROR);
+ Dictionary target_extensions = anim_target["extensions"];
+ ERR_FAIL_COND_V(!target_extensions.has("KHR_animation_pointer"), ERR_PARSE_ERROR);
+ Dictionary khr_anim_ptr = target_extensions["KHR_animation_pointer"];
+ ERR_FAIL_COND_V(!khr_anim_ptr.has("pointer"), ERR_PARSE_ERROR);
+ String anim_json_ptr = khr_anim_ptr["pointer"];
+ _parse_animation_pointer(p_state, anim_json_ptr, animation, interp, times, output_value_accessor_index);
+ } else {
+ // If it's not a pointer, it's a regular animation channel from vanilla glTF (pos/rot/scale/weights).
+ if (!anim_target.has("node")) {
+ WARN_PRINT("glTF: Animation channel target missing 'node' property. Ignoring this channel.");
+ continue;
+ }
- const Vector<float> times = _decode_accessor_as_floats(p_state, input, false);
- if (path == "translation") {
- const Vector<Vector3> positions = _decode_accessor_as_vec3(p_state, output, false);
- track->position_track.interpolation = interp;
- track->position_track.times = Variant(times); //convert via variant
- track->position_track.values = Variant(positions); //convert via variant
- } else if (path == "rotation") {
- const Vector<Quaternion> rotations = _decode_accessor_as_quaternion(p_state, output, false);
- track->rotation_track.interpolation = interp;
- track->rotation_track.times = Variant(times); //convert via variant
- track->rotation_track.values = rotations;
- } else if (path == "scale") {
- const Vector<Vector3> scales = _decode_accessor_as_vec3(p_state, output, false);
- track->scale_track.interpolation = interp;
- track->scale_track.times = Variant(times); //convert via variant
- track->scale_track.values = Variant(scales); //convert via variant
- } else if (path == "weights") {
- const Vector<float> weights = _decode_accessor_as_floats(p_state, output, false);
-
- ERR_FAIL_INDEX_V(p_state->nodes[node]->mesh, p_state->meshes.size(), ERR_PARSE_ERROR);
- Ref<GLTFMesh> mesh = p_state->meshes[p_state->nodes[node]->mesh];
- ERR_CONTINUE(!mesh->get_blend_weights().size());
- const int wc = mesh->get_blend_weights().size();
-
- track->weight_tracks.resize(wc);
-
- const int expected_value_count = times.size() * output_count * wc;
- ERR_CONTINUE_MSG(weights.size() != expected_value_count, "Invalid weight data, expected " + itos(expected_value_count) + " weight values, got " + itos(weights.size()) + " instead.");
-
- const int wlen = weights.size() / wc;
- for (int k = 0; k < wc; k++) { //separate tracks, having them together is not such a good idea
- GLTFAnimation::Channel<real_t> cf;
- cf.interpolation = interp;
- cf.times = Variant(times);
- Vector<real_t> wdata;
- wdata.resize(wlen);
- for (int l = 0; l < wlen; l++) {
- wdata.write[l] = weights[l * wc + k];
- }
+ GLTFNodeIndex node = anim_target["node"];
+
+ ERR_FAIL_INDEX_V(node, p_state->nodes.size(), ERR_PARSE_ERROR);
- cf.values = wdata;
- track->weight_tracks.write[k] = cf;
+ GLTFAnimation::NodeTrack *track = nullptr;
+
+ if (!animation->get_node_tracks().has(node)) {
+ animation->get_node_tracks()[node] = GLTFAnimation::NodeTrack();
+ }
+
+ track = &animation->get_node_tracks()[node];
+
+ if (path == "translation") {
+ const Vector<Vector3> positions = _decode_accessor_as_vec3(p_state, output_value_accessor_index, false);
+ track->position_track.interpolation = interp;
+ track->position_track.times = times;
+ track->position_track.values = positions;
+ } else if (path == "rotation") {
+ const Vector<Quaternion> rotations = _decode_accessor_as_quaternion(p_state, output_value_accessor_index, false);
+ track->rotation_track.interpolation = interp;
+ track->rotation_track.times = times;
+ track->rotation_track.values = rotations;
+ } else if (path == "scale") {
+ const Vector<Vector3> scales = _decode_accessor_as_vec3(p_state, output_value_accessor_index, false);
+ track->scale_track.interpolation = interp;
+ track->scale_track.times = times;
+ track->scale_track.values = scales;
+ } else if (path == "weights") {
+ const Vector<float> weights = _decode_accessor_as_floats(p_state, output_value_accessor_index, false);
+
+ ERR_FAIL_INDEX_V(p_state->nodes[node]->mesh, p_state->meshes.size(), ERR_PARSE_ERROR);
+ Ref<GLTFMesh> mesh = p_state->meshes[p_state->nodes[node]->mesh];
+ const int wc = mesh->get_blend_weights().size();
+ ERR_CONTINUE_MSG(wc == 0, "glTF: Animation tried to animate weights, but mesh has no weights.");
+
+ track->weight_tracks.resize(wc);
+
+ const int expected_value_count = times.size() * output_count * wc;
+ ERR_CONTINUE_MSG(weights.size() != expected_value_count, "Invalid weight data, expected " + itos(expected_value_count) + " weight values, got " + itos(weights.size()) + " instead.");
+
+ const int wlen = weights.size() / wc;
+ for (int k = 0; k < wc; k++) { //separate tracks, having them together is not such a good idea
+ GLTFAnimation::Channel<real_t> cf;
+ cf.interpolation = interp;
+ cf.times = Variant(times);
+ Vector<real_t> wdata;
+ wdata.resize(wlen);
+ for (int l = 0; l < wlen; l++) {
+ wdata.write[l] = weights[l * wc + k];
+ }
+
+ cf.values = wdata;
+ track->weight_tracks.write[k] = cf;
+ }
+ } else {
+ WARN_PRINT("Invalid path '" + path + "'.");
}
- } else {
- WARN_PRINT("Invalid path '" + path + "'.");
}
}
@@ -5091,6 +5564,96 @@ Error GLTFDocument::_parse_animations(Ref<GLTFState> p_state) {
return OK;
}
+void GLTFDocument::_parse_animation_pointer(Ref<GLTFState> p_state, const String &p_animation_json_pointer, const Ref<GLTFAnimation> p_gltf_animation, const GLTFAnimation::Interpolation p_interp, const Vector<double> &p_times, const int p_output_value_accessor_index) {
+ // Special case: Convert TRS animation pointers to node track pos/rot/scale.
+ // This is required to handle skeleton bones, and improves performance for regular nodes.
+ // Mark this as unlikely because TRS animation pointers are not recommended,
+ // since vanilla glTF animations can already animate TRS properties directly.
+ // But having this code exist is required to be spec-compliant and handle all test files.
+ // Note that TRS still needs to be handled in the general case as well, for KHR_interactivity.
+ const PackedStringArray split = p_animation_json_pointer.split("/", false, 3);
+ if (unlikely(split.size() == 3 && split[0] == "nodes" && (split[2] == "translation" || split[2] == "rotation" || split[2] == "scale" || split[2] == "matrix" || split[2] == "weights"))) {
+ const GLTFNodeIndex node_index = split[1].to_int();
+ HashMap<int, GLTFAnimation::NodeTrack> &node_tracks = p_gltf_animation->get_node_tracks();
+ if (!node_tracks.has(node_index)) {
+ node_tracks[node_index] = GLTFAnimation::NodeTrack();
+ }
+ GLTFAnimation::NodeTrack *track = &node_tracks[node_index];
+ if (split[2] == "translation") {
+ const Vector<Vector3> positions = _decode_accessor_as_vec3(p_state, p_output_value_accessor_index, false);
+ track->position_track.interpolation = p_interp;
+ track->position_track.times = p_times;
+ track->position_track.values = positions;
+ } else if (split[2] == "rotation") {
+ const Vector<Quaternion> rotations = _decode_accessor_as_quaternion(p_state, p_output_value_accessor_index, false);
+ track->rotation_track.interpolation = p_interp;
+ track->rotation_track.times = p_times;
+ track->rotation_track.values = rotations;
+ } else if (split[2] == "scale") {
+ const Vector<Vector3> scales = _decode_accessor_as_vec3(p_state, p_output_value_accessor_index, false);
+ track->scale_track.interpolation = p_interp;
+ track->scale_track.times = p_times;
+ track->scale_track.values = scales;
+ } else if (split[2] == "matrix") {
+ const Vector<Transform3D> transforms = _decode_accessor_as_xform(p_state, p_output_value_accessor_index, false);
+ track->position_track.interpolation = p_interp;
+ track->position_track.times = p_times;
+ track->position_track.values.resize(transforms.size());
+ track->rotation_track.interpolation = p_interp;
+ track->rotation_track.times = p_times;
+ track->rotation_track.values.resize(transforms.size());
+ track->scale_track.interpolation = p_interp;
+ track->scale_track.times = p_times;
+ track->scale_track.values.resize(transforms.size());
+ for (int i = 0; i < transforms.size(); i++) {
+ track->position_track.values.write[i] = transforms[i].get_origin();
+ track->rotation_track.values.write[i] = transforms[i].basis.get_rotation_quaternion();
+ track->scale_track.values.write[i] = transforms[i].basis.get_scale();
+ }
+ } else { // if (split[2] == "weights")
+ const Vector<float> accessor_weights = _decode_accessor_as_floats(p_state, p_output_value_accessor_index, false);
+ const GLTFMeshIndex mesh_index = p_state->nodes[node_index]->mesh;
+ ERR_FAIL_INDEX(mesh_index, p_state->meshes.size());
+ const Ref<GLTFMesh> gltf_mesh = p_state->meshes[mesh_index];
+ const Vector<float> &blend_weights = gltf_mesh->get_blend_weights();
+ const int blend_weight_count = gltf_mesh->get_blend_weights().size();
+ const int anim_weights_size = accessor_weights.size();
+ // For example, if a mesh has 2 blend weights, and the accessor provides 10 values, then there are 5 frames of animation, each with 2 blend weights.
+ ERR_FAIL_COND_MSG(blend_weight_count == 0 || ((anim_weights_size % blend_weight_count) != 0), "glTF: Cannot apply " + itos(accessor_weights.size()) + " weights to a mesh with " + itos(blend_weights.size()) + " blend weights.");
+ const int frame_count = anim_weights_size / blend_weight_count;
+ track->weight_tracks.resize(blend_weight_count);
+ for (int blend_weight_index = 0; blend_weight_index < blend_weight_count; blend_weight_index++) {
+ GLTFAnimation::Channel<real_t> weight_track;
+ weight_track.interpolation = p_interp;
+ weight_track.times = p_times;
+ weight_track.values.resize(frame_count);
+ for (int frame_index = 0; frame_index < frame_count; frame_index++) {
+ // For example, if a mesh has 2 blend weights, and the accessor provides 10 values,
+ // then the first frame has indices [0, 1], the second frame has [2, 3], and so on.
+ // Here we process all frames of one blend weight, so we want [0, 2, 4, 6, 8] or [1, 3, 5, 7, 9].
+ // For the fist one we calculate 0 * 2 + 0, 1 * 2 + 0, 2 * 2 + 0, etc, then for the second 0 * 2 + 1, 1 * 2 + 1, 2 * 2 + 1, etc.
+ weight_track.values.write[frame_index] = accessor_weights[frame_index * blend_weight_count + blend_weight_index];
+ }
+ track->weight_tracks.write[blend_weight_index] = weight_track;
+ }
+ }
+ // The special case was handled, return to skip the general case.
+ return;
+ }
+ // General case: Convert animation pointers to Variant value pointer tracks.
+ Ref<GLTFObjectModelProperty> obj_model_prop = import_object_model_property(p_state, p_animation_json_pointer);
+ if (obj_model_prop.is_null() || !obj_model_prop->has_node_paths()) {
+ // Exit quietly, `import_object_model_property` already prints a warning if the property is not found.
+ return;
+ }
+ HashMap<String, GLTFAnimation::Channel<Variant>> &anim_ptr_map = p_gltf_animation->get_pointer_tracks();
+ GLTFAnimation::Channel<Variant> channel;
+ channel.interpolation = p_interp;
+ channel.times = p_times;
+ channel.values = _decode_accessor_as_variant(p_state, p_output_value_accessor_index, obj_model_prop->get_variant_type(), obj_model_prop->get_accessor_type());
+ anim_ptr_map[p_animation_json_pointer] = channel;
+}
+
void GLTFDocument::_assign_node_names(Ref<GLTFState> p_state) {
for (int i = 0; i < p_state->nodes.size(); i++) {
Ref<GLTFNode> gltf_node = p_state->nodes[i];
@@ -5256,46 +5819,46 @@ void GLTFDocument::_convert_scene_node(Ref<GLTFState> p_state, Node *p_current,
gltf_node->set_original_name(p_current->get_name());
gltf_node->set_name(_gen_unique_name(p_state, p_current->get_name()));
gltf_node->merge_meta_from(p_current);
- if (cast_to<Node3D>(p_current)) {
- Node3D *spatial = cast_to<Node3D>(p_current);
+ if (Object::cast_to<Node3D>(p_current)) {
+ Node3D *spatial = Object::cast_to<Node3D>(p_current);
_convert_spatial(p_state, spatial, gltf_node);
}
- if (cast_to<MeshInstance3D>(p_current)) {
- MeshInstance3D *mi = cast_to<MeshInstance3D>(p_current);
+ if (Object::cast_to<MeshInstance3D>(p_current)) {
+ MeshInstance3D *mi = Object::cast_to<MeshInstance3D>(p_current);
_convert_mesh_instance_to_gltf(mi, p_state, gltf_node);
- } else if (cast_to<BoneAttachment3D>(p_current)) {
- BoneAttachment3D *bone = cast_to<BoneAttachment3D>(p_current);
+ } else if (Object::cast_to<BoneAttachment3D>(p_current)) {
+ BoneAttachment3D *bone = Object::cast_to<BoneAttachment3D>(p_current);
_convert_bone_attachment_to_gltf(bone, p_state, p_gltf_parent, p_gltf_root, gltf_node);
return;
- } else if (cast_to<Skeleton3D>(p_current)) {
- Skeleton3D *skel = cast_to<Skeleton3D>(p_current);
+ } else if (Object::cast_to<Skeleton3D>(p_current)) {
+ Skeleton3D *skel = Object::cast_to<Skeleton3D>(p_current);
_convert_skeleton_to_gltf(skel, p_state, p_gltf_parent, p_gltf_root, gltf_node);
// We ignore the Godot Engine node that is the skeleton.
return;
- } else if (cast_to<MultiMeshInstance3D>(p_current)) {
- MultiMeshInstance3D *multi = cast_to<MultiMeshInstance3D>(p_current);
+ } else if (Object::cast_to<MultiMeshInstance3D>(p_current)) {
+ MultiMeshInstance3D *multi = Object::cast_to<MultiMeshInstance3D>(p_current);
_convert_multi_mesh_instance_to_gltf(multi, p_gltf_parent, p_gltf_root, gltf_node, p_state);
#ifdef MODULE_CSG_ENABLED
- } else if (cast_to<CSGShape3D>(p_current)) {
- CSGShape3D *shape = cast_to<CSGShape3D>(p_current);
+ } else if (Object::cast_to<CSGShape3D>(p_current)) {
+ CSGShape3D *shape = Object::cast_to<CSGShape3D>(p_current);
if (shape->get_parent() && shape->is_root_shape()) {
_convert_csg_shape_to_gltf(shape, p_gltf_parent, gltf_node, p_state);
}
#endif // MODULE_CSG_ENABLED
#ifdef MODULE_GRIDMAP_ENABLED
- } else if (cast_to<GridMap>(p_current)) {
+ } else if (Object::cast_to<GridMap>(p_current)) {
GridMap *gridmap = Object::cast_to<GridMap>(p_current);
_convert_grid_map_to_gltf(gridmap, p_gltf_parent, p_gltf_root, gltf_node, p_state);
#endif // MODULE_GRIDMAP_ENABLED
- } else if (cast_to<Camera3D>(p_current)) {
+ } else if (Object::cast_to<Camera3D>(p_current)) {
Camera3D *camera = Object::cast_to<Camera3D>(p_current);
_convert_camera_to_gltf(camera, p_state, gltf_node);
- } else if (cast_to<Light3D>(p_current)) {
+ } else if (Object::cast_to<Light3D>(p_current)) {
Light3D *light = Object::cast_to<Light3D>(p_current);
_convert_light_to_gltf(light, p_state, gltf_node);
- } else if (cast_to<AnimationPlayer>(p_current)) {
+ } else if (Object::cast_to<AnimationPlayer>(p_current)) {
AnimationPlayer *animation_player = Object::cast_to<AnimationPlayer>(p_current);
- _convert_animation_player_to_gltf(animation_player, p_state, p_gltf_parent, p_gltf_root, gltf_node, p_current);
+ p_state->animation_players.push_back(animation_player);
}
for (Ref<GLTFDocumentExtension> ext : document_extensions) {
ERR_CONTINUE(ext.is_null());
@@ -5373,12 +5936,6 @@ void GLTFDocument::_convert_csg_shape_to_gltf(CSGShape3D *p_current, GLTFNodeInd
}
#endif // MODULE_CSG_ENABLED
-void GLTFDocument::_convert_animation_player_to_gltf(AnimationPlayer *p_animation_player, Ref<GLTFState> p_state, GLTFNodeIndex p_gltf_current, GLTFNodeIndex p_gltf_root_index, Ref<GLTFNode> p_gltf_node, Node *p_scene_parent) {
- ERR_FAIL_NULL(p_animation_player);
- p_state->animation_players.push_back(p_animation_player);
- print_verbose(String("glTF: Converting animation player: ") + p_animation_player->get_name());
-}
-
void GLTFDocument::_check_visibility(Node *p_node, bool &r_retflag) {
r_retflag = true;
Node3D *spatial = Object::cast_to<Node3D>(p_node);
@@ -5571,9 +6128,9 @@ void GLTFDocument::_convert_bone_attachment_to_gltf(BoneAttachment3D *p_bone_att
Skeleton3D *skeleton;
// Note that relative transforms to external skeletons and pose overrides are not supported.
if (p_bone_attachment->get_use_external_skeleton()) {
- skeleton = cast_to<Skeleton3D>(p_bone_attachment->get_node_or_null(p_bone_attachment->get_external_skeleton()));
+ skeleton = Object::cast_to<Skeleton3D>(p_bone_attachment->get_node_or_null(p_bone_attachment->get_external_skeleton()));
} else {
- skeleton = cast_to<Skeleton3D>(p_bone_attachment->get_parent());
+ skeleton = Object::cast_to<Skeleton3D>(p_bone_attachment->get_parent());
}
GLTFSkeletonIndex skel_gltf_i = -1;
if (skeleton != nullptr && p_state->skeleton3d_to_gltf_skeleton.has(skeleton->get_instance_id())) {
@@ -5856,7 +6413,7 @@ struct SceneFormatImporterGLTFInterpolate<Quaternion> {
};
template <typename T>
-T GLTFDocument::_interpolate_track(const Vector<real_t> &p_times, const Vector<T> &p_values, const float p_time, const GLTFAnimation::Interpolation p_interp) {
+T GLTFDocument::_interpolate_track(const Vector<double> &p_times, const Vector<T> &p_values, const float p_time, const GLTFAnimation::Interpolation p_interp) {
ERR_FAIL_COND_V(p_values.is_empty(), T());
if (p_times.size() != (p_values.size() / (p_interp == GLTFAnimation::INTERP_CUBIC_SPLINE ? 3 : 1))) {
ERR_PRINT_ONCE("The interpolated values are not corresponding to its times.");
@@ -5927,8 +6484,433 @@ T GLTFDocument::_interpolate_track(const Vector<real_t> &p_times, const Vector<T
ERR_FAIL_V(p_values[0]);
}
+NodePath GLTFDocument::_find_material_node_path(Ref<GLTFState> p_state, Ref<Material> p_material) {
+ int mesh_index = 0;
+ for (Ref<GLTFMesh> gltf_mesh : p_state->meshes) {
+ TypedArray<Material> materials = gltf_mesh->get_instance_materials();
+ for (int mat_index = 0; mat_index < materials.size(); mat_index++) {
+ if (materials[mat_index] == p_material) {
+ for (Ref<GLTFNode> gltf_node : p_state->nodes) {
+ if (gltf_node->mesh == mesh_index) {
+ NodePath node_path = gltf_node->get_scene_node_path(p_state);
+ // Example: MyNode:mesh:surface_0/material:albedo_color, so we want the mesh:surface_0/material part.
+ Vector<StringName> subpath;
+ subpath.append("mesh");
+ subpath.append("surface_" + itos(mat_index) + "/material");
+ return NodePath(node_path.get_names(), subpath, false);
+ }
+ }
+ }
+ }
+ mesh_index++;
+ }
+ return NodePath();
+}
+
+Ref<GLTFObjectModelProperty> GLTFDocument::import_object_model_property(Ref<GLTFState> p_state, const String &p_json_pointer) {
+ if (p_state->object_model_properties.has(p_json_pointer)) {
+ return p_state->object_model_properties[p_json_pointer];
+ }
+ Ref<GLTFObjectModelProperty> ret;
+ // Split the JSON pointer into its components.
+ const PackedStringArray split = p_json_pointer.split("/", false);
+ ERR_FAIL_COND_V_MSG(split.size() < 3, ret, "glTF: Cannot use JSON pointer '" + p_json_pointer + "' because it does not contain enough elements. The only animatable properties are at least 3 levels deep (ex: '/nodes/0/translation' or '/materials/0/emissiveFactor').");
+ ret.instantiate();
+ ret->set_json_pointers({ split });
+ // Partial paths are passed to GLTFDocumentExtension classes if GLTFDocument cannot handle a given JSON pointer.
+ TypedArray<NodePath> partial_paths;
+ // Note: This might not be an integer, but in that case, we don't use this value anyway.
+ const int top_level_index = split[1].to_int();
+ // For JSON pointers present in the core glTF Object Model, hard-code them in GLTFDocument.
+ // https://github.com/KhronosGroup/glTF/blob/main/specification/2.0/ObjectModel.adoc
+ if (split[0] == "nodes") {
+ ERR_FAIL_INDEX_V_MSG(top_level_index, p_state->nodes.size(), ret, vformat("glTF: Unable to find node %d for JSON pointer '%s'.", top_level_index, p_json_pointer));
+ Ref<GLTFNode> pointed_gltf_node = p_state->nodes[top_level_index];
+ NodePath node_path = pointed_gltf_node->get_scene_node_path(p_state);
+ partial_paths.append(node_path);
+ // Check if it's something we should be able to handle.
+ const String &node_prop = split[2];
+ if (node_prop == "translation") {
+ ret->append_path_to_property(node_path, "position");
+ ret->set_types(Variant::VECTOR3, GLTFObjectModelProperty::GLTF_OBJECT_MODEL_TYPE_FLOAT3);
+ } else if (node_prop == "rotation") {
+ ret->append_path_to_property(node_path, "quaternion");
+ ret->set_types(Variant::QUATERNION, GLTFObjectModelProperty::GLTF_OBJECT_MODEL_TYPE_FLOAT4);
+ } else if (node_prop == "scale") {
+ ret->append_path_to_property(node_path, "scale");
+ ret->set_types(Variant::VECTOR3, GLTFObjectModelProperty::GLTF_OBJECT_MODEL_TYPE_FLOAT3);
+ } else if (node_prop == "matrix") {
+ ret->append_path_to_property(node_path, "transform");
+ ret->set_types(Variant::TRANSFORM3D, GLTFObjectModelProperty::GLTF_OBJECT_MODEL_TYPE_FLOAT4X4);
+ } else if (node_prop == "globalMatrix") {
+ ret->append_path_to_property(node_path, "global_transform");
+ ret->set_types(Variant::TRANSFORM3D, GLTFObjectModelProperty::GLTF_OBJECT_MODEL_TYPE_FLOAT4X4);
+ } else if (node_prop == "weights") {
+ if (split.size() > 3) {
+ const String &weight_index_string = split[3];
+ ret->append_path_to_property(node_path, "blend_shapes/morph_" + weight_index_string);
+ ret->set_types(Variant::FLOAT, GLTFObjectModelProperty::GLTF_OBJECT_MODEL_TYPE_FLOAT);
+ }
+ // Else, Godot's MeshInstance3D does not expose the blend shape weights as one property.
+ // But that's fine, we handle this case in _parse_animation_pointer instead.
+ }
+ } else if (split[0] == "cameras") {
+ const String &camera_prop = split[2];
+ for (Ref<GLTFNode> gltf_node : p_state->nodes) {
+ if (gltf_node->camera == top_level_index) {
+ NodePath node_path = gltf_node->get_scene_node_path(p_state);
+ partial_paths.append(node_path);
+ // Check if it's something we should be able to handle.
+ if (camera_prop == "orthographic" || camera_prop == "perspective") {
+ ERR_FAIL_COND_V(split.size() < 4, ret);
+ ret->set_types(Variant::FLOAT, GLTFObjectModelProperty::GLTF_OBJECT_MODEL_TYPE_FLOAT);
+ const String &sub_prop = split[3];
+ if (sub_prop == "xmag" || sub_prop == "ymag") {
+ ret->append_path_to_property(node_path, "size");
+ } else if (sub_prop == "yfov") {
+ ret->append_path_to_property(node_path, "fov");
+ GLTFCamera::set_fov_conversion_expressions(ret);
+ } else if (sub_prop == "zfar") {
+ ret->append_path_to_property(node_path, "far");
+ } else if (sub_prop == "znear") {
+ ret->append_path_to_property(node_path, "near");
+ }
+ }
+ }
+ }
+ } else if (split[0] == "materials") {
+ ERR_FAIL_INDEX_V_MSG(top_level_index, p_state->materials.size(), ret, vformat("glTF: Unable to find material %d for JSON pointer '%s'.", top_level_index, p_json_pointer));
+ Ref<Material> pointed_material = p_state->materials[top_level_index];
+ NodePath mat_path = _find_material_node_path(p_state, pointed_material);
+ if (mat_path.is_empty()) {
+ WARN_PRINT(vformat("glTF: Unable to find a path to the material %d for JSON pointer '%s'. This is likely bad data but it's also possible this is intentional. Continuing anyway.", top_level_index, p_json_pointer));
+ } else {
+ partial_paths.append(mat_path);
+ const String &mat_prop = split[2];
+ if (mat_prop == "alphaCutoff") {
+ ret->append_path_to_property(mat_path, "alpha_scissor_threshold");
+ ret->set_types(Variant::FLOAT, GLTFObjectModelProperty::GLTF_OBJECT_MODEL_TYPE_FLOAT);
+ } else if (mat_prop == "emissiveFactor") {
+ ret->append_path_to_property(mat_path, "emission");
+ ret->set_types(Variant::COLOR, GLTFObjectModelProperty::GLTF_OBJECT_MODEL_TYPE_FLOAT3);
+ } else if (mat_prop == "extensions") {
+ ERR_FAIL_COND_V(split.size() < 5, ret);
+ const String &ext_name = split[3];
+ const String &ext_prop = split[4];
+ if (ext_name == "KHR_materials_emissive_strength" && ext_prop == "emissiveStrength") {
+ ret->append_path_to_property(mat_path, "emission_energy_multiplier");
+ ret->set_types(Variant::FLOAT, GLTFObjectModelProperty::GLTF_OBJECT_MODEL_TYPE_FLOAT);
+ }
+ } else {
+ ERR_FAIL_COND_V(split.size() < 4, ret);
+ const String &sub_prop = split[3];
+ if (mat_prop == "normalTexture") {
+ if (sub_prop == "scale") {
+ ret->append_path_to_property(mat_path, "normal_scale");
+ ret->set_types(Variant::FLOAT, GLTFObjectModelProperty::GLTF_OBJECT_MODEL_TYPE_FLOAT);
+ }
+ } else if (mat_prop == "occlusionTexture") {
+ if (sub_prop == "strength") {
+ // This is the closest thing Godot has to an occlusion strength property.
+ ret->append_path_to_property(mat_path, "ao_light_affect");
+ ret->set_types(Variant::FLOAT, GLTFObjectModelProperty::GLTF_OBJECT_MODEL_TYPE_FLOAT);
+ }
+ } else if (mat_prop == "pbrMetallicRoughness") {
+ if (sub_prop == "baseColorFactor") {
+ ret->append_path_to_property(mat_path, "albedo_color");
+ ret->set_types(Variant::COLOR, GLTFObjectModelProperty::GLTF_OBJECT_MODEL_TYPE_FLOAT4);
+ } else if (sub_prop == "metallicFactor") {
+ ret->append_path_to_property(mat_path, "metallic");
+ ret->set_types(Variant::FLOAT, GLTFObjectModelProperty::GLTF_OBJECT_MODEL_TYPE_FLOAT);
+ } else if (sub_prop == "roughnessFactor") {
+ ret->append_path_to_property(mat_path, "roughness");
+ ret->set_types(Variant::FLOAT, GLTFObjectModelProperty::GLTF_OBJECT_MODEL_TYPE_FLOAT);
+ } else if (sub_prop == "baseColorTexture") {
+ ERR_FAIL_COND_V(split.size() < 6, ret);
+ const String &tex_ext_dict = split[4];
+ const String &tex_ext_name = split[5];
+ const String &tex_ext_prop = split[6];
+ if (tex_ext_dict == "extensions" && tex_ext_name == "KHR_texture_transform") {
+ // Godot only supports UVs for the whole material, not per texture.
+ // We treat the albedo texture as the main texture, and import as UV1.
+ // Godot does not support texture rotation, only offset and scale.
+ if (tex_ext_prop == "offset") {
+ ret->append_path_to_property(mat_path, "uv1_offset");
+ ret->set_types(Variant::VECTOR3, GLTFObjectModelProperty::GLTF_OBJECT_MODEL_TYPE_FLOAT2);
+ } else if (tex_ext_prop == "scale") {
+ ret->append_path_to_property(mat_path, "uv1_scale");
+ ret->set_types(Variant::VECTOR3, GLTFObjectModelProperty::GLTF_OBJECT_MODEL_TYPE_FLOAT2);
+ }
+ }
+ }
+ }
+ }
+ }
+ } else if (split[0] == "meshes") {
+ for (Ref<GLTFNode> gltf_node : p_state->nodes) {
+ if (gltf_node->mesh == top_level_index) {
+ NodePath node_path = gltf_node->get_scene_node_path(p_state);
+ Vector<StringName> subpath;
+ subpath.append("mesh");
+ partial_paths.append(NodePath(node_path.get_names(), subpath, false));
+ break;
+ }
+ }
+ } else if (split[0] == "extensions") {
+ if (split[1] == "KHR_lights_punctual" && split[2] == "lights" && split.size() > 4) {
+ const int light_index = split[3].to_int();
+ ERR_FAIL_INDEX_V_MSG(light_index, p_state->lights.size(), ret, vformat("glTF: Unable to find light %d for JSON pointer '%s'.", light_index, p_json_pointer));
+ const String &light_prop = split[4];
+ const Ref<GLTFLight> pointed_light = p_state->lights[light_index];
+ for (Ref<GLTFNode> gltf_node : p_state->nodes) {
+ if (gltf_node->light == light_index) {
+ NodePath node_path = gltf_node->get_scene_node_path(p_state);
+ partial_paths.append(node_path);
+ ret->set_types(Variant::FLOAT, GLTFObjectModelProperty::GLTF_OBJECT_MODEL_TYPE_FLOAT);
+ // Check if it's something we should be able to handle.
+ if (light_prop == "color") {
+ ret->append_path_to_property(node_path, "light_color");
+ ret->set_types(Variant::COLOR, GLTFObjectModelProperty::GLTF_OBJECT_MODEL_TYPE_FLOAT3);
+ } else if (light_prop == "intensity") {
+ ret->append_path_to_property(node_path, "light_energy");
+ } else if (light_prop == "range") {
+ const String &light_type = p_state->lights[light_index]->light_type;
+ if (light_type == "spot") {
+ ret->append_path_to_property(node_path, "spot_range");
+ } else {
+ ret->append_path_to_property(node_path, "omni_range");
+ }
+ } else if (light_prop == "spot") {
+ ERR_FAIL_COND_V(split.size() < 6, ret);
+ const String &sub_prop = split[5];
+ if (sub_prop == "innerConeAngle") {
+ ret->append_path_to_property(node_path, "spot_angle_attenuation");
+ GLTFLight::set_cone_inner_attenuation_conversion_expressions(ret);
+ } else if (sub_prop == "outerConeAngle") {
+ ret->append_path_to_property(node_path, "spot_angle");
+ }
+ }
+ }
+ }
+ }
+ }
+ // Additional JSON pointers can be added by GLTFDocumentExtension classes.
+ // We only need this if no mapping has been found yet from GLTFDocument's internal code.
+ // When available, we pass the partial paths to the extension to help it generate the full path.
+ // For example, for `/nodes/3/extensions/MY_ext/prop`, we pass a NodePath that leads to node 3,
+ // so the GLTFDocumentExtension class only needs to resolve the last `MY_ext/prop` part of the path.
+ // It should check `split.size() > 4 and split[0] == "nodes" and split[2] == "extensions" and split[3] == "MY_ext"`
+ // at the start of the function to check if this JSON pointer applies to it, then it can handle `split[4]`.
+ if (!ret->has_node_paths()) {
+ for (Ref<GLTFDocumentExtension> ext : all_document_extensions) {
+ ret = ext->import_object_model_property(p_state, split, partial_paths);
+ if (ret.is_valid() && ret->has_node_paths()) {
+ if (!ret->has_json_pointers()) {
+ ret->set_json_pointers({ split });
+ }
+ break;
+ }
+ }
+ if (ret.is_null() || !ret->has_node_paths()) {
+ if (split.has("KHR_texture_transform")) {
+ WARN_VERBOSE(vformat("glTF: Texture transforms are only supported per material in Godot. All KHR_texture_transform properties will be ignored except for the albedo texture. Ignoring JSON pointer '%s'.", p_json_pointer));
+ } else {
+ WARN_PRINT(vformat("glTF: Animation contained JSON pointer '%s' which could not be resolved. This property will not be animated.", p_json_pointer));
+ }
+ }
+ }
+ p_state->object_model_properties[p_json_pointer] = ret;
+ return ret;
+}
+
+Ref<GLTFObjectModelProperty> GLTFDocument::export_object_model_property(Ref<GLTFState> p_state, const NodePath &p_node_path, const Node *p_godot_node, GLTFNodeIndex p_gltf_node_index) {
+ Ref<GLTFObjectModelProperty> ret;
+ const Object *target_object = p_godot_node;
+ const Vector<StringName> subpath = p_node_path.get_subnames();
+ ERR_FAIL_COND_V_MSG(subpath.is_empty(), ret, "glTF: Cannot export empty property. No property was specified in the NodePath: " + p_node_path);
+ int target_prop_depth = 0;
+ for (StringName subname : subpath) {
+ Variant target_property = target_object->get(subname);
+ if (target_property.get_type() == Variant::OBJECT) {
+ target_object = target_property;
+ if (target_object) {
+ target_prop_depth++;
+ continue;
+ }
+ }
+ break;
+ }
+ const String &target_prop = subpath[target_prop_depth];
+ ret.instantiate();
+ ret->set_node_paths({ p_node_path });
+ Vector<PackedStringArray> split_json_pointers;
+ PackedStringArray split_json_pointer;
+ if (Object::cast_to<BaseMaterial3D>(target_object)) {
+ for (int i = 0; i < p_state->materials.size(); i++) {
+ if (p_state->materials[i].ptr() == target_object) {
+ split_json_pointer.append("materials");
+ split_json_pointer.append(itos(i));
+ if (target_prop == "alpha_scissor_threshold") {
+ split_json_pointer.append("alphaCutoff");
+ ret->set_types(Variant::FLOAT, GLTFObjectModelProperty::GLTF_OBJECT_MODEL_TYPE_FLOAT);
+ } else if (target_prop == "emission") {
+ split_json_pointer.append("emissiveFactor");
+ ret->set_types(Variant::COLOR, GLTFObjectModelProperty::GLTF_OBJECT_MODEL_TYPE_FLOAT3);
+ } else if (target_prop == "emission_energy_multiplier") {
+ split_json_pointer.append("extensions");
+ split_json_pointer.append("KHR_materials_emissive_strength");
+ split_json_pointer.append("emissiveStrength");
+ ret->set_types(Variant::FLOAT, GLTFObjectModelProperty::GLTF_OBJECT_MODEL_TYPE_FLOAT);
+ } else if (target_prop == "normal_scale") {
+ split_json_pointer.append("normalTexture");
+ split_json_pointer.append("scale");
+ ret->set_types(Variant::FLOAT, GLTFObjectModelProperty::GLTF_OBJECT_MODEL_TYPE_FLOAT);
+ } else if (target_prop == "ao_light_affect") {
+ split_json_pointer.append("occlusionTexture");
+ split_json_pointer.append("strength");
+ ret->set_types(Variant::FLOAT, GLTFObjectModelProperty::GLTF_OBJECT_MODEL_TYPE_FLOAT);
+ } else if (target_prop == "albedo_color") {
+ split_json_pointer.append("pbrMetallicRoughness");
+ split_json_pointer.append("baseColorFactor");
+ ret->set_types(Variant::COLOR, GLTFObjectModelProperty::GLTF_OBJECT_MODEL_TYPE_FLOAT4);
+ } else if (target_prop == "metallic") {
+ split_json_pointer.append("pbrMetallicRoughness");
+ split_json_pointer.append("metallicFactor");
+ ret->set_types(Variant::FLOAT, GLTFObjectModelProperty::GLTF_OBJECT_MODEL_TYPE_FLOAT);
+ } else if (target_prop == "roughness") {
+ split_json_pointer.append("pbrMetallicRoughness");
+ split_json_pointer.append("roughnessFactor");
+ ret->set_types(Variant::FLOAT, GLTFObjectModelProperty::GLTF_OBJECT_MODEL_TYPE_FLOAT);
+ } else if (target_prop == "uv1_offset" || target_prop == "uv1_scale") {
+ split_json_pointer.append("pbrMetallicRoughness");
+ split_json_pointer.append("baseColorTexture");
+ split_json_pointer.append("extensions");
+ split_json_pointer.append("KHR_texture_transform");
+ if (target_prop == "uv1_offset") {
+ split_json_pointer.append("offset");
+ } else {
+ split_json_pointer.append("scale");
+ }
+ ret->set_types(Variant::VECTOR3, GLTFObjectModelProperty::GLTF_OBJECT_MODEL_TYPE_FLOAT2);
+ } else {
+ split_json_pointer.clear();
+ }
+ break;
+ }
+ }
+ } else {
+ // Properties directly on Godot nodes.
+ Ref<GLTFNode> gltf_node = p_state->nodes[p_gltf_node_index];
+ if (Object::cast_to<Camera3D>(target_object) && gltf_node->camera >= 0) {
+ split_json_pointer.append("cameras");
+ split_json_pointer.append(itos(gltf_node->camera));
+ const Camera3D *camera_node = Object::cast_to<Camera3D>(target_object);
+ const Camera3D::ProjectionType projection_type = camera_node->get_projection();
+ if (projection_type == Camera3D::PROJECTION_PERSPECTIVE) {
+ split_json_pointer.append("perspective");
+ } else {
+ split_json_pointer.append("orthographic");
+ }
+ ret->set_types(Variant::FLOAT, GLTFObjectModelProperty::GLTF_OBJECT_MODEL_TYPE_FLOAT);
+ if (target_prop == "size") {
+ PackedStringArray xmag = split_json_pointer.duplicate();
+ xmag.append("xmag");
+ split_json_pointers.append(xmag);
+ split_json_pointer.append("ymag");
+ } else if (target_prop == "fov") {
+ split_json_pointer.append("yfov");
+ GLTFCamera::set_fov_conversion_expressions(ret);
+ } else if (target_prop == "far") {
+ split_json_pointer.append("zfar");
+ } else if (target_prop == "near") {
+ split_json_pointer.append("znear");
+ } else {
+ split_json_pointer.clear();
+ }
+ } else if (Object::cast_to<Light3D>(target_object) && gltf_node->light >= 0) {
+ split_json_pointer.append("extensions");
+ split_json_pointer.append("KHR_lights_punctual");
+ split_json_pointer.append("lights");
+ split_json_pointer.append(itos(gltf_node->light));
+ ret->set_types(Variant::FLOAT, GLTFObjectModelProperty::GLTF_OBJECT_MODEL_TYPE_FLOAT);
+ if (target_prop == "light_color") {
+ split_json_pointer.append("color");
+ ret->set_types(Variant::COLOR, GLTFObjectModelProperty::GLTF_OBJECT_MODEL_TYPE_FLOAT3);
+ } else if (target_prop == "light_energy") {
+ split_json_pointer.append("intensity");
+ } else if (target_prop == "spot_range") {
+ split_json_pointer.append("range");
+ } else if (target_prop == "omni_range") {
+ split_json_pointer.append("range");
+ } else if (target_prop == "spot_angle") {
+ split_json_pointer.append("spot");
+ split_json_pointer.append("outerConeAngle");
+ } else if (target_prop == "spot_angle_attenuation") {
+ split_json_pointer.append("spot");
+ split_json_pointer.append("innerConeAngle");
+ GLTFLight::set_cone_inner_attenuation_conversion_expressions(ret);
+ } else {
+ split_json_pointer.clear();
+ }
+ } else if (Object::cast_to<MeshInstance3D>(target_object) && target_prop.begins_with("blend_shapes/morph_")) {
+ const String &weight_index_string = target_prop.trim_prefix("blend_shapes/morph_");
+ split_json_pointer.append("nodes");
+ split_json_pointer.append(itos(p_gltf_node_index));
+ split_json_pointer.append("weights");
+ split_json_pointer.append(weight_index_string);
+ }
+ // Transform properties. Check for all 3D nodes if we haven't resolved the JSON pointer yet.
+ // Note: Do not put this in an `else`, because otherwise this will not be reached.
+ if (split_json_pointer.is_empty() && Object::cast_to<Node3D>(target_object)) {
+ split_json_pointer.append("nodes");
+ split_json_pointer.append(itos(p_gltf_node_index));
+ if (target_prop == "position") {
+ split_json_pointer.append("translation");
+ ret->set_types(Variant::VECTOR3, GLTFObjectModelProperty::GLTF_OBJECT_MODEL_TYPE_FLOAT3);
+ } else if (target_prop == "quaternion") {
+ // Note: Only Quaternion rotation can be converted from Godot in this mapping.
+ // Struct methods like from_euler are not accessible from the Expression class. :(
+ split_json_pointer.append("rotation");
+ ret->set_types(Variant::QUATERNION, GLTFObjectModelProperty::GLTF_OBJECT_MODEL_TYPE_FLOAT4);
+ } else if (target_prop == "scale") {
+ split_json_pointer.append("scale");
+ ret->set_types(Variant::VECTOR3, GLTFObjectModelProperty::GLTF_OBJECT_MODEL_TYPE_FLOAT3);
+ } else if (target_prop == "transform") {
+ split_json_pointer.append("matrix");
+ ret->set_types(Variant::TRANSFORM3D, GLTFObjectModelProperty::GLTF_OBJECT_MODEL_TYPE_FLOAT4X4);
+ } else if (target_prop == "global_transform") {
+ split_json_pointer.append("globalMatrix");
+ ret->set_types(Variant::TRANSFORM3D, GLTFObjectModelProperty::GLTF_OBJECT_MODEL_TYPE_FLOAT4X4);
+ } else {
+ split_json_pointer.clear();
+ }
+ }
+ }
+ // Additional JSON pointers can be added by GLTFDocumentExtension classes.
+ // We only need this if no mapping has been found yet from GLTFDocument's internal code.
+ // We pass as many pieces of information as we can to the extension to give it lots of context.
+ if (split_json_pointer.is_empty()) {
+ for (Ref<GLTFDocumentExtension> ext : all_document_extensions) {
+ ret = ext->export_object_model_property(p_state, p_node_path, p_godot_node, p_gltf_node_index, target_object, target_prop_depth);
+ if (ret.is_valid() && ret->has_json_pointers()) {
+ if (!ret->has_node_paths()) {
+ ret->set_node_paths({ p_node_path });
+ }
+ break;
+ }
+ }
+ } else {
+ // GLTFDocument's internal code found a mapping, so set it and return it.
+ split_json_pointers.append(split_json_pointer);
+ ret->set_json_pointers(split_json_pointers);
+ }
+ return ret;
+}
+
void GLTFDocument::_import_animation(Ref<GLTFState> p_state, AnimationPlayer *p_animation_player, const GLTFAnimationIndex p_index, const bool p_trimming, const bool p_remove_immutable_tracks) {
ERR_FAIL_COND(p_state.is_null());
+ Node *scene_root = p_animation_player->get_parent();
+ ERR_FAIL_NULL(scene_root);
Ref<GLTFAnimation> anim = p_state->animations[p_index];
String anim_name = anim->get_name();
@@ -5949,8 +6931,8 @@ void GLTFDocument::_import_animation(Ref<GLTFState> p_state, AnimationPlayer *p_
double anim_start = p_trimming ? INFINITY : 0.0;
double anim_end = 0.0;
- for (const KeyValue<int, GLTFAnimation::Track> &track_i : anim->get_tracks()) {
- const GLTFAnimation::Track &track = track_i.value;
+ for (const KeyValue<int, GLTFAnimation::NodeTrack> &track_i : anim->get_node_tracks()) {
+ const GLTFAnimation::NodeTrack &track = track_i.value;
//need to find the path: for skeletons, weight tracks will affect the mesh
NodePath node_path;
//for skeletons, transform tracks always affect bones
@@ -5962,14 +6944,12 @@ void GLTFDocument::_import_animation(Ref<GLTFState> p_state, AnimationPlayer *p_
const Ref<GLTFNode> gltf_node = p_state->nodes[track_i.key];
- Node *root = p_animation_player->get_parent();
- ERR_FAIL_NULL(root);
HashMap<GLTFNodeIndex, Node *>::Iterator node_element = p_state->scene_nodes.find(node_index);
ERR_CONTINUE_MSG(!node_element, vformat("Unable to find node %d for animation.", node_index));
- node_path = root->get_path_to(node_element->value);
+ node_path = scene_root->get_path_to(node_element->value);
HashMap<GLTFNodeIndex, ImporterMeshInstance3D *>::Iterator mesh_instance_element = p_state->scene_mesh_instances.find(node_index);
if (mesh_instance_element) {
- mesh_instance_node_path = root->get_path_to(mesh_instance_element->value);
+ mesh_instance_node_path = scene_root->get_path_to(mesh_instance_element->value);
} else {
mesh_instance_node_path = node_path;
}
@@ -6203,6 +7183,56 @@ void GLTFDocument::_import_animation(Ref<GLTFState> p_state, AnimationPlayer *p_
}
}
+ for (const KeyValue<String, GLTFAnimation::Channel<Variant>> &track_iter : anim->get_pointer_tracks()) {
+ // Determine the property to animate.
+ const String json_pointer = track_iter.key;
+ const Ref<GLTFObjectModelProperty> prop = import_object_model_property(p_state, json_pointer);
+ ERR_FAIL_COND(prop.is_null());
+ // Adjust the animation duration to encompass all keyframes.
+ const GLTFAnimation::Channel<Variant> &channel = track_iter.value;
+ ERR_CONTINUE_MSG(channel.times.size() != channel.values.size(), vformat("glTF: Animation pointer '%s' has mismatched keyframe times and values.", json_pointer));
+ if (p_trimming) {
+ for (int i = 0; i < channel.times.size(); i++) {
+ anim_start = MIN(anim_start, channel.times[i]);
+ anim_end = MAX(anim_end, channel.times[i]);
+ }
+ } else {
+ for (int i = 0; i < channel.times.size(); i++) {
+ anim_end = MAX(anim_end, channel.times[i]);
+ }
+ }
+ // Begin converting the glTF animation to a Godot animation.
+ const Ref<Expression> gltf_to_godot_expr = prop->get_gltf_to_godot_expression();
+ const bool is_gltf_to_godot_expr_valid = gltf_to_godot_expr.is_valid();
+ for (const NodePath node_path : prop->get_node_paths()) {
+ // If using an expression, determine the base instance to pass to the expression.
+ Object *base_instance = nullptr;
+ if (is_gltf_to_godot_expr_valid) {
+ Ref<Resource> resource;
+ Vector<StringName> leftover_subpath;
+ base_instance = scene_root->get_node_and_resource(node_path, resource, leftover_subpath);
+ if (resource.is_valid()) {
+ base_instance = resource.ptr();
+ }
+ }
+ // Add a track and insert all keys and values.
+ const int track_index = animation->get_track_count();
+ animation->add_track(Animation::TYPE_VALUE);
+ animation->track_set_interpolation_type(track_index, GLTFAnimation::gltf_to_godot_interpolation(channel.interpolation));
+ animation->track_set_path(track_index, node_path);
+ for (int i = 0; i < channel.times.size(); i++) {
+ const double time = channel.times[i];
+ Variant value = channel.values[i];
+ if (is_gltf_to_godot_expr_valid) {
+ Array inputs;
+ inputs.append(value);
+ value = gltf_to_godot_expr->execute(inputs, base_instance);
+ }
+ animation->track_insert_key(track_index, time, value);
+ }
+ }
+ }
+
animation->set_length(anim_end - anim_start);
Ref<AnimationLibrary> library;
@@ -6373,41 +7403,56 @@ void GLTFDocument::_process_mesh_instances(Ref<GLTFState> p_state, Node *p_scene
}
}
-GLTFAnimation::Track GLTFDocument::_convert_animation_track(Ref<GLTFState> p_state, GLTFAnimation::Track p_track, Ref<Animation> p_animation, int32_t p_track_i, GLTFNodeIndex p_node_i) {
- Animation::InterpolationType interpolation = p_animation->track_get_interpolation_type(p_track_i);
-
- GLTFAnimation::Interpolation gltf_interpolation = GLTFAnimation::INTERP_LINEAR;
- if (interpolation == Animation::InterpolationType::INTERPOLATION_LINEAR) {
- gltf_interpolation = GLTFAnimation::INTERP_LINEAR;
- } else if (interpolation == Animation::InterpolationType::INTERPOLATION_NEAREST) {
- gltf_interpolation = GLTFAnimation::INTERP_STEP;
- } else if (interpolation == Animation::InterpolationType::INTERPOLATION_CUBIC) {
- gltf_interpolation = GLTFAnimation::INTERP_CUBIC_SPLINE;
+GLTFNodeIndex GLTFDocument::_node_and_or_bone_to_gltf_node_index(Ref<GLTFState> p_state, const Vector<StringName> &p_node_subpath, const Node *p_godot_node) {
+ const Skeleton3D *skeleton = Object::cast_to<Skeleton3D>(p_godot_node);
+ if (skeleton && p_node_subpath.size() == 1) {
+ // Special case: Handle skeleton bone TRS tracks. They use the format `A/B/C/Skeleton3D:bone_name`.
+ // We have a Skeleton3D, check if it has a bone with the same name as this subpath.
+ const String &bone_name = p_node_subpath[0];
+ const int32_t bone_index = skeleton->find_bone(bone_name);
+ if (bone_index != -1) {
+ // A bone was found! But we still need to figure out which glTF node it corresponds to.
+ for (GLTFSkeletonIndex skeleton_i = 0; skeleton_i < p_state->skeletons.size(); skeleton_i++) {
+ const Ref<GLTFSkeleton> &skeleton_gltf = p_state->skeletons[skeleton_i];
+ if (skeleton == skeleton_gltf->godot_skeleton) {
+ GLTFNodeIndex node_i = skeleton_gltf->godot_bone_node[bone_index];
+ return node_i;
+ }
+ }
+ ERR_FAIL_V_MSG(-1, vformat("glTF: Found a bone %s in a Skeleton3D that wasn't in the GLTFState. Ensure that all nodes referenced by the AnimationPlayer are in the scene you are exporting.", bone_name));
+ }
}
- Animation::TrackType track_type = p_animation->track_get_type(p_track_i);
- int32_t key_count = p_animation->track_get_key_count(p_track_i);
- Vector<real_t> times;
- times.resize(key_count);
- String path = p_animation->track_get_path(p_track_i);
- for (int32_t key_i = 0; key_i < key_count; key_i++) {
- times.write[key_i] = p_animation->track_get_key_time(p_track_i, key_i);
+ // General case: Not a skeleton bone, usually this means a normal node, or it could be the Skeleton3D itself.
+ for (const KeyValue<GLTFNodeIndex, Node *> &scene_node_i : p_state->scene_nodes) {
+ if (scene_node_i.value == p_godot_node) {
+ return scene_node_i.key;
+ }
}
- double anim_end = p_animation->get_length();
+ ERR_FAIL_V_MSG(-1, vformat("glTF: A node was animated, but it wasn't found in the GLTFState. Ensure that all nodes referenced by the AnimationPlayer are in the scene you are exporting."));
+}
+
+bool GLTFDocument::_convert_animation_node_track(Ref<GLTFState> p_state, GLTFAnimation::NodeTrack &p_gltf_node_track, const Ref<Animation> &p_godot_animation, int32_t p_godot_anim_track_index, Vector<double> &p_times) {
+ GLTFAnimation::Interpolation gltf_interpolation = GLTFAnimation::godot_to_gltf_interpolation(p_godot_animation, p_godot_anim_track_index);
+ const Animation::TrackType track_type = p_godot_animation->track_get_type(p_godot_anim_track_index);
+ const int32_t key_count = p_godot_animation->track_get_key_count(p_godot_anim_track_index);
+ const NodePath node_path = p_godot_animation->track_get_path(p_godot_anim_track_index);
+ const Vector<StringName> subpath = node_path.get_subnames();
+ double anim_end = p_godot_animation->get_length();
if (track_type == Animation::TYPE_SCALE_3D) {
if (gltf_interpolation == GLTFAnimation::INTERP_CUBIC_SPLINE) {
gltf_interpolation = GLTFAnimation::INTERP_LINEAR;
- p_track.scale_track.times.clear();
- p_track.scale_track.values.clear();
+ p_gltf_node_track.scale_track.times.clear();
+ p_gltf_node_track.scale_track.values.clear();
// CATMULLROMSPLINE or CUBIC_SPLINE have to be baked, apologies.
const double increment = 1.0 / p_state->get_bake_fps();
double time = 0.0;
bool last = false;
while (true) {
Vector3 scale;
- Error err = p_animation->try_scale_track_interpolate(p_track_i, time, &scale);
+ Error err = p_godot_animation->try_scale_track_interpolate(p_godot_anim_track_index, time, &scale);
ERR_CONTINUE(err != OK);
- p_track.scale_track.values.push_back(scale);
- p_track.scale_track.times.push_back(time);
+ p_gltf_node_track.scale_track.values.push_back(scale);
+ p_gltf_node_track.scale_track.times.push_back(time);
if (last) {
break;
}
@@ -6418,31 +7463,31 @@ GLTFAnimation::Track GLTFDocument::_convert_animation_track(Ref<GLTFState> p_sta
}
}
} else {
- p_track.scale_track.times = times;
- p_track.scale_track.interpolation = gltf_interpolation;
- p_track.scale_track.values.resize(key_count);
+ p_gltf_node_track.scale_track.times = p_times;
+ p_gltf_node_track.scale_track.interpolation = gltf_interpolation;
+ p_gltf_node_track.scale_track.values.resize(key_count);
for (int32_t key_i = 0; key_i < key_count; key_i++) {
Vector3 scale;
- Error err = p_animation->scale_track_get_key(p_track_i, key_i, &scale);
+ Error err = p_godot_animation->scale_track_get_key(p_godot_anim_track_index, key_i, &scale);
ERR_CONTINUE(err != OK);
- p_track.scale_track.values.write[key_i] = scale;
+ p_gltf_node_track.scale_track.values.write[key_i] = scale;
}
}
} else if (track_type == Animation::TYPE_POSITION_3D) {
if (gltf_interpolation == GLTFAnimation::INTERP_CUBIC_SPLINE) {
gltf_interpolation = GLTFAnimation::INTERP_LINEAR;
- p_track.position_track.times.clear();
- p_track.position_track.values.clear();
+ p_gltf_node_track.position_track.times.clear();
+ p_gltf_node_track.position_track.values.clear();
// CATMULLROMSPLINE or CUBIC_SPLINE have to be baked, apologies.
const double increment = 1.0 / p_state->get_bake_fps();
double time = 0.0;
bool last = false;
while (true) {
Vector3 scale;
- Error err = p_animation->try_position_track_interpolate(p_track_i, time, &scale);
+ Error err = p_godot_animation->try_position_track_interpolate(p_godot_anim_track_index, time, &scale);
ERR_CONTINUE(err != OK);
- p_track.position_track.values.push_back(scale);
- p_track.position_track.times.push_back(time);
+ p_gltf_node_track.position_track.values.push_back(scale);
+ p_gltf_node_track.position_track.times.push_back(time);
if (last) {
break;
}
@@ -6453,31 +7498,31 @@ GLTFAnimation::Track GLTFDocument::_convert_animation_track(Ref<GLTFState> p_sta
}
}
} else {
- p_track.position_track.times = times;
- p_track.position_track.values.resize(key_count);
- p_track.position_track.interpolation = gltf_interpolation;
+ p_gltf_node_track.position_track.times = p_times;
+ p_gltf_node_track.position_track.values.resize(key_count);
+ p_gltf_node_track.position_track.interpolation = gltf_interpolation;
for (int32_t key_i = 0; key_i < key_count; key_i++) {
Vector3 position;
- Error err = p_animation->position_track_get_key(p_track_i, key_i, &position);
+ Error err = p_godot_animation->position_track_get_key(p_godot_anim_track_index, key_i, &position);
ERR_CONTINUE(err != OK);
- p_track.position_track.values.write[key_i] = position;
+ p_gltf_node_track.position_track.values.write[key_i] = position;
}
}
} else if (track_type == Animation::TYPE_ROTATION_3D) {
if (gltf_interpolation == GLTFAnimation::INTERP_CUBIC_SPLINE) {
gltf_interpolation = GLTFAnimation::INTERP_LINEAR;
- p_track.rotation_track.times.clear();
- p_track.rotation_track.values.clear();
+ p_gltf_node_track.rotation_track.times.clear();
+ p_gltf_node_track.rotation_track.values.clear();
// CATMULLROMSPLINE or CUBIC_SPLINE have to be baked, apologies.
const double increment = 1.0 / p_state->get_bake_fps();
double time = 0.0;
bool last = false;
while (true) {
Quaternion rotation;
- Error err = p_animation->try_rotation_track_interpolate(p_track_i, time, &rotation);
+ Error err = p_godot_animation->try_rotation_track_interpolate(p_godot_anim_track_index, time, &rotation);
ERR_CONTINUE(err != OK);
- p_track.rotation_track.values.push_back(rotation);
- p_track.rotation_track.times.push_back(time);
+ p_gltf_node_track.rotation_track.values.push_back(rotation);
+ p_gltf_node_track.rotation_track.times.push_back(time);
if (last) {
break;
}
@@ -6488,306 +7533,326 @@ GLTFAnimation::Track GLTFDocument::_convert_animation_track(Ref<GLTFState> p_sta
}
}
} else {
- p_track.rotation_track.times = times;
- p_track.rotation_track.values.resize(key_count);
- p_track.rotation_track.interpolation = gltf_interpolation;
+ p_gltf_node_track.rotation_track.times = p_times;
+ p_gltf_node_track.rotation_track.values.resize(key_count);
+ p_gltf_node_track.rotation_track.interpolation = gltf_interpolation;
for (int32_t key_i = 0; key_i < key_count; key_i++) {
Quaternion rotation;
- Error err = p_animation->rotation_track_get_key(p_track_i, key_i, &rotation);
+ Error err = p_godot_animation->rotation_track_get_key(p_godot_anim_track_index, key_i, &rotation);
ERR_CONTINUE(err != OK);
- p_track.rotation_track.values.write[key_i] = rotation;
- }
- }
- } else if (track_type == Animation::TYPE_VALUE) {
- if (path.contains(":position")) {
- p_track.position_track.interpolation = gltf_interpolation;
- p_track.position_track.times = times;
- p_track.position_track.values.resize(key_count);
-
- if (gltf_interpolation == GLTFAnimation::INTERP_CUBIC_SPLINE) {
- gltf_interpolation = GLTFAnimation::INTERP_LINEAR;
- p_track.position_track.times.clear();
- p_track.position_track.values.clear();
- // CATMULLROMSPLINE or CUBIC_SPLINE have to be baked, apologies.
- const double increment = 1.0 / p_state->get_bake_fps();
- double time = 0.0;
- bool last = false;
- while (true) {
- Vector3 position;
- Error err = p_animation->try_position_track_interpolate(p_track_i, time, &position);
- ERR_CONTINUE(err != OK);
- p_track.position_track.values.push_back(position);
- p_track.position_track.times.push_back(time);
- if (last) {
- break;
+ p_gltf_node_track.rotation_track.values.write[key_i] = rotation;
+ }
+ }
+ } else if (subpath.size() > 0) {
+ const StringName &node_prop = subpath[0];
+ if (track_type == Animation::TYPE_VALUE) {
+ if (node_prop == "position") {
+ p_gltf_node_track.position_track.interpolation = gltf_interpolation;
+ p_gltf_node_track.position_track.times = p_times;
+ p_gltf_node_track.position_track.values.resize(key_count);
+
+ if (gltf_interpolation == GLTFAnimation::INTERP_CUBIC_SPLINE) {
+ gltf_interpolation = GLTFAnimation::INTERP_LINEAR;
+ p_gltf_node_track.position_track.times.clear();
+ p_gltf_node_track.position_track.values.clear();
+ // CATMULLROMSPLINE or CUBIC_SPLINE have to be baked, apologies.
+ const double increment = 1.0 / p_state->get_bake_fps();
+ double time = 0.0;
+ bool last = false;
+ while (true) {
+ Vector3 position;
+ Error err = p_godot_animation->try_position_track_interpolate(p_godot_anim_track_index, time, &position);
+ ERR_CONTINUE(err != OK);
+ p_gltf_node_track.position_track.values.push_back(position);
+ p_gltf_node_track.position_track.times.push_back(time);
+ if (last) {
+ break;
+ }
+ time += increment;
+ if (time >= anim_end) {
+ last = true;
+ time = anim_end;
+ }
}
- time += increment;
- if (time >= anim_end) {
- last = true;
- time = anim_end;
+ } else {
+ for (int32_t key_i = 0; key_i < key_count; key_i++) {
+ Vector3 position = p_godot_animation->track_get_key_value(p_godot_anim_track_index, key_i);
+ p_gltf_node_track.position_track.values.write[key_i] = position;
}
}
- } else {
- for (int32_t key_i = 0; key_i < key_count; key_i++) {
- Vector3 position = p_animation->track_get_key_value(p_track_i, key_i);
- p_track.position_track.values.write[key_i] = position;
- }
- }
- } else if (path.contains(":rotation")) {
- p_track.rotation_track.interpolation = gltf_interpolation;
- p_track.rotation_track.times = times;
- p_track.rotation_track.values.resize(key_count);
- if (gltf_interpolation == GLTFAnimation::INTERP_CUBIC_SPLINE) {
- gltf_interpolation = GLTFAnimation::INTERP_LINEAR;
- p_track.rotation_track.times.clear();
- p_track.rotation_track.values.clear();
- // CATMULLROMSPLINE or CUBIC_SPLINE have to be baked, apologies.
- const double increment = 1.0 / p_state->get_bake_fps();
- double time = 0.0;
- bool last = false;
- while (true) {
- Quaternion rotation;
- Error err = p_animation->try_rotation_track_interpolate(p_track_i, time, &rotation);
- ERR_CONTINUE(err != OK);
- p_track.rotation_track.values.push_back(rotation);
- p_track.rotation_track.times.push_back(time);
- if (last) {
- break;
+ } else if (node_prop == "rotation" || node_prop == "rotation_degrees" || node_prop == "quaternion") {
+ p_gltf_node_track.rotation_track.interpolation = gltf_interpolation;
+ p_gltf_node_track.rotation_track.times = p_times;
+ p_gltf_node_track.rotation_track.values.resize(key_count);
+ if (gltf_interpolation == GLTFAnimation::INTERP_CUBIC_SPLINE) {
+ gltf_interpolation = GLTFAnimation::INTERP_LINEAR;
+ p_gltf_node_track.rotation_track.times.clear();
+ p_gltf_node_track.rotation_track.values.clear();
+ // CATMULLROMSPLINE or CUBIC_SPLINE have to be baked, apologies.
+ const double increment = 1.0 / p_state->get_bake_fps();
+ double time = 0.0;
+ bool last = false;
+ while (true) {
+ Quaternion rotation;
+ Error err = p_godot_animation->try_rotation_track_interpolate(p_godot_anim_track_index, time, &rotation);
+ ERR_CONTINUE(err != OK);
+ p_gltf_node_track.rotation_track.values.push_back(rotation);
+ p_gltf_node_track.rotation_track.times.push_back(time);
+ if (last) {
+ break;
+ }
+ time += increment;
+ if (time >= anim_end) {
+ last = true;
+ time = anim_end;
+ }
}
- time += increment;
- if (time >= anim_end) {
- last = true;
- time = anim_end;
+ } else {
+ for (int32_t key_i = 0; key_i < key_count; key_i++) {
+ Quaternion rotation_quaternion;
+ if (node_prop == "quaternion") {
+ rotation_quaternion = p_godot_animation->track_get_key_value(p_godot_anim_track_index, key_i);
+ } else {
+ Vector3 rotation_euler = p_godot_animation->track_get_key_value(p_godot_anim_track_index, key_i);
+ if (node_prop == "rotation_degrees") {
+ rotation_euler *= Math_TAU / 360.0;
+ }
+ rotation_quaternion = Quaternion::from_euler(rotation_euler);
+ }
+ p_gltf_node_track.rotation_track.values.write[key_i] = rotation_quaternion;
}
}
- } else {
- for (int32_t key_i = 0; key_i < key_count; key_i++) {
- Vector3 rotation_radian = p_animation->track_get_key_value(p_track_i, key_i);
- p_track.rotation_track.values.write[key_i] = Quaternion::from_euler(rotation_radian);
+ } else if (node_prop == "scale") {
+ p_gltf_node_track.scale_track.interpolation = gltf_interpolation;
+ p_gltf_node_track.scale_track.times = p_times;
+ p_gltf_node_track.scale_track.values.resize(key_count);
+
+ if (gltf_interpolation == GLTFAnimation::INTERP_CUBIC_SPLINE) {
+ gltf_interpolation = GLTFAnimation::INTERP_LINEAR;
+ p_gltf_node_track.scale_track.times.clear();
+ p_gltf_node_track.scale_track.values.clear();
+ // CATMULLROMSPLINE or CUBIC_SPLINE have to be baked, apologies.
+ const double increment = 1.0 / p_state->get_bake_fps();
+ double time = 0.0;
+ bool last = false;
+ while (true) {
+ Vector3 scale;
+ Error err = p_godot_animation->try_scale_track_interpolate(p_godot_anim_track_index, time, &scale);
+ ERR_CONTINUE(err != OK);
+ p_gltf_node_track.scale_track.values.push_back(scale);
+ p_gltf_node_track.scale_track.times.push_back(time);
+ if (last) {
+ break;
+ }
+ time += increment;
+ if (time >= anim_end) {
+ last = true;
+ time = anim_end;
+ }
+ }
+ } else {
+ for (int32_t key_i = 0; key_i < key_count; key_i++) {
+ Vector3 scale_track = p_godot_animation->track_get_key_value(p_godot_anim_track_index, key_i);
+ p_gltf_node_track.scale_track.values.write[key_i] = scale_track;
+ }
}
- }
- } else if (path.contains(":scale")) {
- p_track.scale_track.times = times;
- p_track.scale_track.interpolation = gltf_interpolation;
-
- p_track.scale_track.values.resize(key_count);
- p_track.scale_track.interpolation = gltf_interpolation;
-
- if (gltf_interpolation == GLTFAnimation::INTERP_CUBIC_SPLINE) {
- gltf_interpolation = GLTFAnimation::INTERP_LINEAR;
- p_track.scale_track.times.clear();
- p_track.scale_track.values.clear();
- // CATMULLROMSPLINE or CUBIC_SPLINE have to be baked, apologies.
- const double increment = 1.0 / p_state->get_bake_fps();
- double time = 0.0;
- bool last = false;
- while (true) {
- Vector3 scale;
- Error err = p_animation->try_scale_track_interpolate(p_track_i, time, &scale);
- ERR_CONTINUE(err != OK);
- p_track.scale_track.values.push_back(scale);
- p_track.scale_track.times.push_back(time);
- if (last) {
- break;
+ } else if (node_prop == "transform") {
+ p_gltf_node_track.position_track.interpolation = gltf_interpolation;
+ p_gltf_node_track.position_track.times = p_times;
+ p_gltf_node_track.position_track.values.resize(key_count);
+ p_gltf_node_track.rotation_track.interpolation = gltf_interpolation;
+ p_gltf_node_track.rotation_track.times = p_times;
+ p_gltf_node_track.rotation_track.values.resize(key_count);
+ p_gltf_node_track.scale_track.interpolation = gltf_interpolation;
+ p_gltf_node_track.scale_track.times = p_times;
+ p_gltf_node_track.scale_track.values.resize(key_count);
+ if (gltf_interpolation == GLTFAnimation::INTERP_CUBIC_SPLINE) {
+ gltf_interpolation = GLTFAnimation::INTERP_LINEAR;
+ p_gltf_node_track.position_track.times.clear();
+ p_gltf_node_track.position_track.values.clear();
+ p_gltf_node_track.rotation_track.times.clear();
+ p_gltf_node_track.rotation_track.values.clear();
+ p_gltf_node_track.scale_track.times.clear();
+ p_gltf_node_track.scale_track.values.clear();
+ // CATMULLROMSPLINE or CUBIC_SPLINE have to be baked, apologies.
+ const double increment = 1.0 / p_state->get_bake_fps();
+ double time = 0.0;
+ bool last = false;
+ while (true) {
+ Vector3 position;
+ Quaternion rotation;
+ Vector3 scale;
+ Error err = p_godot_animation->try_position_track_interpolate(p_godot_anim_track_index, time, &position);
+ ERR_CONTINUE(err != OK);
+ err = p_godot_animation->try_rotation_track_interpolate(p_godot_anim_track_index, time, &rotation);
+ ERR_CONTINUE(err != OK);
+ err = p_godot_animation->try_scale_track_interpolate(p_godot_anim_track_index, time, &scale);
+ ERR_CONTINUE(err != OK);
+ p_gltf_node_track.position_track.values.push_back(position);
+ p_gltf_node_track.position_track.times.push_back(time);
+ p_gltf_node_track.rotation_track.values.push_back(rotation);
+ p_gltf_node_track.rotation_track.times.push_back(time);
+ p_gltf_node_track.scale_track.values.push_back(scale);
+ p_gltf_node_track.scale_track.times.push_back(time);
+ if (last) {
+ break;
+ }
+ time += increment;
+ if (time >= anim_end) {
+ last = true;
+ time = anim_end;
+ }
}
- time += increment;
- if (time >= anim_end) {
- last = true;
- time = anim_end;
+ } else {
+ for (int32_t key_i = 0; key_i < key_count; key_i++) {
+ Transform3D transform = p_godot_animation->track_get_key_value(p_godot_anim_track_index, key_i);
+ p_gltf_node_track.position_track.values.write[key_i] = transform.get_origin();
+ p_gltf_node_track.rotation_track.values.write[key_i] = transform.basis.get_rotation_quaternion();
+ p_gltf_node_track.scale_track.values.write[key_i] = transform.basis.get_scale();
}
}
} else {
- for (int32_t key_i = 0; key_i < key_count; key_i++) {
- Vector3 scale_track = p_animation->track_get_key_value(p_track_i, key_i);
- p_track.scale_track.values.write[key_i] = scale_track;
- }
- }
- }
- } else if (track_type == Animation::TYPE_BEZIER) {
- const int32_t keys = anim_end * p_state->get_bake_fps();
- if (path.contains(":scale")) {
- if (!p_track.scale_track.times.size()) {
- p_track.scale_track.interpolation = gltf_interpolation;
- Vector<real_t> new_times;
- new_times.resize(keys);
- for (int32_t key_i = 0; key_i < keys; key_i++) {
- new_times.write[key_i] = key_i / p_state->get_bake_fps();
- }
- p_track.scale_track.times = new_times;
+ // This is a Value track animating a property, but not a TRS property, so it can't be converted into a node track.
+ return false;
+ }
+ } else if (track_type == Animation::TYPE_BEZIER) {
+ const int32_t keys = anim_end * p_state->get_bake_fps();
+ if (node_prop == "scale") {
+ if (p_gltf_node_track.scale_track.times.is_empty()) {
+ p_gltf_node_track.scale_track.interpolation = gltf_interpolation;
+ Vector<double> new_times;
+ new_times.resize(keys);
+ for (int32_t key_i = 0; key_i < keys; key_i++) {
+ new_times.write[key_i] = key_i / p_state->get_bake_fps();
+ }
+ p_gltf_node_track.scale_track.times = new_times;
- p_track.scale_track.values.resize(keys);
+ p_gltf_node_track.scale_track.values.resize(keys);
- for (int32_t key_i = 0; key_i < keys; key_i++) {
- p_track.scale_track.values.write[key_i] = Vector3(1.0f, 1.0f, 1.0f);
- }
+ for (int32_t key_i = 0; key_i < keys; key_i++) {
+ p_gltf_node_track.scale_track.values.write[key_i] = Vector3(1.0f, 1.0f, 1.0f);
+ }
- for (int32_t key_i = 0; key_i < keys; key_i++) {
- Vector3 bezier_track = p_track.scale_track.values[key_i];
- if (path.contains(":scale:x")) {
- bezier_track.x = p_animation->bezier_track_interpolate(p_track_i, key_i / p_state->get_bake_fps());
- } else if (path.contains(":scale:y")) {
- bezier_track.y = p_animation->bezier_track_interpolate(p_track_i, key_i / p_state->get_bake_fps());
- } else if (path.contains(":scale:z")) {
- bezier_track.z = p_animation->bezier_track_interpolate(p_track_i, key_i / p_state->get_bake_fps());
+ for (int32_t key_i = 0; key_i < keys; key_i++) {
+ Vector3 bezier_track = p_gltf_node_track.scale_track.values[key_i];
+ if (subpath.size() == 2) {
+ if (subpath[1] == StringName("x")) {
+ bezier_track.x = p_godot_animation->bezier_track_interpolate(p_godot_anim_track_index, key_i / p_state->get_bake_fps());
+ } else if (subpath[1] == StringName("y")) {
+ bezier_track.y = p_godot_animation->bezier_track_interpolate(p_godot_anim_track_index, key_i / p_state->get_bake_fps());
+ } else if (subpath[1] == StringName("z")) {
+ bezier_track.z = p_godot_animation->bezier_track_interpolate(p_godot_anim_track_index, key_i / p_state->get_bake_fps());
+ }
+ }
+ p_gltf_node_track.scale_track.values.write[key_i] = bezier_track;
}
- p_track.scale_track.values.write[key_i] = bezier_track;
}
- }
- } else if (path.contains(":position")) {
- if (!p_track.position_track.times.size()) {
- p_track.position_track.interpolation = gltf_interpolation;
- Vector<real_t> new_times;
- new_times.resize(keys);
- for (int32_t key_i = 0; key_i < keys; key_i++) {
- new_times.write[key_i] = key_i / p_state->get_bake_fps();
+ } else if (node_prop == "position") {
+ if (p_gltf_node_track.position_track.times.is_empty()) {
+ p_gltf_node_track.position_track.interpolation = gltf_interpolation;
+ Vector<double> new_times;
+ new_times.resize(keys);
+ for (int32_t key_i = 0; key_i < keys; key_i++) {
+ new_times.write[key_i] = key_i / p_state->get_bake_fps();
+ }
+ p_gltf_node_track.position_track.times = new_times;
+
+ p_gltf_node_track.position_track.values.resize(keys);
}
- p_track.position_track.times = new_times;
- p_track.position_track.values.resize(keys);
- }
+ for (int32_t key_i = 0; key_i < keys; key_i++) {
+ Vector3 bezier_track = p_gltf_node_track.position_track.values[key_i];
+ if (subpath.size() == 2) {
+ if (subpath[1] == StringName("x")) {
+ bezier_track.x = p_godot_animation->bezier_track_interpolate(p_godot_anim_track_index, key_i / p_state->get_bake_fps());
+ } else if (subpath[1] == StringName("y")) {
+ bezier_track.y = p_godot_animation->bezier_track_interpolate(p_godot_anim_track_index, key_i / p_state->get_bake_fps());
+ } else if (subpath[1] == StringName("z")) {
+ bezier_track.z = p_godot_animation->bezier_track_interpolate(p_godot_anim_track_index, key_i / p_state->get_bake_fps());
+ }
+ }
+ p_gltf_node_track.position_track.values.write[key_i] = bezier_track;
+ }
+ } else if (node_prop == "quaternion") {
+ if (p_gltf_node_track.rotation_track.times.is_empty()) {
+ p_gltf_node_track.rotation_track.interpolation = gltf_interpolation;
+ Vector<double> new_times;
+ new_times.resize(keys);
+ for (int32_t key_i = 0; key_i < keys; key_i++) {
+ new_times.write[key_i] = key_i / p_state->get_bake_fps();
+ }
+ p_gltf_node_track.rotation_track.times = new_times;
- for (int32_t key_i = 0; key_i < keys; key_i++) {
- Vector3 bezier_track = p_track.position_track.values[key_i];
- if (path.contains(":position:x")) {
- bezier_track.x = p_animation->bezier_track_interpolate(p_track_i, key_i / p_state->get_bake_fps());
- } else if (path.contains(":position:y")) {
- bezier_track.y = p_animation->bezier_track_interpolate(p_track_i, key_i / p_state->get_bake_fps());
- } else if (path.contains(":position:z")) {
- bezier_track.z = p_animation->bezier_track_interpolate(p_track_i, key_i / p_state->get_bake_fps());
+ p_gltf_node_track.rotation_track.values.resize(keys);
}
- p_track.position_track.values.write[key_i] = bezier_track;
- }
- } else if (path.contains(":rotation")) {
- if (!p_track.rotation_track.times.size()) {
- p_track.rotation_track.interpolation = gltf_interpolation;
- Vector<real_t> new_times;
- new_times.resize(keys);
for (int32_t key_i = 0; key_i < keys; key_i++) {
- new_times.write[key_i] = key_i / p_state->get_bake_fps();
- }
- p_track.rotation_track.times = new_times;
-
- p_track.rotation_track.values.resize(keys);
- }
- for (int32_t key_i = 0; key_i < keys; key_i++) {
- Quaternion bezier_track = p_track.rotation_track.values[key_i];
- if (path.contains(":rotation:x")) {
- bezier_track.x = p_animation->bezier_track_interpolate(p_track_i, key_i / p_state->get_bake_fps());
- } else if (path.contains(":rotation:y")) {
- bezier_track.y = p_animation->bezier_track_interpolate(p_track_i, key_i / p_state->get_bake_fps());
- } else if (path.contains(":rotation:z")) {
- bezier_track.z = p_animation->bezier_track_interpolate(p_track_i, key_i / p_state->get_bake_fps());
- } else if (path.contains(":rotation:w")) {
- bezier_track.w = p_animation->bezier_track_interpolate(p_track_i, key_i / p_state->get_bake_fps());
+ Quaternion bezier_track = p_gltf_node_track.rotation_track.values[key_i];
+ if (subpath.size() == 2) {
+ if (subpath[1] == StringName("x")) {
+ bezier_track.x = p_godot_animation->bezier_track_interpolate(p_godot_anim_track_index, key_i / p_state->get_bake_fps());
+ } else if (subpath[1] == StringName("y")) {
+ bezier_track.y = p_godot_animation->bezier_track_interpolate(p_godot_anim_track_index, key_i / p_state->get_bake_fps());
+ } else if (subpath[1] == StringName("z")) {
+ bezier_track.z = p_godot_animation->bezier_track_interpolate(p_godot_anim_track_index, key_i / p_state->get_bake_fps());
+ } else if (subpath[1] == StringName("w")) {
+ bezier_track.w = p_godot_animation->bezier_track_interpolate(p_godot_anim_track_index, key_i / p_state->get_bake_fps());
+ }
+ }
+ p_gltf_node_track.rotation_track.values.write[key_i] = bezier_track;
}
- p_track.rotation_track.values.write[key_i] = bezier_track;
+ } else {
+ // This is a Bezier track animating a property, but not a TRS property, so it can't be converted into a node track.
+ return false;
}
+ } else {
+ // This property track isn't a Value track or Bezier track, so it can't be converted into a node track.
+ return false;
}
+ } else {
+ // This isn't a TRS track or a property track, so it can't be converted into a node track.
+ return false;
}
- return p_track;
+ // If we reached this point, the track was some kind of TRS track and was successfully converted.
+ // All failure paths should return false before this point to indicate this
+ // isn't a node track so it can be handled by KHR_animation_pointer instead.
+ return true;
}
-void GLTFDocument::_convert_animation(Ref<GLTFState> p_state, AnimationPlayer *p_animation_player, String p_animation_track_name) {
+void GLTFDocument::_convert_animation(Ref<GLTFState> p_state, AnimationPlayer *p_animation_player, const String &p_animation_track_name) {
Ref<Animation> animation = p_animation_player->get_animation(p_animation_track_name);
Ref<GLTFAnimation> gltf_animation;
gltf_animation.instantiate();
gltf_animation->set_original_name(p_animation_track_name);
gltf_animation->set_name(_gen_unique_name(p_state, p_animation_track_name));
- for (int32_t track_i = 0; track_i < animation->get_track_count(); track_i++) {
- if (!animation->track_is_enabled(track_i)) {
+ HashMap<int, GLTFAnimation::NodeTrack> &node_tracks = gltf_animation->get_node_tracks();
+ for (int32_t track_index = 0; track_index < animation->get_track_count(); track_index++) {
+ if (!animation->track_is_enabled(track_index)) {
continue;
}
- String final_track_path = animation->track_get_path(track_i);
- Node *animation_base_node = p_animation_player->get_parent();
- ERR_CONTINUE_MSG(!animation_base_node, "Cannot get the parent of the animation player.");
- if (String(final_track_path).contains(":position")) {
- const Vector<String> node_suffix = String(final_track_path).split(":position");
- const NodePath path = node_suffix[0];
- const Node *node = animation_base_node->get_node_or_null(path);
- ERR_CONTINUE_MSG(!node, "Cannot get the node from a position path.");
- for (const KeyValue<GLTFNodeIndex, Node *> &position_scene_node_i : p_state->scene_nodes) {
- if (position_scene_node_i.value == node) {
- GLTFNodeIndex node_index = position_scene_node_i.key;
- HashMap<int, GLTFAnimation::Track>::Iterator position_track_i = gltf_animation->get_tracks().find(node_index);
- GLTFAnimation::Track track;
- if (position_track_i) {
- track = position_track_i->value;
- }
- track = _convert_animation_track(p_state, track, animation, track_i, node_index);
- gltf_animation->get_tracks().insert(node_index, track);
- }
- }
- } else if (String(final_track_path).contains(":rotation_degrees")) {
- const Vector<String> node_suffix = String(final_track_path).split(":rotation_degrees");
- const NodePath path = node_suffix[0];
- const Node *node = animation_base_node->get_node_or_null(path);
- ERR_CONTINUE_MSG(!node, "Cannot get the node from a rotation degrees path.");
- for (const KeyValue<GLTFNodeIndex, Node *> &rotation_degree_scene_node_i : p_state->scene_nodes) {
- if (rotation_degree_scene_node_i.value == node) {
- GLTFNodeIndex node_index = rotation_degree_scene_node_i.key;
- HashMap<int, GLTFAnimation::Track>::Iterator rotation_degree_track_i = gltf_animation->get_tracks().find(node_index);
- GLTFAnimation::Track track;
- if (rotation_degree_track_i) {
- track = rotation_degree_track_i->value;
- }
- track = _convert_animation_track(p_state, track, animation, track_i, node_index);
- gltf_animation->get_tracks().insert(node_index, track);
- }
- }
- } else if (String(final_track_path).contains(":scale")) {
- const Vector<String> node_suffix = String(final_track_path).split(":scale");
- const NodePath path = node_suffix[0];
- const Node *node = animation_base_node->get_node_or_null(path);
- ERR_CONTINUE_MSG(!node, "Cannot get the node from a scale path.");
- for (const KeyValue<GLTFNodeIndex, Node *> &scale_scene_node_i : p_state->scene_nodes) {
- if (scale_scene_node_i.value == node) {
- GLTFNodeIndex node_index = scale_scene_node_i.key;
- HashMap<int, GLTFAnimation::Track>::Iterator scale_track_i = gltf_animation->get_tracks().find(node_index);
- GLTFAnimation::Track track;
- if (scale_track_i) {
- track = scale_track_i->value;
- }
- track = _convert_animation_track(p_state, track, animation, track_i, node_index);
- gltf_animation->get_tracks().insert(node_index, track);
- }
- }
- } else if (String(final_track_path).contains(":transform")) {
- const Vector<String> node_suffix = String(final_track_path).split(":transform");
- const NodePath path = node_suffix[0];
- const Node *node = animation_base_node->get_node_or_null(path);
- ERR_CONTINUE_MSG(!node, "Cannot get the node from a transform path.");
- for (const KeyValue<GLTFNodeIndex, Node *> &transform_track_i : p_state->scene_nodes) {
- if (transform_track_i.value == node) {
- GLTFAnimation::Track track;
- track = _convert_animation_track(p_state, track, animation, track_i, transform_track_i.key);
- gltf_animation->get_tracks().insert(transform_track_i.key, track);
- }
- }
- } else if (String(final_track_path).contains(":") && animation->track_get_type(track_i) == Animation::TYPE_BLEND_SHAPE) {
- const Vector<String> node_suffix = String(final_track_path).split(":");
- const NodePath path = node_suffix[0];
- const String suffix = node_suffix[1];
- Node *node = animation_base_node->get_node_or_null(path);
- ERR_CONTINUE_MSG(!node, "Cannot get the node from a blend shape path.");
- MeshInstance3D *mi = cast_to<MeshInstance3D>(node);
- if (!mi) {
- continue;
- }
- Ref<Mesh> mesh = mi->get_mesh();
+ // Get the Godot node and the glTF node index for the animation track.
+ const NodePath track_path = animation->track_get_path(track_index);
+ const Node *anim_player_parent = p_animation_player->get_parent();
+ const Node *animated_node = anim_player_parent->get_node_or_null(track_path);
+ ERR_CONTINUE_MSG(!animated_node, "glTF: Cannot get node for animated track using path: " + String(track_path));
+ const GLTFAnimation::Interpolation gltf_interpolation = GLTFAnimation::godot_to_gltf_interpolation(animation, track_index);
+ // First, check if it's a Blend Shape track.
+ if (animation->track_get_type(track_index) == Animation::TYPE_BLEND_SHAPE) {
+ const MeshInstance3D *mesh_instance = Object::cast_to<MeshInstance3D>(animated_node);
+ ERR_CONTINUE_MSG(!mesh_instance, "glTF: Animation had a Blend Shape track, but the node wasn't a MeshInstance3D. Ignoring this track.");
+ Ref<Mesh> mesh = mesh_instance->get_mesh();
ERR_CONTINUE(mesh.is_null());
int32_t mesh_index = -1;
for (const KeyValue<GLTFNodeIndex, Node *> &mesh_track_i : p_state->scene_nodes) {
- if (mesh_track_i.value == node) {
+ if (mesh_track_i.value == animated_node) {
mesh_index = mesh_track_i.key;
}
}
ERR_CONTINUE(mesh_index == -1);
- HashMap<int, GLTFAnimation::Track> &tracks = gltf_animation->get_tracks();
- GLTFAnimation::Track track = gltf_animation->get_tracks().has(mesh_index) ? gltf_animation->get_tracks()[mesh_index] : GLTFAnimation::Track();
- if (!tracks.has(mesh_index)) {
+ GLTFAnimation::NodeTrack track = node_tracks.has(mesh_index) ? node_tracks[mesh_index] : GLTFAnimation::NodeTrack();
+ if (!node_tracks.has(mesh_index)) {
for (int32_t shape_i = 0; shape_i < mesh->get_blend_shape_count(); shape_i++) {
String shape_name = mesh->get_blend_shape_name(shape_i);
- NodePath shape_path = String(path) + ":" + shape_name;
+ NodePath shape_path = NodePath(track_path.get_names(), { shape_name }, false);
int32_t shape_track_i = animation->find_track(shape_path, Animation::TYPE_BLEND_SHAPE);
if (shape_track_i == -1) {
GLTFAnimation::Channel<real_t> weight;
@@ -6799,15 +7864,6 @@ void GLTFDocument::_convert_animation(Ref<GLTFState> p_state, AnimationPlayer *p
track.weight_tracks.push_back(weight);
continue;
}
- Animation::InterpolationType interpolation = animation->track_get_interpolation_type(track_i);
- GLTFAnimation::Interpolation gltf_interpolation = GLTFAnimation::INTERP_LINEAR;
- if (interpolation == Animation::InterpolationType::INTERPOLATION_LINEAR) {
- gltf_interpolation = GLTFAnimation::INTERP_LINEAR;
- } else if (interpolation == Animation::InterpolationType::INTERPOLATION_NEAREST) {
- gltf_interpolation = GLTFAnimation::INTERP_STEP;
- } else if (interpolation == Animation::InterpolationType::INTERPOLATION_CUBIC) {
- gltf_interpolation = GLTFAnimation::INTERP_CUBIC_SPLINE;
- }
int32_t key_count = animation->track_get_key_count(shape_track_i);
GLTFAnimation::Channel<real_t> weight;
weight.interpolation = gltf_interpolation;
@@ -6821,64 +7877,74 @@ void GLTFDocument::_convert_animation(Ref<GLTFState> p_state, AnimationPlayer *p
}
track.weight_tracks.push_back(weight);
}
- tracks[mesh_index] = track;
- }
- } else if (String(final_track_path).contains(":")) {
- //Process skeleton
- const Vector<String> node_suffix = String(final_track_path).split(":");
- const String &node = node_suffix[0];
- const NodePath node_path = node;
- const String &suffix = node_suffix[1];
- Node *godot_node = animation_base_node->get_node_or_null(node_path);
- if (!godot_node) {
- continue;
- }
- Skeleton3D *skeleton = cast_to<Skeleton3D>(animation_base_node->get_node_or_null(node));
- if (!skeleton) {
- continue;
- }
- GLTFSkeletonIndex skeleton_gltf_i = -1;
- for (GLTFSkeletonIndex skeleton_i = 0; skeleton_i < p_state->skeletons.size(); skeleton_i++) {
- if (p_state->skeletons[skeleton_i]->godot_skeleton == cast_to<Skeleton3D>(godot_node)) {
- skeleton = p_state->skeletons[skeleton_i]->godot_skeleton;
- skeleton_gltf_i = skeleton_i;
- ERR_CONTINUE(!skeleton);
- Ref<GLTFSkeleton> skeleton_gltf = p_state->skeletons[skeleton_gltf_i];
- int32_t bone = skeleton->find_bone(suffix);
- ERR_CONTINUE_MSG(bone == -1, vformat("Cannot find the bone %s.", suffix));
- if (!skeleton_gltf->godot_bone_node.has(bone)) {
- continue;
- }
- GLTFNodeIndex node_i = skeleton_gltf->godot_bone_node[bone];
- HashMap<int, GLTFAnimation::Track>::Iterator property_track_i = gltf_animation->get_tracks().find(node_i);
- GLTFAnimation::Track track;
- if (property_track_i) {
- track = property_track_i->value;
- }
- track = _convert_animation_track(p_state, track, animation, track_i, node_i);
- gltf_animation->get_tracks()[node_i] = track;
- }
- }
- } else if (!String(final_track_path).contains(":")) {
- ERR_CONTINUE(!animation_base_node);
- Node *godot_node = animation_base_node->get_node_or_null(final_track_path);
- ERR_CONTINUE_MSG(!godot_node, vformat("Cannot get the node from a skeleton path %s.", final_track_path));
- for (const KeyValue<GLTFNodeIndex, Node *> &scene_node_i : p_state->scene_nodes) {
- if (scene_node_i.value == godot_node) {
- GLTFNodeIndex node_i = scene_node_i.key;
- HashMap<int, GLTFAnimation::Track>::Iterator node_track_i = gltf_animation->get_tracks().find(node_i);
- GLTFAnimation::Track track;
- if (node_track_i) {
- track = node_track_i->value;
- }
- track = _convert_animation_track(p_state, track, animation, track_i, node_i);
- gltf_animation->get_tracks()[node_i] = track;
- break;
- }
+ node_tracks[mesh_index] = track;
}
+ continue;
}
- }
- if (gltf_animation->get_tracks().size()) {
+ // If it's not a Blend Shape track, it must either be a TRS track, a property Value track, or something we can't handle.
+ // For the cases we can handle, we will need to know the glTF node index, glTF interpolation, and the times of the track.
+ const Vector<StringName> subnames = track_path.get_subnames();
+ const GLTFNodeIndex node_i = _node_and_or_bone_to_gltf_node_index(p_state, subnames, animated_node);
+ ERR_CONTINUE_MSG(node_i == -1, "glTF: Cannot get glTF node index for animated track using path: " + String(track_path));
+ const int anim_key_count = animation->track_get_key_count(track_index);
+ Vector<double> times;
+ times.resize(anim_key_count);
+ for (int32_t key_i = 0; key_i < anim_key_count; key_i++) {
+ times.write[key_i] = animation->track_get_key_time(track_index, key_i);
+ }
+ // Try converting the track to a TRS glTF node track. This will only succeed if the Godot animation is a TRS track.
+ const HashMap<int, GLTFAnimation::NodeTrack>::Iterator node_track_iter = node_tracks.find(node_i);
+ GLTFAnimation::NodeTrack track;
+ if (node_track_iter) {
+ track = node_track_iter->value;
+ }
+ if (_convert_animation_node_track(p_state, track, animation, track_index, times)) {
+ // If the track was successfully converted, save it and continue to the next track.
+ node_tracks[node_i] = track;
+ continue;
+ }
+ // If the track wasn't a TRS track or Blend Shape track, it might be a Value track animating a property.
+ // Then this is something that we need to handle with KHR_animation_pointer.
+ Ref<GLTFObjectModelProperty> obj_model_prop = export_object_model_property(p_state, track_path, animated_node, node_i);
+ if (obj_model_prop.is_valid() && obj_model_prop->has_json_pointers()) {
+ // Insert the property track into the KHR_animation_pointer pointer tracks.
+ GLTFAnimation::Channel<Variant> channel;
+ channel.interpolation = gltf_interpolation;
+ channel.times = times;
+ channel.values.resize(anim_key_count);
+ // If using an expression, determine the base instance to pass to the expression.
+ const Ref<Expression> godot_to_gltf_expr = obj_model_prop->get_godot_to_gltf_expression();
+ const bool is_godot_to_gltf_expr_valid = godot_to_gltf_expr.is_valid();
+ Object *base_instance = nullptr;
+ if (is_godot_to_gltf_expr_valid) {
+ Ref<Resource> resource;
+ Vector<StringName> leftover_subpath;
+ base_instance = anim_player_parent->get_node_and_resource(track_path, resource, leftover_subpath);
+ if (resource.is_valid()) {
+ base_instance = resource.ptr();
+ }
+ }
+ // Convert the Godot animation values into glTF animation values (still Variant).
+ for (int32_t key_i = 0; key_i < anim_key_count; key_i++) {
+ Variant value = animation->track_get_key_value(track_index, key_i);
+ if (is_godot_to_gltf_expr_valid) {
+ Array inputs;
+ inputs.append(value);
+ value = godot_to_gltf_expr->execute(inputs, base_instance);
+ }
+ channel.values.write[key_i] = value;
+ }
+ // Use the JSON pointer to insert the property track into the pointer tracks. There will usually be just one JSON pointer.
+ HashMap<String, GLTFAnimation::Channel<Variant>> &pointer_tracks = gltf_animation->get_pointer_tracks();
+ Vector<PackedStringArray> split_json_pointers = obj_model_prop->get_json_pointers();
+ for (const PackedStringArray &split_json_pointer : split_json_pointers) {
+ String json_pointer_str = "/" + String("/").join(split_json_pointer);
+ p_state->object_model_properties[json_pointer_str] = obj_model_prop;
+ pointer_tracks[json_pointer_str] = channel;
+ }
+ }
+ }
+ if (!gltf_animation->is_empty_of_tracks()) {
p_state->animations.push_back(gltf_animation);
}
}
@@ -7077,6 +8143,9 @@ void GLTFDocument::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "lossy_quality"), "set_lossy_quality", "get_lossy_quality");
ADD_PROPERTY(PropertyInfo(Variant::INT, "root_node_mode"), "set_root_node_mode", "get_root_node_mode");
+ ClassDB::bind_static_method("GLTFDocument", D_METHOD("import_object_model_property", "state", "json_pointer"), &GLTFDocument::import_object_model_property);
+ ClassDB::bind_static_method("GLTFDocument", D_METHOD("export_object_model_property", "state", "node_path", "godot_node", "gltf_node_index"), &GLTFDocument::export_object_model_property);
+
ClassDB::bind_static_method("GLTFDocument", D_METHOD("register_gltf_document_extension", "extension", "first_priority"),
&GLTFDocument::register_gltf_document_extension, DEFVAL(false));
ClassDB::bind_static_method("GLTFDocument", D_METHOD("unregister_gltf_document_extension", "extension"),
@@ -7138,6 +8207,7 @@ HashSet<String> GLTFDocument::get_supported_gltf_extensions_hashset() {
// If the extension is supported directly in GLTFDocument, list it here.
// Other built-in extensions are supported by GLTFDocumentExtension classes.
supported_extensions.insert("GODOT_single_root");
+ supported_extensions.insert("KHR_animation_pointer");
supported_extensions.insert("KHR_lights_punctual");
supported_extensions.insert("KHR_materials_emissive_strength");
supported_extensions.insert("KHR_materials_pbrSpecularGlossiness");
@@ -7315,6 +8385,10 @@ Error GLTFDocument::_parse_gltf_state(Ref<GLTFState> p_state, const String &p_se
err = SkinTool::_determine_skeletons(p_state->skins, p_state->nodes, p_state->skeletons, p_state->get_import_as_skeleton_bones() ? p_state->root_nodes : Vector<GLTFNodeIndex>());
ERR_FAIL_COND_V(err != OK, ERR_PARSE_ERROR);
+ /* ASSIGN SCENE NODE NAMES */
+ // This must be run AFTER determining skeletons, and BEFORE parsing animations.
+ _assign_node_names(p_state);
+
/* PARSE MESHES (we have enough info now) */
err = _parse_meshes(p_state);
ERR_FAIL_COND_V(err != OK, ERR_PARSE_ERROR);
@@ -7331,9 +8405,6 @@ Error GLTFDocument::_parse_gltf_state(Ref<GLTFState> p_state, const String &p_se
err = _parse_animations(p_state);
ERR_FAIL_COND_V(err != OK, ERR_PARSE_ERROR);
- /* ASSIGN SCENE NAMES */
- _assign_node_names(p_state);
-
return OK;
}
@@ -7352,7 +8423,7 @@ PackedByteArray GLTFDocument::generate_buffer(Ref<GLTFState> p_state) {
Error GLTFDocument::write_to_filesystem(Ref<GLTFState> p_state, const String &p_path) {
Ref<GLTFState> state = p_state;
ERR_FAIL_COND_V(state.is_null(), ERR_INVALID_PARAMETER);
- state->base_path = p_path.get_base_dir();
+ state->set_base_path(p_path.get_base_dir());
state->filename = p_path.get_file();
Error err = _serialize(state);
if (err != OK) {
@@ -7465,7 +8536,7 @@ Error GLTFDocument::append_from_buffer(PackedByteArray p_bytes, String p_base_pa
Ref<FileAccessMemory> file_access;
file_access.instantiate();
file_access->open_custom(p_bytes.ptr(), p_bytes.size());
- state->base_path = p_base_path.get_base_dir();
+ state->set_base_path(p_base_path.get_base_dir());
err = _parse(p_state, state->base_path, file_access);
ERR_FAIL_COND_V(err != OK, err);
for (Ref<GLTFDocumentExtension> ext : document_extensions) {
@@ -7482,7 +8553,7 @@ Error GLTFDocument::append_from_file(String p_path, Ref<GLTFState> p_state, uint
if (state == Ref<GLTFState>()) {
state.instantiate();
}
- state->filename = p_path.get_file().get_basename();
+ state->set_filename(p_path.get_file().get_basename());
state->use_named_skin_binds = p_flags & GLTF_IMPORT_USE_NAMED_SKIN_BINDS;
state->discard_meshes_and_materials = p_flags & GLTF_IMPORT_DISCARD_MESHES_AND_MATERIALS;
state->force_generate_tangents = p_flags & GLTF_IMPORT_GENERATE_TANGENT_ARRAYS;
@@ -7496,7 +8567,7 @@ Error GLTFDocument::append_from_file(String p_path, Ref<GLTFState> p_state, uint
if (base_path.is_empty()) {
base_path = p_path.get_base_dir();
}
- state->base_path = base_path;
+ state->set_base_path(base_path);
err = _parse(p_state, base_path, file);
ERR_FAIL_COND_V(err != OK, err);
for (Ref<GLTFDocumentExtension> ext : document_extensions) {
diff --git a/modules/gltf/gltf_document.h b/modules/gltf/gltf_document.h
index 18b9f5bf2a..96cb9a1f48 100644
--- a/modules/gltf/gltf_document.h
+++ b/modules/gltf/gltf_document.h
@@ -58,13 +58,6 @@ public:
enum {
ARRAY_BUFFER = 34962,
ELEMENT_ARRAY_BUFFER = 34963,
-
- COMPONENT_TYPE_BYTE = 5120,
- COMPONENT_TYPE_UNSIGNED_BYTE = 5121,
- COMPONENT_TYPE_SHORT = 5122,
- COMPONENT_TYPE_UNSIGNED_SHORT = 5123,
- COMPONENT_TYPE_INT = 5125,
- COMPONENT_TYPE_FLOAT = 5126,
};
enum {
TEXTURE_TYPE_GENERIC = 0,
@@ -97,6 +90,10 @@ public:
static Vector<String> get_supported_gltf_extensions();
static HashSet<String> get_supported_gltf_extensions_hashset();
+ static NodePath _find_material_node_path(Ref<GLTFState> p_state, Ref<Material> p_material);
+ static Ref<GLTFObjectModelProperty> import_object_model_property(Ref<GLTFState> p_state, const String &p_json_pointer);
+ static Ref<GLTFObjectModelProperty> export_object_model_property(Ref<GLTFState> p_state, const NodePath &p_node_path, const Node *p_godot_node, GLTFNodeIndex p_gltf_node_index);
+
void set_naming_version(int p_version);
int get_naming_version() const;
void set_image_format(const String &p_image_format);
@@ -111,8 +108,8 @@ private:
void _build_parent_hierachy(Ref<GLTFState> p_state);
double _filter_number(double p_float);
void _round_min_max_components(Vector<double> &r_type_min, Vector<double> &r_type_max);
- String _get_component_type_name(const uint32_t p_component);
- int _get_component_type_size(const int p_component_type);
+ String _get_component_type_name(const GLTFAccessor::GLTFComponentType p_component_type);
+ int _get_component_type_size(const GLTFAccessor::GLTFComponentType p_component_type);
Error _parse_scenes(Ref<GLTFState> p_state);
Error _parse_nodes(Ref<GLTFState> p_state);
String _get_accessor_type_name(const GLTFAccessor::GLTFAccessorType p_accessor_type);
@@ -142,7 +139,7 @@ private:
const int p_skip_every, const int p_skip_bytes,
const int p_element_size, const int p_count,
const GLTFAccessor::GLTFAccessorType p_accessor_type, const int p_component_count,
- const int p_component_type, const int p_component_size,
+ const GLTFAccessor::GLTFComponentType p_component_type, const int p_component_size,
const bool p_normalized, const int p_byte_offset,
const bool p_for_vertex);
Vector<double> _decode_accessor(Ref<GLTFState> p_state,
@@ -180,6 +177,15 @@ private:
Vector<Transform3D> _decode_accessor_as_xform(Ref<GLTFState> p_state,
const GLTFAccessorIndex p_accessor,
const bool p_for_vertex);
+ Vector<Variant> _decode_accessor_as_variant(Ref<GLTFState> p_state,
+ const GLTFAccessorIndex p_accessor,
+ Variant::Type p_variant_type,
+ GLTFAccessor::GLTFAccessorType p_accessor_type);
+ GLTFAccessorIndex _encode_accessor_as_variant(Ref<GLTFState> p_state,
+ Vector<Variant> p_attribs,
+ Variant::Type p_variant_type,
+ GLTFAccessor::GLTFAccessorType p_accessor_type,
+ GLTFAccessor::GLTFComponentType p_component_type = GLTFAccessor::COMPONENT_TYPE_SINGLE_FLOAT);
Error _parse_meshes(Ref<GLTFState> p_state);
Error _serialize_textures(Ref<GLTFState> p_state);
Error _serialize_texture_samplers(Ref<GLTFState> p_state);
@@ -207,6 +213,7 @@ private:
Error _parse_cameras(Ref<GLTFState> p_state);
Error _parse_lights(Ref<GLTFState> p_state);
Error _parse_animations(Ref<GLTFState> p_state);
+ void _parse_animation_pointer(Ref<GLTFState> p_state, const String &p_animation_json_pointer, const Ref<GLTFAnimation> p_gltf_animation, const GLTFAnimation::Interpolation p_interp, const Vector<double> &p_times, const int p_output_value_accessor_index);
Error _serialize_animations(Ref<GLTFState> p_state);
BoneAttachment3D *_generate_bone_attachment(Ref<GLTFState> p_state,
Skeleton3D *p_skeleton,
@@ -218,7 +225,7 @@ private:
Node3D *_generate_spatial(Ref<GLTFState> p_state, const GLTFNodeIndex p_node_index);
void _assign_node_names(Ref<GLTFState> p_state);
template <typename T>
- T _interpolate_track(const Vector<real_t> &p_times, const Vector<T> &p_values,
+ T _interpolate_track(const Vector<double> &p_times, const Vector<T> &p_values,
const float p_time,
const GLTFAnimation::Interpolation p_interp);
GLTFAccessorIndex _encode_accessor_as_quaternions(Ref<GLTFState> p_state,
@@ -231,7 +238,7 @@ private:
const Vector<Color> p_attribs,
const bool p_for_vertex);
GLTFAccessorIndex _encode_accessor_as_floats(Ref<GLTFState> p_state,
- const Vector<real_t> p_attribs,
+ const Vector<double> p_attribs,
const bool p_for_vertex);
GLTFAccessorIndex _encode_accessor_as_vec2(Ref<GLTFState> p_state,
const Vector<Vector2> p_attribs,
@@ -271,7 +278,7 @@ private:
const bool p_for_vertex);
Error _encode_buffer_view(Ref<GLTFState> p_state, const double *p_src,
const int p_count, const GLTFAccessor::GLTFAccessorType p_accessor_type,
- const int p_component_type, const bool p_normalized,
+ const GLTFAccessor::GLTFComponentType p_component_type, const bool p_normalized,
const int p_byte_offset, const bool p_for_vertex,
GLTFBufferViewIndex &r_accessor, const bool p_for_indices = false);
@@ -282,11 +289,6 @@ private:
Error _serialize_nodes(Ref<GLTFState> p_state);
Error _serialize_scenes(Ref<GLTFState> p_state);
String interpolation_to_string(const GLTFAnimation::Interpolation p_interp);
- GLTFAnimation::Track _convert_animation_track(Ref<GLTFState> p_state,
- GLTFAnimation::Track p_track,
- Ref<Animation> p_animation,
- int32_t p_track_i,
- GLTFNodeIndex p_node_i);
Error _encode_buffer_bins(Ref<GLTFState> p_state, const String &p_path);
Error _encode_buffer_glb(Ref<GLTFState> p_state, const String &p_path);
PackedByteArray _serialize_glb_buffer(Ref<GLTFState> p_state, Error *r_err);
@@ -344,11 +346,6 @@ public:
void _convert_csg_shape_to_gltf(CSGShape3D *p_current, GLTFNodeIndex p_gltf_parent, Ref<GLTFNode> p_gltf_node, Ref<GLTFState> p_state);
#endif // MODULE_CSG_ENABLED
- void _convert_animation_player_to_gltf(
- AnimationPlayer *p_animation_player, Ref<GLTFState> p_state,
- GLTFNodeIndex p_gltf_current,
- GLTFNodeIndex p_gltf_root_index,
- Ref<GLTFNode> p_gltf_node, Node *p_scene_parent);
void _check_visibility(Node *p_node, bool &r_retflag);
void _convert_camera_to_gltf(Camera3D *p_camera, Ref<GLTFState> p_state,
Ref<GLTFNode> p_gltf_node);
@@ -379,7 +376,15 @@ public:
Ref<GLTFNode> p_gltf_node);
GLTFMeshIndex _convert_mesh_to_gltf(Ref<GLTFState> p_state,
MeshInstance3D *p_mesh_instance);
- void _convert_animation(Ref<GLTFState> p_state, AnimationPlayer *p_animation_player, String p_animation_track_name);
+
+ GLTFNodeIndex _node_and_or_bone_to_gltf_node_index(Ref<GLTFState> p_state, const Vector<StringName> &p_node_subpath, const Node *p_godot_node);
+ bool _convert_animation_node_track(Ref<GLTFState> p_state,
+ GLTFAnimation::NodeTrack &p_gltf_node_track,
+ const Ref<Animation> &p_godot_animation,
+ int32_t p_godot_anim_track_index,
+ Vector<double> &p_times);
+ void _convert_animation(Ref<GLTFState> p_state, AnimationPlayer *p_animation_player, const String &p_animation_track_name);
+
Error _serialize(Ref<GLTFState> p_state);
Error _parse(Ref<GLTFState> p_state, String p_path, Ref<FileAccess> p_file);
};
diff --git a/modules/gltf/gltf_state.cpp b/modules/gltf/gltf_state.cpp
index 15ada00a13..65a7735684 100644
--- a/modules/gltf/gltf_state.cpp
+++ b/modules/gltf/gltf_state.cpp
@@ -399,8 +399,27 @@ String GLTFState::get_base_path() {
return base_path;
}
-void GLTFState::set_base_path(String p_base_path) {
+void GLTFState::set_base_path(const String &p_base_path) {
base_path = p_base_path;
+ if (extract_path.is_empty()) {
+ extract_path = p_base_path;
+ }
+}
+
+String GLTFState::get_extract_path() {
+ return extract_path;
+}
+
+void GLTFState::set_extract_path(const String &p_extract_path) {
+ extract_path = p_extract_path;
+}
+
+String GLTFState::get_extract_prefix() {
+ return extract_prefix;
+}
+
+void GLTFState::set_extract_prefix(const String &p_extract_prefix) {
+ extract_prefix = p_extract_prefix;
}
String GLTFState::get_filename() const {
@@ -409,6 +428,9 @@ String GLTFState::get_filename() const {
void GLTFState::set_filename(const String &p_filename) {
filename = p_filename;
+ if (extract_prefix.is_empty()) {
+ extract_prefix = p_filename.get_basename();
+ }
}
Variant GLTFState::get_additional_data(const StringName &p_extension_name) {
diff --git a/modules/gltf/gltf_state.h b/modules/gltf/gltf_state.h
index d077d767e6..bbee9435e2 100644
--- a/modules/gltf/gltf_state.h
+++ b/modules/gltf/gltf_state.h
@@ -40,6 +40,7 @@
#include "structures/gltf_camera.h"
#include "structures/gltf_mesh.h"
#include "structures/gltf_node.h"
+#include "structures/gltf_object_model_property.h"
#include "structures/gltf_skeleton.h"
#include "structures/gltf_skin.h"
#include "structures/gltf_texture.h"
@@ -50,9 +51,12 @@
class GLTFState : public Resource {
GDCLASS(GLTFState, Resource);
friend class GLTFDocument;
+ friend class GLTFNode;
protected:
String base_path;
+ String extract_path;
+ String extract_prefix;
String filename;
Dictionary json;
int major_version = 0;
@@ -102,6 +106,7 @@ protected:
Vector<Ref<GLTFAnimation>> animations;
HashMap<GLTFNodeIndex, Node *> scene_nodes;
HashMap<GLTFNodeIndex, ImporterMeshInstance3D *> scene_mesh_instances;
+ HashMap<String, Ref<GLTFObjectModelProperty>> object_model_properties;
HashMap<ObjectID, GLTFSkeletonIndex> skeleton3d_to_gltf_skeleton;
HashMap<ObjectID, HashMap<ObjectID, GLTFSkinIndex>> skin_and_skeleton3d_to_gltf_skin;
@@ -188,7 +193,13 @@ public:
void set_scene_name(String p_scene_name);
String get_base_path();
- void set_base_path(String p_base_path);
+ void set_base_path(const String &p_base_path);
+
+ String get_extract_path();
+ void set_extract_path(const String &p_extract_path);
+
+ String get_extract_prefix();
+ void set_extract_prefix(const String &p_extract_prefix);
String get_filename() const;
void set_filename(const String &p_filename);
diff --git a/modules/gltf/register_types.cpp b/modules/gltf/register_types.cpp
index 83dee34135..09332cd751 100644
--- a/modules/gltf/register_types.cpp
+++ b/modules/gltf/register_types.cpp
@@ -39,6 +39,7 @@
#include "extensions/physics/gltf_document_extension_physics.h"
#include "gltf_document.h"
#include "gltf_state.h"
+#include "structures/gltf_object_model_property.h"
#ifdef TOOLS_ENABLED
#include "editor/editor_import_blend_runner.h"
@@ -114,6 +115,7 @@ void initialize_gltf_module(ModuleInitializationLevel p_level) {
GDREGISTER_CLASS(GLTFLight);
GDREGISTER_CLASS(GLTFMesh);
GDREGISTER_CLASS(GLTFNode);
+ GDREGISTER_CLASS(GLTFObjectModelProperty);
GDREGISTER_CLASS(GLTFPhysicsBody);
GDREGISTER_CLASS(GLTFPhysicsShape);
GDREGISTER_CLASS(GLTFSkeleton);
diff --git a/modules/gltf/structures/gltf_accessor.cpp b/modules/gltf/structures/gltf_accessor.cpp
index 8b2eeebeea..a68ae86e55 100644
--- a/modules/gltf/structures/gltf_accessor.cpp
+++ b/modules/gltf/structures/gltf_accessor.cpp
@@ -41,6 +41,19 @@ void GLTFAccessor::_bind_methods() {
BIND_ENUM_CONSTANT(TYPE_MAT3);
BIND_ENUM_CONSTANT(TYPE_MAT4);
+ BIND_ENUM_CONSTANT(COMPONENT_TYPE_NONE);
+ BIND_ENUM_CONSTANT(COMPONENT_TYPE_SIGNED_BYTE);
+ BIND_ENUM_CONSTANT(COMPONENT_TYPE_UNSIGNED_BYTE);
+ BIND_ENUM_CONSTANT(COMPONENT_TYPE_SIGNED_SHORT);
+ BIND_ENUM_CONSTANT(COMPONENT_TYPE_UNSIGNED_SHORT);
+ BIND_ENUM_CONSTANT(COMPONENT_TYPE_SIGNED_INT);
+ BIND_ENUM_CONSTANT(COMPONENT_TYPE_UNSIGNED_INT);
+ BIND_ENUM_CONSTANT(COMPONENT_TYPE_SINGLE_FLOAT);
+ BIND_ENUM_CONSTANT(COMPONENT_TYPE_DOUBLE_FLOAT);
+ BIND_ENUM_CONSTANT(COMPONENT_TYPE_HALF_FLOAT);
+ BIND_ENUM_CONSTANT(COMPONENT_TYPE_SIGNED_LONG);
+ BIND_ENUM_CONSTANT(COMPONENT_TYPE_UNSIGNED_LONG);
+
ClassDB::bind_method(D_METHOD("get_buffer_view"), &GLTFAccessor::get_buffer_view);
ClassDB::bind_method(D_METHOD("set_buffer_view", "buffer_view"), &GLTFAccessor::set_buffer_view);
ClassDB::bind_method(D_METHOD("get_byte_offset"), &GLTFAccessor::get_byte_offset);
@@ -110,7 +123,7 @@ int GLTFAccessor::get_component_type() {
}
void GLTFAccessor::set_component_type(int p_component_type) {
- component_type = p_component_type;
+ component_type = (GLTFComponentType)p_component_type;
}
bool GLTFAccessor::get_normalized() {
@@ -190,7 +203,7 @@ int GLTFAccessor::get_sparse_indices_component_type() {
}
void GLTFAccessor::set_sparse_indices_component_type(int p_sparse_indices_component_type) {
- sparse_indices_component_type = p_sparse_indices_component_type;
+ sparse_indices_component_type = (GLTFComponentType)p_sparse_indices_component_type;
}
int GLTFAccessor::get_sparse_values_buffer_view() {
diff --git a/modules/gltf/structures/gltf_accessor.h b/modules/gltf/structures/gltf_accessor.h
index dfde7af490..8b7cd2875f 100644
--- a/modules/gltf/structures/gltf_accessor.h
+++ b/modules/gltf/structures/gltf_accessor.h
@@ -52,10 +52,25 @@ public:
TYPE_MAT4,
};
+ enum GLTFComponentType {
+ COMPONENT_TYPE_NONE = 0,
+ COMPONENT_TYPE_SIGNED_BYTE = 5120,
+ COMPONENT_TYPE_UNSIGNED_BYTE = 5121,
+ COMPONENT_TYPE_SIGNED_SHORT = 5122,
+ COMPONENT_TYPE_UNSIGNED_SHORT = 5123,
+ COMPONENT_TYPE_SIGNED_INT = 5124,
+ COMPONENT_TYPE_UNSIGNED_INT = 5125,
+ COMPONENT_TYPE_SINGLE_FLOAT = 5126,
+ COMPONENT_TYPE_DOUBLE_FLOAT = 5130,
+ COMPONENT_TYPE_HALF_FLOAT = 5131,
+ COMPONENT_TYPE_SIGNED_LONG = 5134,
+ COMPONENT_TYPE_UNSIGNED_LONG = 5135,
+ };
+
private:
GLTFBufferViewIndex buffer_view = -1;
int byte_offset = 0;
- int component_type = 0;
+ GLTFComponentType component_type = COMPONENT_TYPE_NONE;
bool normalized = false;
int count = 0;
GLTFAccessorType accessor_type = GLTFAccessorType::TYPE_SCALAR;
@@ -64,7 +79,7 @@ private:
int sparse_count = 0;
int sparse_indices_buffer_view = 0;
int sparse_indices_byte_offset = 0;
- int sparse_indices_component_type = 0;
+ GLTFComponentType sparse_indices_component_type = COMPONENT_TYPE_NONE;
int sparse_values_buffer_view = 0;
int sparse_values_byte_offset = 0;
@@ -119,5 +134,6 @@ public:
};
VARIANT_ENUM_CAST(GLTFAccessor::GLTFAccessorType);
+VARIANT_ENUM_CAST(GLTFAccessor::GLTFComponentType);
#endif // GLTF_ACCESSOR_H
diff --git a/modules/gltf/structures/gltf_animation.cpp b/modules/gltf/structures/gltf_animation.cpp
index 4b99f004cf..ada70d1a79 100644
--- a/modules/gltf/structures/gltf_animation.cpp
+++ b/modules/gltf/structures/gltf_animation.cpp
@@ -44,6 +44,34 @@ void GLTFAnimation::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "loop"), "set_loop", "get_loop"); // bool
}
+GLTFAnimation::Interpolation GLTFAnimation::godot_to_gltf_interpolation(const Ref<Animation> &p_godot_animation, int32_t p_godot_anim_track_index) {
+ Animation::InterpolationType interpolation = p_godot_animation->track_get_interpolation_type(p_godot_anim_track_index);
+ switch (interpolation) {
+ case Animation::INTERPOLATION_LINEAR:
+ case Animation::INTERPOLATION_LINEAR_ANGLE:
+ return INTERP_LINEAR;
+ case Animation::INTERPOLATION_NEAREST:
+ return INTERP_STEP;
+ case Animation::INTERPOLATION_CUBIC:
+ case Animation::INTERPOLATION_CUBIC_ANGLE:
+ return INTERP_CUBIC_SPLINE;
+ }
+ return INTERP_LINEAR;
+}
+
+Animation::InterpolationType GLTFAnimation::gltf_to_godot_interpolation(Interpolation p_gltf_interpolation) {
+ switch (p_gltf_interpolation) {
+ case INTERP_LINEAR:
+ return Animation::INTERPOLATION_LINEAR;
+ case INTERP_STEP:
+ return Animation::INTERPOLATION_NEAREST;
+ case INTERP_CATMULLROMSPLINE:
+ case INTERP_CUBIC_SPLINE:
+ return Animation::INTERPOLATION_CUBIC;
+ }
+ return Animation::INTERPOLATION_LINEAR;
+}
+
String GLTFAnimation::get_original_name() {
return original_name;
}
@@ -60,8 +88,16 @@ void GLTFAnimation::set_loop(bool p_val) {
loop = p_val;
}
-HashMap<int, GLTFAnimation::Track> &GLTFAnimation::get_tracks() {
- return tracks;
+HashMap<int, GLTFAnimation::NodeTrack> &GLTFAnimation::get_node_tracks() {
+ return node_tracks;
+}
+
+HashMap<String, GLTFAnimation::Channel<Variant>> &GLTFAnimation::get_pointer_tracks() {
+ return pointer_tracks;
+}
+
+bool GLTFAnimation::is_empty_of_tracks() const {
+ return node_tracks.is_empty() && pointer_tracks.is_empty();
}
GLTFAnimation::GLTFAnimation() {
diff --git a/modules/gltf/structures/gltf_animation.h b/modules/gltf/structures/gltf_animation.h
index 57689ce61a..8f10f717ed 100644
--- a/modules/gltf/structures/gltf_animation.h
+++ b/modules/gltf/structures/gltf_animation.h
@@ -52,33 +52,41 @@ public:
template <typename T>
struct Channel {
Interpolation interpolation = INTERP_LINEAR;
- Vector<real_t> times;
+ Vector<double> times;
Vector<T> values;
};
- struct Track {
+ struct NodeTrack {
Channel<Vector3> position_track;
Channel<Quaternion> rotation_track;
Channel<Vector3> scale_track;
Vector<Channel<real_t>> weight_tracks;
};
+ String original_name;
+ bool loop = false;
+ HashMap<int, NodeTrack> node_tracks;
+ HashMap<String, Channel<Variant>> pointer_tracks;
+ Dictionary additional_data;
+
public:
+ static Interpolation godot_to_gltf_interpolation(const Ref<Animation> &p_godot_animation, int32_t p_godot_anim_track_index);
+ static Animation::InterpolationType gltf_to_godot_interpolation(Interpolation p_gltf_interpolation);
+
String get_original_name();
void set_original_name(String p_name);
bool get_loop() const;
void set_loop(bool p_val);
- HashMap<int, GLTFAnimation::Track> &get_tracks();
+
+ HashMap<int, GLTFAnimation::NodeTrack> &get_node_tracks();
+ HashMap<String, GLTFAnimation::Channel<Variant>> &get_pointer_tracks();
+ bool is_empty_of_tracks() const;
+
Variant get_additional_data(const StringName &p_extension_name);
void set_additional_data(const StringName &p_extension_name, Variant p_additional_data);
- GLTFAnimation();
-private:
- String original_name;
- bool loop = false;
- HashMap<int, Track> tracks;
- Dictionary additional_data;
+ GLTFAnimation();
};
#endif // GLTF_ANIMATION_H
diff --git a/modules/gltf/structures/gltf_camera.cpp b/modules/gltf/structures/gltf_camera.cpp
index 69d887322c..8e958dce5f 100644
--- a/modules/gltf/structures/gltf_camera.cpp
+++ b/modules/gltf/structures/gltf_camera.cpp
@@ -32,6 +32,7 @@
#include "gltf_camera.h"
+#include "gltf_object_model_property.h"
#include "scene/3d/camera_3d.h"
void GLTFCamera::_bind_methods() {
@@ -59,6 +60,21 @@ void GLTFCamera::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "depth_near"), "set_depth_near", "get_depth_near");
}
+void GLTFCamera::set_fov_conversion_expressions(Ref<GLTFObjectModelProperty> &r_obj_model_prop) {
+ // Expression to convert glTF yfov in radians to Godot fov in degrees.
+ Ref<Expression> gltf_to_godot_expr;
+ gltf_to_godot_expr.instantiate();
+ PackedStringArray gltf_to_godot_args = { "yfov_rad" };
+ gltf_to_godot_expr->parse("rad_to_deg(yfov_rad)", gltf_to_godot_args);
+ r_obj_model_prop->set_gltf_to_godot_expression(gltf_to_godot_expr);
+ // Expression to convert Godot fov in degrees to glTF yfov in radians.
+ Ref<Expression> godot_to_gltf_expr;
+ godot_to_gltf_expr.instantiate();
+ PackedStringArray godot_to_gltf_args = { "fov_deg" };
+ godot_to_gltf_expr->parse("deg_to_rad(fov_deg)", godot_to_gltf_args);
+ r_obj_model_prop->set_godot_to_gltf_expression(godot_to_gltf_expr);
+}
+
Ref<GLTFCamera> GLTFCamera::from_node(const Camera3D *p_camera) {
Ref<GLTFCamera> c;
c.instantiate();
diff --git a/modules/gltf/structures/gltf_camera.h b/modules/gltf/structures/gltf_camera.h
index d6de705192..5257d61520 100644
--- a/modules/gltf/structures/gltf_camera.h
+++ b/modules/gltf/structures/gltf_camera.h
@@ -36,6 +36,7 @@
#include "core/io/resource.h"
class Camera3D;
+class GLTFObjectModelProperty;
// Reference and test file:
// https://github.com/KhronosGroup/glTF-Tutorials/blob/master/gltfTutorial/gltfTutorial_015_SimpleCameras.md
@@ -56,6 +57,8 @@ protected:
static void _bind_methods();
public:
+ static void set_fov_conversion_expressions(Ref<GLTFObjectModelProperty> &r_obj_model_prop);
+
bool get_perspective() const { return perspective; }
void set_perspective(bool p_val) { perspective = p_val; }
real_t get_fov() const { return fov; }
diff --git a/modules/gltf/structures/gltf_node.cpp b/modules/gltf/structures/gltf_node.cpp
index 891228a541..fa074b1e9b 100644
--- a/modules/gltf/structures/gltf_node.cpp
+++ b/modules/gltf/structures/gltf_node.cpp
@@ -32,6 +32,8 @@
#include "gltf_node.h"
+#include "../gltf_state.h"
+
void GLTFNode::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_original_name"), &GLTFNode::get_original_name);
ClassDB::bind_method(D_METHOD("set_original_name", "original_name"), &GLTFNode::set_original_name);
@@ -62,6 +64,7 @@ void GLTFNode::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_light", "light"), &GLTFNode::set_light);
ClassDB::bind_method(D_METHOD("get_additional_data", "extension_name"), &GLTFNode::get_additional_data);
ClassDB::bind_method(D_METHOD("set_additional_data", "extension_name", "additional_data"), &GLTFNode::set_additional_data);
+ ClassDB::bind_method(D_METHOD("get_scene_node_path", "gltf_state", "handle_skeletons"), &GLTFNode::get_scene_node_path, DEFVAL(true));
ADD_PROPERTY(PropertyInfo(Variant::STRING, "original_name"), "set_original_name", "get_original_name"); // String
ADD_PROPERTY(PropertyInfo(Variant::INT, "parent"), "set_parent", "get_parent"); // GLTFNodeIndex
@@ -189,6 +192,48 @@ Variant GLTFNode::get_additional_data(const StringName &p_extension_name) {
return additional_data[p_extension_name];
}
+bool GLTFNode::has_additional_data(const StringName &p_extension_name) {
+ return additional_data.has(p_extension_name);
+}
+
void GLTFNode::set_additional_data(const StringName &p_extension_name, Variant p_additional_data) {
additional_data[p_extension_name] = p_additional_data;
}
+
+NodePath GLTFNode::get_scene_node_path(Ref<GLTFState> p_state, bool p_handle_skeletons) {
+ Vector<StringName> path;
+ Vector<StringName> subpath;
+ Ref<GLTFNode> current_gltf_node = this;
+ const int gltf_node_count = p_state->nodes.size();
+ if (p_handle_skeletons && skeleton != -1) {
+ // Special case for skeleton nodes, skip all bones so that the path is to the Skeleton3D node.
+ // A path that would otherwise be `A/B/C/Bone1/Bone2/Bone3` becomes `A/B/C/Skeleton3D:Bone3`.
+ subpath.append(get_name());
+ // The generated Skeleton3D node will be named Skeleton3D, so add it to the path.
+ path.append("Skeleton3D");
+ do {
+ const int parent_index = current_gltf_node->get_parent();
+ ERR_FAIL_INDEX_V(parent_index, gltf_node_count, NodePath());
+ current_gltf_node = p_state->nodes[parent_index];
+ } while (current_gltf_node->skeleton != -1);
+ }
+ const bool is_godot_single_root = p_state->extensions_used.has("GODOT_single_root");
+ while (true) {
+ const int parent_index = current_gltf_node->get_parent();
+ if (is_godot_single_root && parent_index == -1) {
+ // For GODOT_single_root scenes, the root glTF node becomes the Godot scene root, so it
+ // should not be included in the path. Ex: A/B/C, A is single root, we want B/C only.
+ break;
+ }
+ path.insert(0, current_gltf_node->get_name());
+ if (!is_godot_single_root && parent_index == -1) {
+ break;
+ }
+ ERR_FAIL_INDEX_V(parent_index, gltf_node_count, NodePath());
+ current_gltf_node = p_state->nodes[parent_index];
+ }
+ if (unlikely(path.is_empty())) {
+ path.append(".");
+ }
+ return NodePath(path, subpath, false);
+}
diff --git a/modules/gltf/structures/gltf_node.h b/modules/gltf/structures/gltf_node.h
index fc2d973b7d..9c52bce0d9 100644
--- a/modules/gltf/structures/gltf_node.h
+++ b/modules/gltf/structures/gltf_node.h
@@ -105,7 +105,10 @@ public:
void set_light(GLTFLightIndex p_light);
Variant get_additional_data(const StringName &p_extension_name);
+ bool has_additional_data(const StringName &p_extension_name);
void set_additional_data(const StringName &p_extension_name, Variant p_additional_data);
+
+ NodePath get_scene_node_path(Ref<GLTFState> p_state, bool p_handle_skeletons = true);
};
#endif // GLTF_NODE_H
diff --git a/modules/gltf/structures/gltf_object_model_property.cpp b/modules/gltf/structures/gltf_object_model_property.cpp
new file mode 100644
index 0000000000..4f01005c3b
--- /dev/null
+++ b/modules/gltf/structures/gltf_object_model_property.cpp
@@ -0,0 +1,175 @@
+/**************************************************************************/
+/* gltf_object_model_property.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* REDOT ENGINE */
+/* https://redotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2024-present Redot Engine contributors */
+/* (see REDOT_AUTHORS.md) */
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 "gltf_object_model_property.h"
+
+#include "../gltf_template_convert.h"
+
+void GLTFObjectModelProperty::_bind_methods() {
+ BIND_ENUM_CONSTANT(GLTF_OBJECT_MODEL_TYPE_UNKNOWN);
+ BIND_ENUM_CONSTANT(GLTF_OBJECT_MODEL_TYPE_BOOL);
+ BIND_ENUM_CONSTANT(GLTF_OBJECT_MODEL_TYPE_FLOAT);
+ BIND_ENUM_CONSTANT(GLTF_OBJECT_MODEL_TYPE_FLOAT_ARRAY);
+ BIND_ENUM_CONSTANT(GLTF_OBJECT_MODEL_TYPE_FLOAT2);
+ BIND_ENUM_CONSTANT(GLTF_OBJECT_MODEL_TYPE_FLOAT3);
+ BIND_ENUM_CONSTANT(GLTF_OBJECT_MODEL_TYPE_FLOAT4);
+ BIND_ENUM_CONSTANT(GLTF_OBJECT_MODEL_TYPE_FLOAT2X2);
+ BIND_ENUM_CONSTANT(GLTF_OBJECT_MODEL_TYPE_FLOAT3X3);
+ BIND_ENUM_CONSTANT(GLTF_OBJECT_MODEL_TYPE_FLOAT4X4);
+ BIND_ENUM_CONSTANT(GLTF_OBJECT_MODEL_TYPE_INT);
+
+ ClassDB::bind_method(D_METHOD("append_node_path", "node_path"), &GLTFObjectModelProperty::append_node_path);
+ ClassDB::bind_method(D_METHOD("append_path_to_property", "node_path", "prop_name"), &GLTFObjectModelProperty::append_path_to_property);
+
+ ClassDB::bind_method(D_METHOD("get_accessor_type"), &GLTFObjectModelProperty::get_accessor_type);
+ ClassDB::bind_method(D_METHOD("get_gltf_to_godot_expression"), &GLTFObjectModelProperty::get_gltf_to_godot_expression);
+ ClassDB::bind_method(D_METHOD("set_gltf_to_godot_expression", "gltf_to_godot_expr"), &GLTFObjectModelProperty::set_gltf_to_godot_expression);
+ ClassDB::bind_method(D_METHOD("get_godot_to_gltf_expression"), &GLTFObjectModelProperty::get_godot_to_gltf_expression);
+ ClassDB::bind_method(D_METHOD("set_godot_to_gltf_expression", "godot_to_gltf_expr"), &GLTFObjectModelProperty::set_godot_to_gltf_expression);
+ ClassDB::bind_method(D_METHOD("get_node_paths"), &GLTFObjectModelProperty::get_node_paths);
+ ClassDB::bind_method(D_METHOD("has_node_paths"), &GLTFObjectModelProperty::has_node_paths);
+ ClassDB::bind_method(D_METHOD("set_node_paths", "node_paths"), &GLTFObjectModelProperty::set_node_paths);
+ ClassDB::bind_method(D_METHOD("get_object_model_type"), &GLTFObjectModelProperty::get_object_model_type);
+ ClassDB::bind_method(D_METHOD("set_object_model_type", "type"), &GLTFObjectModelProperty::set_object_model_type);
+ ClassDB::bind_method(D_METHOD("get_json_pointers"), &GLTFObjectModelProperty::get_json_pointers_bind);
+ ClassDB::bind_method(D_METHOD("has_json_pointers"), &GLTFObjectModelProperty::has_json_pointers);
+ ClassDB::bind_method(D_METHOD("set_json_pointers", "json_pointers"), &GLTFObjectModelProperty::set_json_pointers_bind);
+ ClassDB::bind_method(D_METHOD("get_variant_type"), &GLTFObjectModelProperty::get_variant_type);
+ ClassDB::bind_method(D_METHOD("set_variant_type", "variant_type"), &GLTFObjectModelProperty::set_variant_type);
+ ClassDB::bind_method(D_METHOD("set_types", "variant_type", "obj_model_type"), &GLTFObjectModelProperty::set_types);
+
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "gltf_to_godot_expression", PROPERTY_HINT_RESOURCE_TYPE, "Expression"), "set_gltf_to_godot_expression", "get_gltf_to_godot_expression"); // Ref<Expression>
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "godot_to_gltf_expression", PROPERTY_HINT_RESOURCE_TYPE, "Expression"), "set_godot_to_gltf_expression", "get_godot_to_gltf_expression"); // Ref<Expression>
+ ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "node_paths", PROPERTY_HINT_TYPE_STRING, "NodePath"), "set_node_paths", "get_node_paths"); // TypedArray<NodePath>
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "object_model_type"), "set_object_model_type", "get_object_model_type"); // GLTFObjectModelType
+ ADD_PROPERTY(PropertyInfo(Variant::PACKED_STRING_ARRAY, "json_pointers"), "set_json_pointers", "get_json_pointers"); // TypedArray<PackedStringArray>
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "variant_type"), "set_variant_type", "get_variant_type"); // Variant::Type
+}
+
+void GLTFObjectModelProperty::append_node_path(const NodePath &p_node_path) {
+ node_paths.push_back(p_node_path);
+}
+
+void GLTFObjectModelProperty::append_path_to_property(const NodePath &p_node_path, const StringName &p_prop_name) {
+ Vector<StringName> node_names = p_node_path.get_names();
+ Vector<StringName> subpath = p_node_path.get_subnames();
+ subpath.append(p_prop_name);
+ node_paths.push_back(NodePath(node_names, subpath, false));
+}
+
+GLTFAccessor::GLTFAccessorType GLTFObjectModelProperty::get_accessor_type() const {
+ switch (object_model_type) {
+ case GLTF_OBJECT_MODEL_TYPE_FLOAT2:
+ return GLTFAccessor::TYPE_VEC2;
+ case GLTF_OBJECT_MODEL_TYPE_FLOAT3:
+ return GLTFAccessor::TYPE_VEC3;
+ case GLTF_OBJECT_MODEL_TYPE_FLOAT4:
+ return GLTFAccessor::TYPE_VEC4;
+ case GLTF_OBJECT_MODEL_TYPE_FLOAT2X2:
+ return GLTFAccessor::TYPE_MAT2;
+ case GLTF_OBJECT_MODEL_TYPE_FLOAT3X3:
+ return GLTFAccessor::TYPE_MAT3;
+ case GLTF_OBJECT_MODEL_TYPE_FLOAT4X4:
+ return GLTFAccessor::TYPE_MAT4;
+ default:
+ return GLTFAccessor::TYPE_SCALAR;
+ }
+}
+
+Ref<Expression> GLTFObjectModelProperty::get_gltf_to_godot_expression() const {
+ return gltf_to_godot_expr;
+}
+
+void GLTFObjectModelProperty::set_gltf_to_godot_expression(Ref<Expression> p_gltf_to_godot_expr) {
+ gltf_to_godot_expr = p_gltf_to_godot_expr;
+}
+
+Ref<Expression> GLTFObjectModelProperty::get_godot_to_gltf_expression() const {
+ return godot_to_gltf_expr;
+}
+
+void GLTFObjectModelProperty::set_godot_to_gltf_expression(Ref<Expression> p_godot_to_gltf_expr) {
+ godot_to_gltf_expr = p_godot_to_gltf_expr;
+}
+
+TypedArray<NodePath> GLTFObjectModelProperty::get_node_paths() const {
+ return node_paths;
+}
+
+bool GLTFObjectModelProperty::has_node_paths() const {
+ return !node_paths.is_empty();
+}
+
+void GLTFObjectModelProperty::set_node_paths(TypedArray<NodePath> p_node_paths) {
+ node_paths = p_node_paths;
+}
+
+GLTFObjectModelProperty::GLTFObjectModelType GLTFObjectModelProperty::get_object_model_type() const {
+ return object_model_type;
+}
+
+void GLTFObjectModelProperty::set_object_model_type(GLTFObjectModelType p_type) {
+ object_model_type = p_type;
+}
+
+Vector<PackedStringArray> GLTFObjectModelProperty::get_json_pointers() const {
+ return json_pointers;
+}
+
+bool GLTFObjectModelProperty::has_json_pointers() const {
+ return !json_pointers.is_empty();
+}
+
+void GLTFObjectModelProperty::set_json_pointers(const Vector<PackedStringArray> &p_json_pointers) {
+ json_pointers = p_json_pointers;
+}
+
+TypedArray<PackedStringArray> GLTFObjectModelProperty::get_json_pointers_bind() const {
+ return GLTFTemplateConvert::to_array(json_pointers);
+}
+
+void GLTFObjectModelProperty::set_json_pointers_bind(const TypedArray<PackedStringArray> &p_json_pointers) {
+ GLTFTemplateConvert::set_from_array(json_pointers, p_json_pointers);
+}
+
+Variant::Type GLTFObjectModelProperty::get_variant_type() const {
+ return variant_type;
+}
+
+void GLTFObjectModelProperty::set_variant_type(Variant::Type p_variant_type) {
+ variant_type = p_variant_type;
+}
+
+void GLTFObjectModelProperty::set_types(Variant::Type p_variant_type, GLTFObjectModelType p_obj_model_type) {
+ variant_type = p_variant_type;
+ object_model_type = p_obj_model_type;
+}
diff --git a/modules/gltf/structures/gltf_object_model_property.h b/modules/gltf/structures/gltf_object_model_property.h
new file mode 100644
index 0000000000..9fb11f49d2
--- /dev/null
+++ b/modules/gltf/structures/gltf_object_model_property.h
@@ -0,0 +1,106 @@
+/**************************************************************************/
+/* gltf_object_model_property.h */
+/**************************************************************************/
+/* This file is part of: */
+/* REDOT ENGINE */
+/* https://redotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2024-present Redot Engine contributors */
+/* (see REDOT_AUTHORS.md) */
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 GLTF_OBJECT_MODEL_PROPERTY_H
+#define GLTF_OBJECT_MODEL_PROPERTY_H
+
+#include "core/math/expression.h"
+#include "core/variant/typed_array.h"
+#include "gltf_accessor.h"
+
+// Object model: https://github.com/KhronosGroup/glTF/blob/main/specification/2.0/ObjectModel.adoc
+// KHR_animation_pointer: https://github.com/KhronosGroup/glTF/tree/main/extensions/2.0/Khronos/KHR_animation_pointer
+
+class GLTFObjectModelProperty : public RefCounted {
+ GDCLASS(GLTFObjectModelProperty, RefCounted);
+
+public:
+ enum GLTFObjectModelType {
+ GLTF_OBJECT_MODEL_TYPE_UNKNOWN,
+ GLTF_OBJECT_MODEL_TYPE_BOOL,
+ GLTF_OBJECT_MODEL_TYPE_FLOAT,
+ GLTF_OBJECT_MODEL_TYPE_FLOAT_ARRAY,
+ GLTF_OBJECT_MODEL_TYPE_FLOAT2,
+ GLTF_OBJECT_MODEL_TYPE_FLOAT3,
+ GLTF_OBJECT_MODEL_TYPE_FLOAT4,
+ GLTF_OBJECT_MODEL_TYPE_FLOAT2X2,
+ GLTF_OBJECT_MODEL_TYPE_FLOAT3X3,
+ GLTF_OBJECT_MODEL_TYPE_FLOAT4X4,
+ GLTF_OBJECT_MODEL_TYPE_INT,
+ };
+
+private:
+ Ref<Expression> gltf_to_godot_expr;
+ Ref<Expression> godot_to_gltf_expr;
+ TypedArray<NodePath> node_paths;
+ GLTFObjectModelType object_model_type = GLTF_OBJECT_MODEL_TYPE_UNKNOWN;
+ Vector<PackedStringArray> json_pointers;
+ Variant::Type variant_type = Variant::NIL;
+
+protected:
+ static void _bind_methods();
+
+public:
+ void append_node_path(const NodePath &p_node_path);
+ void append_path_to_property(const NodePath &p_node_path, const StringName &p_prop_name);
+
+ GLTFAccessor::GLTFAccessorType get_accessor_type() const;
+
+ Ref<Expression> get_gltf_to_godot_expression() const;
+ void set_gltf_to_godot_expression(Ref<Expression> p_gltf_to_godot_expr);
+
+ Ref<Expression> get_godot_to_gltf_expression() const;
+ void set_godot_to_gltf_expression(Ref<Expression> p_godot_to_gltf_expr);
+
+ TypedArray<NodePath> get_node_paths() const;
+ bool has_node_paths() const;
+ void set_node_paths(TypedArray<NodePath> p_node_paths);
+
+ GLTFObjectModelType get_object_model_type() const;
+ void set_object_model_type(GLTFObjectModelType p_type);
+
+ Vector<PackedStringArray> get_json_pointers() const;
+ bool has_json_pointers() const;
+ void set_json_pointers(const Vector<PackedStringArray> &p_json_pointers);
+
+ TypedArray<PackedStringArray> get_json_pointers_bind() const;
+ void set_json_pointers_bind(const TypedArray<PackedStringArray> &p_json_pointers);
+
+ Variant::Type get_variant_type() const;
+ void set_variant_type(Variant::Type p_variant_type);
+
+ void set_types(Variant::Type p_variant_type, GLTFObjectModelType p_obj_model_type);
+};
+
+VARIANT_ENUM_CAST(GLTFObjectModelProperty::GLTFObjectModelType);
+
+#endif // GLTF_OBJECT_MODEL_PROPERTY_H
diff --git a/modules/godot_physics_2d/godot_joints_2d.cpp b/modules/godot_physics_2d/godot_joints_2d.cpp
index 7743f9087a..ff90bc16a1 100644
--- a/modules/godot_physics_2d/godot_joints_2d.cpp
+++ b/modules/godot_physics_2d/godot_joints_2d.cpp
@@ -313,7 +313,7 @@ bool GodotPinJoint2D::get_flag(PhysicsServer2D::PinJointFlag p_flag) const {
return motor_enabled;
}
}
- ERR_FAIL_V(0);
+ ERR_FAIL_V(false);
}
GodotPinJoint2D::GodotPinJoint2D(const Vector2 &p_pos, GodotBody2D *p_body_a, GodotBody2D *p_body_b) :
diff --git a/modules/godot_physics_2d/godot_physics_server_2d.cpp b/modules/godot_physics_2d/godot_physics_server_2d.cpp
index 286a93a986..2d61b6b9cd 100644
--- a/modules/godot_physics_2d/godot_physics_server_2d.cpp
+++ b/modules/godot_physics_2d/godot_physics_server_2d.cpp
@@ -1171,8 +1171,8 @@ void GodotPhysicsServer2D::pin_joint_set_flag(RID p_joint, PinJointFlag p_flag,
bool GodotPhysicsServer2D::pin_joint_get_flag(RID p_joint, PinJointFlag p_flag) const {
GodotJoint2D *joint = joint_owner.get_or_null(p_joint);
- ERR_FAIL_NULL_V(joint, 0);
- ERR_FAIL_COND_V(joint->get_type() != JOINT_TYPE_PIN, 0);
+ ERR_FAIL_NULL_V(joint, false);
+ ERR_FAIL_COND_V(joint->get_type() != JOINT_TYPE_PIN, false);
GodotPinJoint2D *pin_joint = static_cast<GodotPinJoint2D *>(joint);
return pin_joint->get_flag(p_flag);
diff --git a/modules/godot_physics_2d/godot_space_2d.cpp b/modules/godot_physics_2d/godot_space_2d.cpp
index 11ef1c03a3..d98c1e8ad1 100644
--- a/modules/godot_physics_2d/godot_space_2d.cpp
+++ b/modules/godot_physics_2d/godot_space_2d.cpp
@@ -344,7 +344,7 @@ bool GodotPhysicsDirectSpaceState2D::collide_shape(const ShapeParameters &p_para
}
GodotShape2D *shape = GodotPhysicsServer2D::godot_singleton->shape_owner.get_or_null(p_parameters.shape_rid);
- ERR_FAIL_NULL_V(shape, 0);
+ ERR_FAIL_NULL_V(shape, false);
Rect2 aabb = p_parameters.transform.xform(shape->get_aabb());
aabb = aabb.merge(Rect2(aabb.position + p_parameters.motion, aabb.size)); //motion
@@ -441,7 +441,7 @@ static void _rest_cbk_result(const Vector2 &p_point_A, const Vector2 &p_point_B,
bool GodotPhysicsDirectSpaceState2D::rest_info(const ShapeParameters &p_parameters, ShapeRestInfo *r_info) {
GodotShape2D *shape = GodotPhysicsServer2D::godot_singleton->shape_owner.get_or_null(p_parameters.shape_rid);
- ERR_FAIL_NULL_V(shape, 0);
+ ERR_FAIL_NULL_V(shape, false);
real_t margin = MAX(p_parameters.margin, TEST_MOTION_MARGIN_MIN_VALUE);
diff --git a/modules/godot_physics_3d/godot_collision_solver_3d_sat.cpp b/modules/godot_physics_3d/godot_collision_solver_3d_sat.cpp
index afa50d6a13..58843f1ad6 100644
--- a/modules/godot_physics_3d/godot_collision_solver_3d_sat.cpp
+++ b/modules/godot_physics_3d/godot_collision_solver_3d_sat.cpp
@@ -78,8 +78,9 @@ struct _CollectorCallback {
Vector3 *prev_axis = nullptr;
_FORCE_INLINE_ void call(const Vector3 &p_point_A, const Vector3 &p_point_B, Vector3 p_normal) {
- if (p_normal.dot(p_point_B - p_point_A) < 0)
+ if (p_normal.dot(p_point_B - p_point_A) < 0) {
p_normal = -p_normal;
+ }
if (swap) {
callback(p_point_B, 0, p_point_A, 0, -p_normal, userdata);
} else {
@@ -177,10 +178,11 @@ static void _generate_contacts_edge_edge(const Vector3 *p_points_A, int p_point_
// The normal should be perpendicular to both edges.
Vector3 normal = rel_A.cross(rel_B);
real_t normal_len = normal.length();
- if (normal_len > 1e-3)
+ if (normal_len > 1e-3) {
normal /= normal_len;
- else
+ } else {
normal = p_callback->normal;
+ }
p_callback->call(closest_A, closest_B, normal);
}
@@ -786,8 +788,9 @@ static void analytic_sphere_collision(const Vector3 &p_origin_a, real_t p_radius
// Calculate the sphere overlap, and bail if not overlapping
real_t overlap = p_radius_a + p_radius_b - b_to_a_len;
- if (overlap < 0)
+ if (overlap < 0) {
return;
+ }
// Report collision
p_collector->collided = true;
diff --git a/modules/godot_physics_3d/godot_physics_server_3d.cpp b/modules/godot_physics_3d/godot_physics_server_3d.cpp
index df58cd7db6..897e509db1 100644
--- a/modules/godot_physics_3d/godot_physics_server_3d.cpp
+++ b/modules/godot_physics_3d/godot_physics_server_3d.cpp
@@ -828,7 +828,7 @@ void GodotPhysicsServer3D::body_set_axis_lock(RID p_body, BodyAxis p_axis, bool
bool GodotPhysicsServer3D::body_is_axis_locked(RID p_body, BodyAxis p_axis) const {
const GodotBody3D *body = body_owner.get_or_null(p_body);
- ERR_FAIL_NULL_V(body, 0);
+ ERR_FAIL_NULL_V(body, false);
return body->is_axis_locked(p_axis);
}
diff --git a/modules/godot_physics_3d/godot_shape_3d.cpp b/modules/godot_physics_3d/godot_shape_3d.cpp
index 06f4f31cc2..e42df1aaca 100644
--- a/modules/godot_physics_3d/godot_shape_3d.cpp
+++ b/modules/godot_physics_3d/godot_shape_3d.cpp
@@ -1135,8 +1135,9 @@ void GodotConvexPolygonShape3D::_setup(const Vector<Vector3> &p_vertices) {
max_support = s;
}
}
- if (!extreme_vertices.has(best_vertex))
+ if (!extreme_vertices.has(best_vertex)) {
extreme_vertices.push_back(best_vertex);
+ }
}
}
}
diff --git a/modules/godot_physics_3d/godot_space_3d.cpp b/modules/godot_physics_3d/godot_space_3d.cpp
index e896141ba0..41c04568fe 100644
--- a/modules/godot_physics_3d/godot_space_3d.cpp
+++ b/modules/godot_physics_3d/godot_space_3d.cpp
@@ -387,7 +387,7 @@ bool GodotPhysicsDirectSpaceState3D::collide_shape(const ShapeParameters &p_para
}
GodotShape3D *shape = GodotPhysicsServer3D::godot_singleton->shape_owner.get_or_null(p_parameters.shape_rid);
- ERR_FAIL_NULL_V(shape, 0);
+ ERR_FAIL_NULL_V(shape, false);
AABB aabb = p_parameters.transform.xform(shape->get_aabb());
aabb = aabb.grow(p_parameters.margin);
@@ -513,7 +513,7 @@ static void _rest_cbk_result(const Vector3 &p_point_A, int p_index_A, const Vect
bool GodotPhysicsDirectSpaceState3D::rest_info(const ShapeParameters &p_parameters, ShapeRestInfo *r_info) {
GodotShape3D *shape = GodotPhysicsServer3D::godot_singleton->shape_owner.get_or_null(p_parameters.shape_rid);
- ERR_FAIL_NULL_V(shape, 0);
+ ERR_FAIL_NULL_V(shape, false);
real_t margin = MAX(p_parameters.margin, TEST_MOTION_MARGIN_MIN_VALUE);
diff --git a/modules/godot_physics_3d/joints/godot_generic_6dof_joint_3d.cpp b/modules/godot_physics_3d/joints/godot_generic_6dof_joint_3d.cpp
index 0cca720f56..943d657b93 100644
--- a/modules/godot_physics_3d/joints/godot_generic_6dof_joint_3d.cpp
+++ b/modules/godot_physics_3d/joints/godot_generic_6dof_joint_3d.cpp
@@ -649,7 +649,7 @@ void GodotGeneric6DOFJoint3D::set_flag(Vector3::Axis p_axis, PhysicsServer3D::G6
}
bool GodotGeneric6DOFJoint3D::get_flag(Vector3::Axis p_axis, PhysicsServer3D::G6DOFJointAxisFlag p_flag) const {
- ERR_FAIL_INDEX_V(p_axis, 3, 0);
+ ERR_FAIL_INDEX_V(p_axis, 3, false);
switch (p_flag) {
case PhysicsServer3D::G6DOF_JOINT_FLAG_ENABLE_LINEAR_LIMIT: {
return m_linearLimits.enable_limit[p_axis];
diff --git a/modules/meshoptimizer/register_types.cpp b/modules/meshoptimizer/register_types.cpp
index 57d1ea8a35..f33e7d8d90 100644
--- a/modules/meshoptimizer/register_types.cpp
+++ b/modules/meshoptimizer/register_types.cpp
@@ -42,10 +42,10 @@ void initialize_meshoptimizer_module(ModuleInitializationLevel p_level) {
}
SurfaceTool::optimize_vertex_cache_func = meshopt_optimizeVertexCache;
+ SurfaceTool::optimize_vertex_fetch_remap_func = meshopt_optimizeVertexFetchRemap;
SurfaceTool::simplify_func = meshopt_simplify;
SurfaceTool::simplify_with_attrib_func = meshopt_simplifyWithAttributes;
SurfaceTool::simplify_scale_func = meshopt_simplifyScale;
- SurfaceTool::simplify_sloppy_func = meshopt_simplifySloppy;
SurfaceTool::generate_remap_func = meshopt_generateVertexRemap;
SurfaceTool::remap_vertex_func = meshopt_remapVertexBuffer;
SurfaceTool::remap_index_func = meshopt_remapIndexBuffer;
@@ -57,9 +57,9 @@ void uninitialize_meshoptimizer_module(ModuleInitializationLevel p_level) {
}
SurfaceTool::optimize_vertex_cache_func = nullptr;
+ SurfaceTool::optimize_vertex_fetch_remap_func = nullptr;
SurfaceTool::simplify_func = nullptr;
SurfaceTool::simplify_scale_func = nullptr;
- SurfaceTool::simplify_sloppy_func = nullptr;
SurfaceTool::generate_remap_func = nullptr;
SurfaceTool::remap_vertex_func = nullptr;
SurfaceTool::remap_index_func = nullptr;
diff --git a/modules/navigation/nav_agent.h b/modules/navigation/nav_agent.h
index ff5ddf03f8..ed233de58c 100644
--- a/modules/navigation/nav_agent.h
+++ b/modules/navigation/nav_agent.h
@@ -69,7 +69,7 @@ class NavAgent : public NavRid {
uint32_t avoidance_mask = 1;
real_t avoidance_priority = 1.0;
- Callable avoidance_callback = Callable();
+ Callable avoidance_callback;
bool agent_dirty = true;
diff --git a/modules/navigation/nav_map.cpp b/modules/navigation/nav_map.cpp
index 6ca7b5fe15..e4c41925a9 100644
--- a/modules/navigation/nav_map.cpp
+++ b/modules/navigation/nav_map.cpp
@@ -428,26 +428,42 @@ void NavMap::sync() {
_new_pm_polygon_count = polygon_count;
+ struct ConnectionPair {
+ gd::Edge::Connection connections[2];
+ int size = 0;
+ };
+
// Group all edges per key.
- HashMap<gd::EdgeKey, Vector<gd::Edge::Connection>, gd::EdgeKey> connections;
+ HashMap<gd::EdgeKey, ConnectionPair, gd::EdgeKey> connection_pairs_map;
+ connection_pairs_map.reserve(polygons.size());
+ int free_edges_count = 0; // How many ConnectionPairs have only one Connection.
+
for (gd::Polygon &poly : polygons) {
for (uint32_t p = 0; p < poly.points.size(); p++) {
- int next_point = (p + 1) % poly.points.size();
- gd::EdgeKey ek(poly.points[p].key, poly.points[next_point].key);
+ const int next_point = (p + 1) % poly.points.size();
+ const gd::EdgeKey ek(poly.points[p].key, poly.points[next_point].key);
- HashMap<gd::EdgeKey, Vector<gd::Edge::Connection>, gd::EdgeKey>::Iterator connection = connections.find(ek);
- if (!connection) {
- connections[ek] = Vector<gd::Edge::Connection>();
+ HashMap<gd::EdgeKey, ConnectionPair, gd::EdgeKey>::Iterator pair_it = connection_pairs_map.find(ek);
+ if (!pair_it) {
+ pair_it = connection_pairs_map.insert(ek, ConnectionPair());
_new_pm_edge_count += 1;
+ ++free_edges_count;
}
- if (connections[ek].size() <= 1) {
+ ConnectionPair &pair = pair_it->value;
+ if (pair.size < 2) {
// Add the polygon/edge tuple to this key.
gd::Edge::Connection new_connection;
new_connection.polygon = &poly;
new_connection.edge = p;
new_connection.pathway_start = poly.points[p].pos;
new_connection.pathway_end = poly.points[next_point].pos;
- connections[ek].push_back(new_connection);
+
+ pair.connections[pair.size] = new_connection;
+ ++pair.size;
+ if (pair.size == 2) {
+ --free_edges_count;
+ }
+
} else {
// The edge is already connected with another edge, skip.
ERR_PRINT_ONCE("Navigation map synchronization error. Attempted to merge a navigation mesh polygon edge with another already-merged edge. This is usually caused by crossing edges, overlapping polygons, or a mismatch of the NavigationMesh / NavigationPolygon baked 'cell_size' and navigation map 'cell_size'. If you're certain none of above is the case, change 'navigation/3d/merge_rasterizer_cell_scale' to 0.001.");
@@ -455,20 +471,23 @@ void NavMap::sync() {
}
}
- Vector<gd::Edge::Connection> free_edges;
- for (KeyValue<gd::EdgeKey, Vector<gd::Edge::Connection>> &E : connections) {
- if (E.value.size() == 2) {
+ LocalVector<gd::Edge::Connection> free_edges;
+ free_edges.reserve(free_edges_count);
+
+ for (const KeyValue<gd::EdgeKey, ConnectionPair> &pair_it : connection_pairs_map) {
+ const ConnectionPair &pair = pair_it.value;
+ if (pair.size == 2) {
// Connect edge that are shared in different polygons.
- gd::Edge::Connection &c1 = E.value.write[0];
- gd::Edge::Connection &c2 = E.value.write[1];
+ const gd::Edge::Connection &c1 = pair.connections[0];
+ const gd::Edge::Connection &c2 = pair.connections[1];
c1.polygon->edges[c1.edge].connections.push_back(c2);
c2.polygon->edges[c2.edge].connections.push_back(c1);
// Note: The pathway_start/end are full for those connection and do not need to be modified.
_new_pm_edge_merge_count += 1;
} else {
- CRASH_COND_MSG(E.value.size() != 1, vformat("Number of connection != 1. Found: %d", E.value.size()));
- if (use_edge_connections && E.value[0].polygon->owner->get_use_edge_connections()) {
- free_edges.push_back(E.value[0]);
+ CRASH_COND_MSG(pair.size != 1, vformat("Number of connection != 1. Found: %d", pair.size));
+ if (use_edge_connections && pair.connections[0].polygon->owner->get_use_edge_connections()) {
+ free_edges.push_back(pair.connections[0]);
}
}
}
@@ -482,14 +501,14 @@ void NavMap::sync() {
// connection, integration and path finding.
_new_pm_edge_free_count = free_edges.size();
- real_t sqr_edge_connection_margin = edge_connection_margin * edge_connection_margin;
+ const real_t edge_connection_margin_squared = edge_connection_margin * edge_connection_margin;
- for (int i = 0; i < free_edges.size(); i++) {
+ for (uint32_t i = 0; i < free_edges.size(); i++) {
const gd::Edge::Connection &free_edge = free_edges[i];
Vector3 edge_p1 = free_edge.polygon->points[free_edge.edge].pos;
Vector3 edge_p2 = free_edge.polygon->points[(free_edge.edge + 1) % free_edge.polygon->points.size()].pos;
- for (int j = 0; j < free_edges.size(); j++) {
+ for (uint32_t j = 0; j < free_edges.size(); j++) {
const gd::Edge::Connection &other_edge = free_edges[j];
if (i == j || free_edge.polygon->owner == other_edge.polygon->owner) {
continue;
@@ -514,7 +533,7 @@ void NavMap::sync() {
} else {
other1 = other_edge_p1.lerp(other_edge_p2, (1.0 - projected_p1_ratio) / (projected_p2_ratio - projected_p1_ratio));
}
- if (other1.distance_squared_to(self1) > sqr_edge_connection_margin) {
+ if (other1.distance_squared_to(self1) > edge_connection_margin_squared) {
continue;
}
@@ -525,7 +544,7 @@ void NavMap::sync() {
} else {
other2 = other_edge_p1.lerp(other_edge_p2, (0.0 - projected_p1_ratio) / (projected_p2_ratio - projected_p1_ratio));
}
- if (other2.distance_squared_to(self2) > sqr_edge_connection_margin) {
+ if (other2.distance_squared_to(self2) > edge_connection_margin_squared) {
continue;
}
diff --git a/modules/openxr/doc_classes/OpenXRExtensionWrapperExtension.xml b/modules/openxr/doc_classes/OpenXRExtensionWrapperExtension.xml
index 813c9d582e..182fe32f9c 100644
--- a/modules/openxr/doc_classes/OpenXRExtensionWrapperExtension.xml
+++ b/modules/openxr/doc_classes/OpenXRExtensionWrapperExtension.xml
@@ -90,6 +90,21 @@
Called right after the main swapchains are (re)created.
</description>
</method>
+ <method name="_on_post_draw_viewport" qualifiers="virtual">
+ <return type="void" />
+ <param index="0" name="viewport" type="RID" />
+ <description>
+ Called right after the given viewport is rendered.
+ [b]Note:[/b] The draw commands might only be queued at this point, not executed.
+ </description>
+ </method>
+ <method name="_on_pre_draw_viewport" qualifiers="virtual">
+ <return type="void" />
+ <param index="0" name="viewport" type="RID" />
+ <description>
+ Called right before the given viewport is rendered.
+ </description>
+ </method>
<method name="_on_pre_render" qualifiers="virtual">
<return type="void" />
<description>
diff --git a/modules/openxr/extensions/openxr_composition_layer_extension.cpp b/modules/openxr/extensions/openxr_composition_layer_extension.cpp
index 6811dae2da..2c1ecaeba5 100644
--- a/modules/openxr/extensions/openxr_composition_layer_extension.cpp
+++ b/modules/openxr/extensions/openxr_composition_layer_extension.cpp
@@ -343,7 +343,7 @@ XrCompositionLayerBaseHeader *OpenXRViewportCompositionLayerProvider::get_compos
}
XrSwapchainSubImage subimage = {
- 0, // swapchain
+ 0, // swapchain // NOLINT(modernize-use-nullptr) - 32-bit uses non-pointer uint64
{ { 0, 0 }, { 0, 0 } }, // imageRect
0, // imageArrayIndex
};
diff --git a/modules/openxr/extensions/openxr_debug_utils_extension.cpp b/modules/openxr/extensions/openxr_debug_utils_extension.cpp
index afdc7f294f..8140c9f0fa 100644
--- a/modules/openxr/extensions/openxr_debug_utils_extension.cpp
+++ b/modules/openxr/extensions/openxr_debug_utils_extension.cpp
@@ -175,7 +175,7 @@ void OpenXRDebugUtilsExtension::begin_debug_label_region(const char *p_label_nam
const XrDebugUtilsLabelEXT session_active_region_label = {
XR_TYPE_DEBUG_UTILS_LABEL_EXT, // type
- NULL, // next
+ nullptr, // next
p_label_name, // labelName
};
@@ -201,7 +201,7 @@ void OpenXRDebugUtilsExtension::insert_debug_label(const char *p_label_name) {
const XrDebugUtilsLabelEXT session_active_region_label = {
XR_TYPE_DEBUG_UTILS_LABEL_EXT, // type
- NULL, // next
+ nullptr, // next
p_label_name, // labelName
};
diff --git a/modules/openxr/extensions/openxr_extension_wrapper_extension.cpp b/modules/openxr/extensions/openxr_extension_wrapper_extension.cpp
index ae496726e8..fc822056d5 100644
--- a/modules/openxr/extensions/openxr_extension_wrapper_extension.cpp
+++ b/modules/openxr/extensions/openxr_extension_wrapper_extension.cpp
@@ -53,6 +53,8 @@ void OpenXRExtensionWrapperExtension::_bind_methods() {
GDVIRTUAL_BIND(_on_process);
GDVIRTUAL_BIND(_on_pre_render);
GDVIRTUAL_BIND(_on_main_swapchains_created);
+ GDVIRTUAL_BIND(_on_pre_draw_viewport, "viewport");
+ GDVIRTUAL_BIND(_on_post_draw_viewport, "viewport");
GDVIRTUAL_BIND(_on_session_destroyed);
GDVIRTUAL_BIND(_on_state_idle);
GDVIRTUAL_BIND(_on_state_ready);
@@ -210,6 +212,14 @@ void OpenXRExtensionWrapperExtension::on_session_destroyed() {
GDVIRTUAL_CALL(_on_session_destroyed);
}
+void OpenXRExtensionWrapperExtension::on_pre_draw_viewport(RID p_render_target) {
+ GDVIRTUAL_CALL(_on_pre_draw_viewport, p_render_target);
+}
+
+void OpenXRExtensionWrapperExtension::on_post_draw_viewport(RID p_render_target) {
+ GDVIRTUAL_CALL(_on_post_draw_viewport, p_render_target);
+}
+
void OpenXRExtensionWrapperExtension::on_state_idle() {
GDVIRTUAL_CALL(_on_state_idle);
}
@@ -300,8 +310,7 @@ void OpenXRExtensionWrapperExtension::register_extension_wrapper() {
OpenXRAPI::register_extension_wrapper(this);
}
-OpenXRExtensionWrapperExtension::OpenXRExtensionWrapperExtension() :
- Object(), OpenXRExtensionWrapper() {
+OpenXRExtensionWrapperExtension::OpenXRExtensionWrapperExtension() {
openxr_api.instantiate();
}
diff --git a/modules/openxr/extensions/openxr_extension_wrapper_extension.h b/modules/openxr/extensions/openxr_extension_wrapper_extension.h
index 04c78c5178..17a8c7f64c 100644
--- a/modules/openxr/extensions/openxr_extension_wrapper_extension.h
+++ b/modules/openxr/extensions/openxr_extension_wrapper_extension.h
@@ -90,6 +90,8 @@ public:
virtual void on_pre_render() override;
virtual void on_main_swapchains_created() override;
virtual void on_session_destroyed() override;
+ virtual void on_pre_draw_viewport(RID p_render_target) override;
+ virtual void on_post_draw_viewport(RID p_render_target) override;
GDVIRTUAL0(_on_register_metadata);
GDVIRTUAL0(_on_before_instance_created);
@@ -100,6 +102,8 @@ public:
GDVIRTUAL0(_on_pre_render);
GDVIRTUAL0(_on_main_swapchains_created);
GDVIRTUAL0(_on_session_destroyed);
+ GDVIRTUAL1(_on_pre_draw_viewport, RID);
+ GDVIRTUAL1(_on_post_draw_viewport, RID);
virtual void on_state_idle() override;
virtual void on_state_ready() override;
diff --git a/modules/openxr/extensions/platform/openxr_opengl_extension.cpp b/modules/openxr/extensions/platform/openxr_opengl_extension.cpp
index 62be546790..58c0f6f290 100644
--- a/modules/openxr/extensions/platform/openxr_opengl_extension.cpp
+++ b/modules/openxr/extensions/platform/openxr_opengl_extension.cpp
@@ -195,7 +195,7 @@ void *OpenXROpenGLExtension::set_session_create_and_get_next_pointer(void *p_nex
// spec says to use proper values but runtimes don't care
graphics_binding_gl.visualid = 0;
- graphics_binding_gl.glxFBConfig = 0;
+ graphics_binding_gl.glxFBConfig = nullptr;
#endif
#endif
diff --git a/modules/openxr/openxr_api.cpp b/modules/openxr/openxr_api.cpp
index def3c87f0c..6ce62e20d1 100644
--- a/modules/openxr/openxr_api.cpp
+++ b/modules/openxr/openxr_api.cpp
@@ -2754,8 +2754,9 @@ void OpenXRAPI::parse_velocities(const XrSpaceVelocity &p_velocity, Vector3 &r_l
}
bool OpenXRAPI::xr_result(XrResult result, const char *format, Array args) const {
- if (XR_SUCCEEDED(result))
+ if (XR_SUCCEEDED(result)) {
return true;
+ }
char resultString[XR_MAX_RESULT_STRING_SIZE];
xrResultToString(instance, result, resultString);
diff --git a/modules/text_server_adv/text_server_adv.cpp b/modules/text_server_adv/text_server_adv.cpp
index 3d433bfd69..38da170a4f 100644
--- a/modules/text_server_adv/text_server_adv.cpp
+++ b/modules/text_server_adv/text_server_adv.cpp
@@ -5266,42 +5266,44 @@ void TextServerAdvanced::_shaped_text_overrun_trim_to_width(const RID &p_shaped_
// Find usable fonts, if fonts from the last glyph do not have required chars.
RID dot_gl_font_rid = sd_glyphs[sd_size - 1].font_rid;
- if (!_font_has_char(dot_gl_font_rid, sd->el_char)) {
- const Array &fonts = spans[spans.size() - 1].fonts;
- for (int i = 0; i < fonts.size(); i++) {
- if (_font_has_char(fonts[i], sd->el_char)) {
- dot_gl_font_rid = fonts[i];
- found_el_char = true;
- break;
- }
- }
- if (!found_el_char && OS::get_singleton()->has_feature("system_fonts") && fonts.size() > 0 && _font_is_allow_system_fallback(fonts[0])) {
- const char32_t u32str[] = { sd->el_char, 0 };
- RID rid = _find_sys_font_for_text(fonts[0], String(), spans[spans.size() - 1].language, u32str);
- if (rid.is_valid()) {
- dot_gl_font_rid = rid;
- found_el_char = true;
- }
- }
- } else {
- found_el_char = true;
- }
- if (!found_el_char) {
- bool found_dot_char = false;
- dot_gl_font_rid = sd_glyphs[sd_size - 1].font_rid;
- if (!_font_has_char(dot_gl_font_rid, '.')) {
+ if (add_ellipsis || enforce_ellipsis) {
+ if (!_font_has_char(dot_gl_font_rid, sd->el_char)) {
const Array &fonts = spans[spans.size() - 1].fonts;
for (int i = 0; i < fonts.size(); i++) {
- if (_font_has_char(fonts[i], '.')) {
+ if (_font_has_char(fonts[i], sd->el_char)) {
dot_gl_font_rid = fonts[i];
- found_dot_char = true;
+ found_el_char = true;
break;
}
}
- if (!found_dot_char && OS::get_singleton()->has_feature("system_fonts") && fonts.size() > 0 && _font_is_allow_system_fallback(fonts[0])) {
- RID rid = _find_sys_font_for_text(fonts[0], String(), spans[spans.size() - 1].language, ".");
+ if (!found_el_char && OS::get_singleton()->has_feature("system_fonts") && fonts.size() > 0 && _font_is_allow_system_fallback(fonts[0])) {
+ const char32_t u32str[] = { sd->el_char, 0 };
+ RID rid = _find_sys_font_for_text(fonts[0], String(), spans[spans.size() - 1].language, u32str);
if (rid.is_valid()) {
dot_gl_font_rid = rid;
+ found_el_char = true;
+ }
+ }
+ } else {
+ found_el_char = true;
+ }
+ if (!found_el_char) {
+ bool found_dot_char = false;
+ dot_gl_font_rid = sd_glyphs[sd_size - 1].font_rid;
+ if (!_font_has_char(dot_gl_font_rid, '.')) {
+ const Array &fonts = spans[spans.size() - 1].fonts;
+ for (int i = 0; i < fonts.size(); i++) {
+ if (_font_has_char(fonts[i], '.')) {
+ dot_gl_font_rid = fonts[i];
+ found_dot_char = true;
+ break;
+ }
+ }
+ if (!found_dot_char && OS::get_singleton()->has_feature("system_fonts") && fonts.size() > 0 && _font_is_allow_system_fallback(fonts[0])) {
+ RID rid = _find_sys_font_for_text(fonts[0], String(), spans[spans.size() - 1].language, ".");
+ if (rid.is_valid()) {
+ dot_gl_font_rid = rid;
+ }
}
}
}
@@ -5317,8 +5319,8 @@ void TextServerAdvanced::_shaped_text_overrun_trim_to_width(const RID &p_shaped_
}
}
- int32_t dot_gl_idx = dot_gl_font_rid.is_valid() ? _font_get_glyph_index(dot_gl_font_rid, last_gl_font_size, (found_el_char ? sd->el_char : '.'), 0) : -1;
- Vector2 dot_adv = dot_gl_font_rid.is_valid() ? _font_get_glyph_advance(dot_gl_font_rid, last_gl_font_size, dot_gl_idx) : Vector2();
+ int32_t dot_gl_idx = ((add_ellipsis || enforce_ellipsis) && dot_gl_font_rid.is_valid()) ? _font_get_glyph_index(dot_gl_font_rid, last_gl_font_size, (found_el_char ? sd->el_char : '.'), 0) : -1;
+ Vector2 dot_adv = ((add_ellipsis || enforce_ellipsis) && dot_gl_font_rid.is_valid()) ? _font_get_glyph_advance(dot_gl_font_rid, last_gl_font_size, dot_gl_idx) : Vector2();
int32_t whitespace_gl_idx = whitespace_gl_font_rid.is_valid() ? _font_get_glyph_index(whitespace_gl_font_rid, last_gl_font_size, ' ', 0) : -1;
Vector2 whitespace_adv = whitespace_gl_font_rid.is_valid() ? _font_get_glyph_advance(whitespace_gl_font_rid, last_gl_font_size, whitespace_gl_idx) : Vector2();
diff --git a/modules/text_server_fb/text_server_fb.cpp b/modules/text_server_fb/text_server_fb.cpp
index 8919e320d0..47884f7df3 100644
--- a/modules/text_server_fb/text_server_fb.cpp
+++ b/modules/text_server_fb/text_server_fb.cpp
@@ -4079,42 +4079,44 @@ void TextServerFallback::_shaped_text_overrun_trim_to_width(const RID &p_shaped_
// Find usable fonts, if fonts from the last glyph do not have required chars.
RID dot_gl_font_rid = sd_glyphs[sd_size - 1].font_rid;
- if (!_font_has_char(dot_gl_font_rid, sd->el_char)) {
- const Array &fonts = spans[spans.size() - 1].fonts;
- for (int i = 0; i < fonts.size(); i++) {
- if (_font_has_char(fonts[i], sd->el_char)) {
- dot_gl_font_rid = fonts[i];
- found_el_char = true;
- break;
- }
- }
- if (!found_el_char && OS::get_singleton()->has_feature("system_fonts") && fonts.size() > 0 && _font_is_allow_system_fallback(fonts[0])) {
- const char32_t u32str[] = { sd->el_char, 0 };
- RID rid = _find_sys_font_for_text(fonts[0], String(), spans[spans.size() - 1].language, u32str);
- if (rid.is_valid()) {
- dot_gl_font_rid = rid;
- found_el_char = true;
- }
- }
- } else {
- found_el_char = true;
- }
- if (!found_el_char) {
- bool found_dot_char = false;
- dot_gl_font_rid = sd_glyphs[sd_size - 1].font_rid;
- if (!_font_has_char(dot_gl_font_rid, '.')) {
+ if (add_ellipsis || enforce_ellipsis) {
+ if (!_font_has_char(dot_gl_font_rid, sd->el_char)) {
const Array &fonts = spans[spans.size() - 1].fonts;
for (int i = 0; i < fonts.size(); i++) {
- if (_font_has_char(fonts[i], '.')) {
+ if (_font_has_char(fonts[i], sd->el_char)) {
dot_gl_font_rid = fonts[i];
- found_dot_char = true;
+ found_el_char = true;
break;
}
}
- if (!found_dot_char && OS::get_singleton()->has_feature("system_fonts") && fonts.size() > 0 && _font_is_allow_system_fallback(fonts[0])) {
- RID rid = _find_sys_font_for_text(fonts[0], String(), spans[spans.size() - 1].language, ".");
+ if (!found_el_char && OS::get_singleton()->has_feature("system_fonts") && fonts.size() > 0 && _font_is_allow_system_fallback(fonts[0])) {
+ const char32_t u32str[] = { sd->el_char, 0 };
+ RID rid = _find_sys_font_for_text(fonts[0], String(), spans[spans.size() - 1].language, u32str);
if (rid.is_valid()) {
dot_gl_font_rid = rid;
+ found_el_char = true;
+ }
+ }
+ } else {
+ found_el_char = true;
+ }
+ if (!found_el_char) {
+ bool found_dot_char = false;
+ dot_gl_font_rid = sd_glyphs[sd_size - 1].font_rid;
+ if (!_font_has_char(dot_gl_font_rid, '.')) {
+ const Array &fonts = spans[spans.size() - 1].fonts;
+ for (int i = 0; i < fonts.size(); i++) {
+ if (_font_has_char(fonts[i], '.')) {
+ dot_gl_font_rid = fonts[i];
+ found_dot_char = true;
+ break;
+ }
+ }
+ if (!found_dot_char && OS::get_singleton()->has_feature("system_fonts") && fonts.size() > 0 && _font_is_allow_system_fallback(fonts[0])) {
+ RID rid = _find_sys_font_for_text(fonts[0], String(), spans[spans.size() - 1].language, ".");
+ if (rid.is_valid()) {
+ dot_gl_font_rid = rid;
+ }
}
}
}
@@ -4130,8 +4132,8 @@ void TextServerFallback::_shaped_text_overrun_trim_to_width(const RID &p_shaped_
}
}
- int32_t dot_gl_idx = dot_gl_font_rid.is_valid() ? _font_get_glyph_index(dot_gl_font_rid, last_gl_font_size, (found_el_char ? sd->el_char : '.'), 0) : -1;
- Vector2 dot_adv = dot_gl_font_rid.is_valid() ? _font_get_glyph_advance(dot_gl_font_rid, last_gl_font_size, dot_gl_idx) : Vector2();
+ int32_t dot_gl_idx = ((add_ellipsis || enforce_ellipsis) && dot_gl_font_rid.is_valid()) ? _font_get_glyph_index(dot_gl_font_rid, last_gl_font_size, (found_el_char ? sd->el_char : '.'), 0) : -1;
+ Vector2 dot_adv = ((add_ellipsis || enforce_ellipsis) && dot_gl_font_rid.is_valid()) ? _font_get_glyph_advance(dot_gl_font_rid, last_gl_font_size, dot_gl_idx) : Vector2();
int32_t whitespace_gl_idx = whitespace_gl_font_rid.is_valid() ? _font_get_glyph_index(whitespace_gl_font_rid, last_gl_font_size, ' ', 0) : -1;
Vector2 whitespace_adv = whitespace_gl_font_rid.is_valid() ? _font_get_glyph_advance(whitespace_gl_font_rid, last_gl_font_size, whitespace_gl_idx) : Vector2();
diff --git a/modules/theora/video_stream_theora.cpp b/modules/theora/video_stream_theora.cpp
index 933271406c..5935fcfe86 100644
--- a/modules/theora/video_stream_theora.cpp
+++ b/modules/theora/video_stream_theora.cpp
@@ -117,7 +117,7 @@ void VideoStreamPlaybackTheora::video_write() {
format = Image::FORMAT_RGBA8;
}
- Ref<Image> img = memnew(Image(size.x, size.y, 0, Image::FORMAT_RGBA8, frame_data)); //zero copy image creation
+ Ref<Image> img = memnew(Image(size.x, size.y, false, Image::FORMAT_RGBA8, frame_data)); //zero copy image creation
texture->update(img); //zero copy send to rendering server
diff --git a/modules/webp/webp_common.cpp b/modules/webp/webp_common.cpp
index 39608cfd11..527a612253 100644
--- a/modules/webp/webp_common.cpp
+++ b/modules/webp/webp_common.cpp
@@ -151,7 +151,7 @@ Ref<Image> _webp_unpack(const Vector<uint8_t> &p_buffer) {
ERR_FAIL_COND_V_MSG(errdec, Ref<Image>(), "Failed decoding WebP image.");
- Ref<Image> img = memnew(Image(features.width, features.height, 0, features.has_alpha ? Image::FORMAT_RGBA8 : Image::FORMAT_RGB8, dst_image));
+ Ref<Image> img = memnew(Image(features.width, features.height, false, features.has_alpha ? Image::FORMAT_RGBA8 : Image::FORMAT_RGB8, dst_image));
return img;
}
diff --git a/platform/android/display_server_android.cpp b/platform/android/display_server_android.cpp
index 492c3f3426..f76b515e87 100644
--- a/platform/android/display_server_android.cpp
+++ b/platform/android/display_server_android.cpp
@@ -205,6 +205,12 @@ void DisplayServerAndroid::emit_file_picker_callback(bool p_ok, const Vector<Str
}
}
+Color DisplayServerAndroid::get_accent_color() const {
+ GodotJavaWrapper *godot_java = OS_Android::get_singleton()->get_godot_java();
+ ERR_FAIL_NULL_V(godot_java, Color(0, 0, 0, 0));
+ return godot_java->get_accent_color();
+}
+
TypedArray<Rect2> DisplayServerAndroid::get_display_cutouts() const {
GodotIOJavaWrapper *godot_io_java = OS_Android::get_singleton()->get_godot_io_java();
ERR_FAIL_NULL_V(godot_io_java, Array());
@@ -245,14 +251,6 @@ DisplayServer::ScreenOrientation DisplayServerAndroid::screen_get_orientation(in
return (ScreenOrientation)orientation;
}
-int DisplayServerAndroid::screen_get_internal_current_rotation(int p_screen) const {
- GodotIOJavaWrapper *godot_io_java = OS_Android::get_singleton()->get_godot_io_java();
- ERR_FAIL_NULL_V(godot_io_java, 0);
-
- const int rotation = godot_io_java->get_internal_current_screen_rotation();
- return rotation;
-}
-
int DisplayServerAndroid::get_screen_count() const {
return 1;
}
diff --git a/platform/android/display_server_android.h b/platform/android/display_server_android.h
index 98d401432f..01aee8de18 100644
--- a/platform/android/display_server_android.h
+++ b/platform/android/display_server_android.h
@@ -127,6 +127,8 @@ public:
virtual Error file_dialog_show(const String &p_title, const String &p_current_directory, const String &p_filename, bool p_show_hidden, const FileDialogMode p_mode, const Vector<String> &p_filters, const Callable &p_callback) override;
void emit_file_picker_callback(bool p_ok, const Vector<String> &p_selected_paths);
+ virtual Color get_accent_color() const override;
+
virtual TypedArray<Rect2> get_display_cutouts() const override;
virtual Rect2i get_display_safe_area() const override;
@@ -135,7 +137,6 @@ public:
virtual void screen_set_orientation(ScreenOrientation p_orientation, int p_screen = SCREEN_OF_MAIN_WINDOW) override;
virtual ScreenOrientation screen_get_orientation(int p_screen = SCREEN_OF_MAIN_WINDOW) const override;
- virtual int screen_get_internal_current_rotation(int p_screen) const override;
virtual int get_screen_count() const override;
virtual int get_primary_screen() const override;
diff --git a/platform/android/export/export.cpp b/platform/android/export/export.cpp
index 03aed4c534..513d1be05b 100644
--- a/platform/android/export/export.cpp
+++ b/platform/android/export/export.cpp
@@ -51,7 +51,9 @@ void register_android_exporter() {
EDITOR_DEF_BASIC("export/android/debug_keystore_pass", DEFAULT_ANDROID_KEYSTORE_DEBUG_PASSWORD);
EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::STRING, "export/android/debug_keystore_pass", PROPERTY_HINT_PASSWORD));
-#ifndef ANDROID_ENABLED
+#ifdef ANDROID_ENABLED
+ EDITOR_DEF_BASIC("export/android/install_exported_apk", true);
+#else
EDITOR_DEF_BASIC("export/android/java_sdk_path", OS::get_singleton()->get_environment("JAVA_HOME"));
EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::STRING, "export/android/java_sdk_path", PROPERTY_HINT_GLOBAL_DIR));
EDITOR_DEF_BASIC("export/android/android_sdk_path", OS::get_singleton()->get_environment("ANDROID_HOME"));
diff --git a/platform/android/export/export_plugin.cpp b/platform/android/export/export_plugin.cpp
index a2ae7bbc13..05c83512f2 100644
--- a/platform/android/export/export_plugin.cpp
+++ b/platform/android/export/export_plugin.cpp
@@ -1695,7 +1695,7 @@ void EditorExportPlatformAndroid::load_icon_refs(const Ref<EditorExportPreset> &
path = static_cast<String>(p_preset->get(launcher_adaptive_icon_monochrome_option)).strip_edges();
if (!path.is_empty()) {
print_verbose("Loading adaptive monochrome icon from " + path);
- ImageLoader::load_image(path, background);
+ ImageLoader::load_image(path, monochrome);
}
}
@@ -1780,6 +1780,12 @@ String EditorExportPlatformAndroid::get_export_option_warning(const EditorExport
if (!is_package_name_valid(pn, &pn_err)) {
return TTR("Invalid package name:") + " " + pn_err;
}
+ } else if (p_name == launcher_adaptive_icon_monochrome_option) {
+ String monochrome_icon_path = p_preset->get(launcher_adaptive_icon_monochrome_option);
+
+ if (monochrome_icon_path.is_empty()) {
+ return TTR("No adaptive monochrome icon specified; default Godot monochrome icon will be used.");
+ }
} else if (p_name == "gradle_build/use_gradle_build") {
bool gradle_build_enabled = p_preset->get("gradle_build/use_gradle_build");
String enabled_plugins_names = _get_plugins_names(Ref<EditorExportPreset>(p_preset));
@@ -2889,6 +2895,14 @@ Error EditorExportPlatformAndroid::sign_apk(const Ref<EditorExportPreset> &p_pre
#endif
print_verbose("Successfully completed signing build.");
+
+#ifdef ANDROID_ENABLED
+ bool prompt_apk_install = EDITOR_GET("export/android/install_exported_apk");
+ if (prompt_apk_install) {
+ OS_Android::get_singleton()->shell_open(apk_path);
+ }
+#endif
+
return OK;
}
diff --git a/platform/android/java/editor/src/main/AndroidManifest.xml b/platform/android/java/editor/src/main/AndroidManifest.xml
index a875745860..c1eb03b31f 100644
--- a/platform/android/java/editor/src/main/AndroidManifest.xml
+++ b/platform/android/java/editor/src/main/AndroidManifest.xml
@@ -25,6 +25,7 @@
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.VIBRATE" />
+ <uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
<application
android:allowBackup="false"
diff --git a/platform/android/java/editor/src/main/java/org/godotengine/editor/BaseGodotEditor.kt b/platform/android/java/editor/src/main/java/org/godotengine/editor/BaseGodotEditor.kt
index 011193f244..a001304116 100644
--- a/platform/android/java/editor/src/main/java/org/godotengine/editor/BaseGodotEditor.kt
+++ b/platform/android/java/editor/src/main/java/org/godotengine/editor/BaseGodotEditor.kt
@@ -392,7 +392,7 @@ abstract class BaseGodotEditor : GodotActivity() {
* If the launch policy is [LaunchPolicy.AUTO], resolve it into a specific policy based on the
* editor setting or device and screen metrics.
*
- * If the launch policy is [LaunchPolicy.PIP] but PIP is not supported, fallback to the default
+ * If the launch policy is [LaunchPolicy.SAME_AND_LAUNCH_IN_PIP_MODE] but PIP is not supported, fallback to the default
* launch policy.
*/
private fun resolveLaunchPolicyIfNeeded(policy: LaunchPolicy): LaunchPolicy {
@@ -455,9 +455,9 @@ abstract class BaseGodotEditor : GodotActivity() {
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
// Check if we got the MANAGE_EXTERNAL_STORAGE permission
- if (requestCode == PermissionsUtil.REQUEST_MANAGE_EXTERNAL_STORAGE_REQ_CODE) {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
- if (!Environment.isExternalStorageManager()) {
+ when (requestCode) {
+ PermissionsUtil.REQUEST_MANAGE_EXTERNAL_STORAGE_REQ_CODE -> {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R && !Environment.isExternalStorageManager()) {
Toast.makeText(
this,
R.string.denied_storage_permission_error_msg,
@@ -465,6 +465,16 @@ abstract class BaseGodotEditor : GodotActivity() {
).show()
}
}
+
+ PermissionsUtil.REQUEST_INSTALL_PACKAGES_REQ_CODE -> {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && !packageManager.canRequestPackageInstalls()) {
+ Toast.makeText(
+ this,
+ R.string.denied_install_packages_permission_error_msg,
+ Toast.LENGTH_LONG
+ ).show()
+ }
+ }
}
}
@@ -516,7 +526,7 @@ abstract class BaseGodotEditor : GodotActivity() {
override fun supportsFeature(featureTag: String): Boolean {
if (featureTag == "xr_editor") {
- return isNativeXRDevice();
+ return isNativeXRDevice()
}
if (featureTag == "horizonos") {
diff --git a/platform/android/java/editor/src/main/res/values/strings.xml b/platform/android/java/editor/src/main/res/values/strings.xml
index 0ad54ac3a1..a25b6c0a2d 100644
--- a/platform/android/java/editor/src/main/res/values/strings.xml
+++ b/platform/android/java/editor/src/main/res/values/strings.xml
@@ -2,5 +2,6 @@
<resources>
<string name="godot_game_activity_name">Godot Play window</string>
<string name="denied_storage_permission_error_msg">Missing storage access permission!</string>
+ <string name="denied_install_packages_permission_error_msg">Missing install packages permission!</string>
<string name="pip_button_description">Button used to toggle picture-in-picture mode for the Play window</string>
</resources>
diff --git a/platform/android/java/lib/src/org/godotengine/godot/Godot.kt b/platform/android/java/lib/src/org/godotengine/godot/Godot.kt
index 9e430c481e..0a424a7c6a 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/Godot.kt
+++ b/platform/android/java/lib/src/org/godotengine/godot/Godot.kt
@@ -927,6 +927,12 @@ class Godot(private val context: Context) {
}
}
+ @Keep
+ private fun getAccentColor(): Int {
+ val value = TypedValue()
+ context.theme.resolveAttribute(android.R.attr.colorAccent, value, true)
+ return value.data
+ }
/**
* Destroys the Godot Engine and kill the process it's running in.
diff --git a/platform/android/java/lib/src/org/godotengine/godot/GodotIO.java b/platform/android/java/lib/src/org/godotengine/godot/GodotIO.java
index 44102c5d9c..d42f8140a2 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/GodotIO.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/GodotIO.java
@@ -298,28 +298,6 @@ public class GodotIO {
}
}
- /**
- This function is used by DisplayServer::screen_get_internal_current_rotation (C++)
- and is used to implement a performance optimization in devices that do not offer
- a HW rotator.
- @return
- Rotation in degrees, in multiples of 90°
- */
- public int getInternalCurrentScreenRotation() {
- int rotation = activity.getWindowManager().getDefaultDisplay().getRotation();
-
- switch (rotation) {
- case Surface.ROTATION_90:
- return 90;
- case Surface.ROTATION_180:
- return 180;
- case Surface.ROTATION_270:
- return 270;
- default:
- return 0;
- }
- }
-
public void setEdit(GodotEditText _edit) {
edit = _edit;
}
diff --git a/platform/android/java/lib/src/org/godotengine/godot/utils/PermissionsUtil.java b/platform/android/java/lib/src/org/godotengine/godot/utils/PermissionsUtil.java
index 966b323bf6..d016b6cad0 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/utils/PermissionsUtil.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/utils/PermissionsUtil.java
@@ -51,7 +51,6 @@ import androidx.core.content.ContextCompat;
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
@@ -68,6 +67,7 @@ public final class PermissionsUtil {
public static final int REQUEST_ALL_PERMISSION_REQ_CODE = 1001;
public static final int REQUEST_SINGLE_PERMISSION_REQ_CODE = 1002;
public static final int REQUEST_MANAGE_EXTERNAL_STORAGE_REQ_CODE = 2002;
+ public static final int REQUEST_INSTALL_PACKAGES_REQ_CODE = 3002;
private PermissionsUtil() {
}
@@ -107,6 +107,16 @@ public final class PermissionsUtil {
activity.startActivityForResult(intent, REQUEST_MANAGE_EXTERNAL_STORAGE_REQ_CODE);
}
}
+ } else if (permission.equals(Manifest.permission.REQUEST_INSTALL_PACKAGES)) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && !activity.getPackageManager().canRequestPackageInstalls()) {
+ try {
+ Intent intent = new Intent(Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES);
+ intent.setData(Uri.parse(String.format("package:%s", activity.getPackageName())));
+ activity.startActivityForResult(intent, REQUEST_INSTALL_PACKAGES_REQ_CODE);
+ } catch (Exception e) {
+ Log.e(TAG, "Unable to request permission " + Manifest.permission.REQUEST_INSTALL_PACKAGES);
+ }
+ }
} else {
PermissionInfo permissionInfo = getPermissionInfo(activity, permission);
int protectionLevel = Build.VERSION.SDK_INT >= Build.VERSION_CODES.P ? permissionInfo.getProtection() : permissionInfo.protectionLevel;
@@ -217,7 +227,7 @@ public final class PermissionsUtil {
try {
manifestPermissions = getManifestPermissions(activity);
} catch (PackageManager.NameNotFoundException e) {
- e.printStackTrace();
+ Log.e(TAG, "Unable to retrieve manifest permissions", e);
return false;
}
@@ -244,7 +254,7 @@ public final class PermissionsUtil {
try {
manifestPermissions = getManifestPermissions(context);
} catch (PackageManager.NameNotFoundException e) {
- e.printStackTrace();
+ Log.e(TAG, "Unable to retrieve manifest permissions", e);
return new String[0];
}
if (manifestPermissions.isEmpty()) {
diff --git a/platform/android/java/nativeSrcsConfigs/CMakeLists.txt b/platform/android/java/nativeSrcsConfigs/CMakeLists.txt
index a7d2774db5..a5ecafeb09 100644
--- a/platform/android/java/nativeSrcsConfigs/CMakeLists.txt
+++ b/platform/android/java/nativeSrcsConfigs/CMakeLists.txt
@@ -21,4 +21,4 @@ target_include_directories(${PROJECT_NAME}
${ANDROID_ROOT_DIR}
${OPENXR_INCLUDE_DIR})
-add_definitions(-DUNIX_ENABLED -DVULKAN_ENABLED -DANDROID_ENABLED -DGLES3_ENABLED -DTOOLS_ENABLED)
+add_definitions(-DUNIX_ENABLED -DVULKAN_ENABLED -DANDROID_ENABLED -DGLES3_ENABLED -DTOOLS_ENABLED -DDEBUG_ENABLED)
diff --git a/platform/android/java_godot_io_wrapper.cpp b/platform/android/java_godot_io_wrapper.cpp
index a43ad40188..651b80e902 100644
--- a/platform/android/java_godot_io_wrapper.cpp
+++ b/platform/android/java_godot_io_wrapper.cpp
@@ -68,7 +68,6 @@ GodotIOJavaWrapper::GodotIOJavaWrapper(JNIEnv *p_env, jobject p_godot_io_instanc
_has_hardware_keyboard = p_env->GetMethodID(cls, "hasHardwareKeyboard", "()Z");
_set_screen_orientation = p_env->GetMethodID(cls, "setScreenOrientation", "(I)V");
_get_screen_orientation = p_env->GetMethodID(cls, "getScreenOrientation", "()I");
- _get_internal_current_screen_rotation = p_env->GetMethodID(cls, "getInternalCurrentScreenRotation", "()I");
_get_system_dir = p_env->GetMethodID(cls, "getSystemDir", "(IZ)Ljava/lang/String;");
}
}
@@ -270,16 +269,6 @@ int GodotIOJavaWrapper::get_screen_orientation() {
}
}
-int GodotIOJavaWrapper::get_internal_current_screen_rotation() {
- if (_get_internal_current_screen_rotation) {
- JNIEnv *env = get_jni_env();
- ERR_FAIL_NULL_V(env, 0);
- return env->CallIntMethod(godot_io_instance, _get_internal_current_screen_rotation);
- } else {
- return 0;
- }
-}
-
String GodotIOJavaWrapper::get_system_dir(int p_dir, bool p_shared_storage) {
if (_get_system_dir) {
JNIEnv *env = get_jni_env();
diff --git a/platform/android/java_godot_io_wrapper.h b/platform/android/java_godot_io_wrapper.h
index 61e2694d02..7aa2967dce 100644
--- a/platform/android/java_godot_io_wrapper.h
+++ b/platform/android/java_godot_io_wrapper.h
@@ -63,7 +63,6 @@ private:
jmethodID _has_hardware_keyboard = 0;
jmethodID _set_screen_orientation = 0;
jmethodID _get_screen_orientation = 0;
- jmethodID _get_internal_current_screen_rotation = 0;
jmethodID _get_system_dir = 0;
public:
@@ -91,7 +90,6 @@ public:
void set_vk_height(int p_height);
void set_screen_orientation(int p_orient);
int get_screen_orientation();
- int get_internal_current_screen_rotation();
String get_system_dir(int p_dir, bool p_shared_storage);
};
diff --git a/platform/android/java_godot_wrapper.cpp b/platform/android/java_godot_wrapper.cpp
index da11c6a5d3..615e1ca897 100644
--- a/platform/android/java_godot_wrapper.cpp
+++ b/platform/android/java_godot_wrapper.cpp
@@ -66,6 +66,7 @@ GodotJavaWrapper::GodotJavaWrapper(JNIEnv *p_env, jobject p_activity, jobject p_
_alert = p_env->GetMethodID(godot_class, "alert", "(Ljava/lang/String;Ljava/lang/String;)V");
_is_dark_mode_supported = p_env->GetMethodID(godot_class, "isDarkModeSupported", "()Z");
_is_dark_mode = p_env->GetMethodID(godot_class, "isDarkMode", "()Z");
+ _get_accent_color = p_env->GetMethodID(godot_class, "getAccentColor", "()I");
_get_clipboard = p_env->GetMethodID(godot_class, "getClipboard", "()Ljava/lang/String;");
_set_clipboard = p_env->GetMethodID(godot_class, "setClipboard", "(Ljava/lang/String;)V");
_has_clipboard = p_env->GetMethodID(godot_class, "hasClipboard", "()Z");
@@ -216,6 +217,23 @@ bool GodotJavaWrapper::is_dark_mode() {
}
}
+Color GodotJavaWrapper::get_accent_color() {
+ if (_get_accent_color) {
+ JNIEnv *env = get_jni_env();
+ ERR_FAIL_NULL_V(env, Color(0, 0, 0, 0));
+ int accent_color = env->CallIntMethod(godot_instance, _get_accent_color);
+
+ // Convert ARGB to RGBA.
+ int alpha = (accent_color >> 24) & 0xFF;
+ int red = (accent_color >> 16) & 0xFF;
+ int green = (accent_color >> 8) & 0xFF;
+ int blue = accent_color & 0xFF;
+ return Color(red / 255.0f, green / 255.0f, blue / 255.0f, alpha / 255.0f);
+ } else {
+ return Color(0, 0, 0, 0);
+ }
+}
+
bool GodotJavaWrapper::has_get_clipboard() {
return _get_clipboard != nullptr;
}
diff --git a/platform/android/java_godot_wrapper.h b/platform/android/java_godot_wrapper.h
index d344cab836..dc51141ec4 100644
--- a/platform/android/java_godot_wrapper.h
+++ b/platform/android/java_godot_wrapper.h
@@ -36,6 +36,7 @@
#include "java_godot_view_wrapper.h"
#include "string_android.h"
+#include "core/math/color.h"
#include "core/templates/list.h"
#include <android/log.h>
@@ -57,6 +58,7 @@ private:
jmethodID _alert = nullptr;
jmethodID _is_dark_mode_supported = nullptr;
jmethodID _is_dark_mode = nullptr;
+ jmethodID _get_accent_color = nullptr;
jmethodID _get_clipboard = nullptr;
jmethodID _set_clipboard = nullptr;
jmethodID _has_clipboard = nullptr;
@@ -101,6 +103,7 @@ public:
void alert(const String &p_message, const String &p_title);
bool is_dark_mode_supported();
bool is_dark_mode();
+ Color get_accent_color();
bool has_get_clipboard();
String get_clipboard();
bool has_set_clipboard();
diff --git a/platform/linuxbsd/os_linuxbsd.cpp b/platform/linuxbsd/os_linuxbsd.cpp
index db2ac3db05..ac01f029b3 100644
--- a/platform/linuxbsd/os_linuxbsd.cpp
+++ b/platform/linuxbsd/os_linuxbsd.cpp
@@ -773,11 +773,11 @@ Vector<String> OS_LinuxBSD::get_system_font_path_for_text(const String &p_font_n
FcLangSetAdd(lang_set, reinterpret_cast<const FcChar8 *>(p_locale.utf8().get_data()));
FcPatternAddLangSet(pattern, FC_LANG, lang_set);
- FcConfigSubstitute(0, pattern, FcMatchPattern);
+ FcConfigSubstitute(nullptr, pattern, FcMatchPattern);
FcDefaultSubstitute(pattern);
FcResult result;
- FcPattern *match = FcFontMatch(0, pattern, &result);
+ FcPattern *match = FcFontMatch(nullptr, pattern, &result);
if (match) {
char *file_name = nullptr;
if (FcPatternGetString(match, FC_FILE, 0, reinterpret_cast<FcChar8 **>(&file_name)) == FcResultMatch) {
@@ -818,11 +818,11 @@ String OS_LinuxBSD::get_system_font_path(const String &p_font_name, int p_weight
FcPatternAddInteger(pattern, FC_WIDTH, _stretch_to_fc(p_stretch));
FcPatternAddInteger(pattern, FC_SLANT, p_italic ? FC_SLANT_ITALIC : FC_SLANT_ROMAN);
- FcConfigSubstitute(0, pattern, FcMatchPattern);
+ FcConfigSubstitute(nullptr, pattern, FcMatchPattern);
FcDefaultSubstitute(pattern);
FcResult result;
- FcPattern *match = FcFontMatch(0, pattern, &result);
+ FcPattern *match = FcFontMatch(nullptr, pattern, &result);
if (match) {
if (!allow_substitutes) {
char *family_name = nullptr;
diff --git a/platform/windows/SCsub b/platform/windows/SCsub
index e17e10e6f1..fa846637a7 100644
--- a/platform/windows/SCsub
+++ b/platform/windows/SCsub
@@ -24,6 +24,7 @@ common_win = [
"gl_manager_windows_angle.cpp",
"wgl_detect_version.cpp",
"rendering_context_driver_vulkan_windows.cpp",
+ "drop_target_windows.cpp",
]
if env.msvc:
diff --git a/platform/windows/crash_handler_windows_signal.cpp b/platform/windows/crash_handler_windows_signal.cpp
index 759b620e26..a2c4ac37e5 100644
--- a/platform/windows/crash_handler_windows_signal.cpp
+++ b/platform/windows/crash_handler_windows_signal.cpp
@@ -161,7 +161,7 @@ extern void CrashHandlerException(int signal) {
// Load process and image info to determine ASLR addresses offset.
MODULEINFO mi;
- GetModuleInformation(GetCurrentProcess(), GetModuleHandle(NULL), &mi, sizeof(mi));
+ GetModuleInformation(GetCurrentProcess(), GetModuleHandle(nullptr), &mi, sizeof(mi));
int64_t image_mem_base = reinterpret_cast<int64_t>(mi.lpBaseOfDll);
int64_t image_file_base = get_image_base(_execpath);
data.offset = image_mem_base - image_file_base;
diff --git a/platform/windows/display_server_windows.cpp b/platform/windows/display_server_windows.cpp
index 36d6f24408..17488b3732 100644
--- a/platform/windows/display_server_windows.cpp
+++ b/platform/windows/display_server_windows.cpp
@@ -32,6 +32,7 @@
#include "display_server_windows.h"
+#include "drop_target_windows.h"
#include "os_windows.h"
#include "wgl_detect_version.h"
@@ -737,7 +738,7 @@ Error DisplayServerWindows::_file_dialog_with_options_show(const String &p_title
GetWindowRect(fd->hwnd_owner, &crect);
fd->wrect = Rect2i(crect.left, crect.top, crect.right - crect.left, crect.bottom - crect.top);
} else {
- fd->hwnd_owner = 0;
+ fd->hwnd_owner = nullptr;
fd->wrect = Rect2i(CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT);
}
fd->appid = appname;
@@ -1619,11 +1620,17 @@ void DisplayServerWindows::delete_sub_window(WindowID p_window) {
}
#endif
- if ((tablet_get_current_driver() == "wintab") && wintab_available && windows[p_window].wtctx) {
- wintab_WTClose(windows[p_window].wtctx);
- windows[p_window].wtctx = nullptr;
+ if ((tablet_get_current_driver() == "wintab") && wintab_available && wd.wtctx) {
+ wintab_WTClose(wd.wtctx);
+ wd.wtctx = nullptr;
+ }
+
+ if (wd.drop_target != nullptr) {
+ RevokeDragDrop(wd.hWnd);
+ wd.drop_target->Release();
}
- DestroyWindow(windows[p_window].hWnd);
+
+ DestroyWindow(wd.hWnd);
windows.erase(p_window);
if (last_focused_window == p_window) {
@@ -1733,7 +1740,14 @@ void DisplayServerWindows::window_set_drop_files_callback(const Callable &p_call
_THREAD_SAFE_METHOD_
ERR_FAIL_COND(!windows.has(p_window));
- windows[p_window].drop_files_callback = p_callable;
+ WindowData &window_data = windows[p_window];
+
+ window_data.drop_files_callback = p_callable;
+
+ if (window_data.drop_target == nullptr) {
+ window_data.drop_target = memnew(DropTargetWindows(&window_data));
+ ERR_FAIL_COND(RegisterDragDrop(window_data.hWnd, window_data.drop_target) != S_OK);
+ }
}
void DisplayServerWindows::window_set_title(const String &p_title, WindowID p_window) {
@@ -5322,32 +5336,6 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA
}
}
} break;
- case WM_DROPFILES: {
- HDROP hDropInfo = (HDROP)wParam;
- const int buffsize = 4096;
- WCHAR buf[buffsize];
-
- int fcount = DragQueryFileW(hDropInfo, 0xFFFFFFFF, nullptr, 0);
-
- Vector<String> files;
-
- for (int i = 0; i < fcount; i++) {
- DragQueryFileW(hDropInfo, i, buf, buffsize);
- String file = String::utf16((const char16_t *)buf);
- files.push_back(file);
- }
-
- if (files.size() && windows[window_id].drop_files_callback.is_valid()) {
- Variant v_files = files;
- const Variant *v_args[1] = { &v_files };
- Variant ret;
- Callable::CallError ce;
- windows[window_id].drop_files_callback.callp((const Variant **)&v_args, 1, ret, ce);
- if (ce.error != Callable::CallError::CALL_OK) {
- ERR_PRINT(vformat("Failed to execute drop files callback: %s.", Variant::get_callable_error_text(windows[window_id].drop_files_callback, v_args, 1, ce)));
- }
- }
- } break;
default: {
if (user_proc) {
return CallWindowProcW(user_proc, hWnd, uMsg, wParam, lParam);
@@ -6176,6 +6164,8 @@ DisplayServerWindows::DisplayServerWindows(const String &p_rendering_driver, Win
FreeLibrary(comctl32);
}
+ OleInitialize(nullptr);
+
memset(&wc, 0, sizeof(WNDCLASSEXW));
wc.cbSize = sizeof(WNDCLASSEXW);
wc.style = CS_OWNDC | CS_DBLCLKS;
@@ -6608,6 +6598,12 @@ DisplayServerWindows::~DisplayServerWindows() {
wintab_WTClose(windows[MAIN_WINDOW_ID].wtctx);
windows[MAIN_WINDOW_ID].wtctx = nullptr;
}
+
+ if (windows[MAIN_WINDOW_ID].drop_target != nullptr) {
+ RevokeDragDrop(windows[MAIN_WINDOW_ID].hWnd);
+ windows[MAIN_WINDOW_ID].drop_target->Release();
+ }
+
DestroyWindow(windows[MAIN_WINDOW_ID].hWnd);
}
@@ -6639,4 +6635,6 @@ DisplayServerWindows::~DisplayServerWindows() {
if (tts) {
memdelete(tts);
}
+
+ OleUninitialize();
}
diff --git a/platform/windows/display_server_windows.h b/platform/windows/display_server_windows.h
index 9143718fb4..b986091a16 100644
--- a/platform/windows/display_server_windows.h
+++ b/platform/windows/display_server_windows.h
@@ -359,9 +359,13 @@ typedef enum _SHC_PROCESS_DPI_AWARENESS {
SHC_PROCESS_PER_MONITOR_DPI_AWARE = 2,
} SHC_PROCESS_DPI_AWARENESS;
+class DropTargetWindows;
+
class DisplayServerWindows : public DisplayServer {
// No need to register with GDCLASS, it's platform-specific and nothing is added.
+ friend class DropTargetWindows;
+
_THREAD_SAFE_CLASS_
// UXTheme API
@@ -523,6 +527,9 @@ class DisplayServerWindows : public DisplayServer {
Callable input_text_callback;
Callable drop_files_callback;
+ // OLE API
+ DropTargetWindows *drop_target = nullptr;
+
WindowID transient_parent = INVALID_WINDOW_ID;
HashSet<WindowID> transient_children;
diff --git a/platform/windows/drop_target_windows.cpp b/platform/windows/drop_target_windows.cpp
new file mode 100644
index 0000000000..ca98a18c5f
--- /dev/null
+++ b/platform/windows/drop_target_windows.cpp
@@ -0,0 +1,377 @@
+/**************************************************************************/
+/* drop_target_windows.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* REDOT ENGINE */
+/* https://redotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2024-present Redot Engine contributors */
+/* (see REDOT_AUTHORS.md) */
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 "drop_target_windows.h"
+
+#include "core/io/dir_access.h"
+#include "core/math/random_pcg.h"
+#include "core/os/time.h"
+
+#include <fileapi.h>
+
+// Helpers
+
+static String create_temp_dir() {
+ Char16String buf;
+ int bufsize = GetTempPathW(0, nullptr) + 1;
+ buf.resize(bufsize);
+ if (GetTempPathW(bufsize, (LPWSTR)buf.ptrw()) == 0) {
+ return "";
+ }
+
+ String tmp_dir = String::utf16((const char16_t *)buf.ptr());
+ RandomPCG gen(Time::get_singleton()->get_ticks_usec());
+
+ const int attempts = 4;
+
+ for (int i = 0; i < attempts; ++i) {
+ uint32_t rnd = gen.rand();
+ String dirname = "godot_tmp_" + String::num_uint64(rnd);
+ String res_dir = tmp_dir.path_join(dirname);
+ Char16String res_dir16 = res_dir.utf16();
+
+ if (CreateDirectoryW((LPCWSTR)res_dir16.ptr(), nullptr)) {
+ return res_dir;
+ }
+ }
+
+ return "";
+}
+
+static bool remove_dir_recursive(const String &p_dir) {
+ Ref<DirAccess> dir_access = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
+ if (dir_access->change_dir(p_dir) != OK) {
+ return false;
+ }
+ return dir_access->erase_contents_recursive() == OK;
+}
+
+static bool stream2file(IStream *p_stream, FILEDESCRIPTORW *p_desc, const String &p_path) {
+ if (DirAccess::make_dir_recursive_absolute(p_path.get_base_dir()) != OK) {
+ return false;
+ }
+
+ Char16String path16 = p_path.utf16();
+ DWORD dwFlagsAndAttributes = FILE_ATTRIBUTE_NORMAL;
+
+ if (p_desc->dwFlags & FD_ATTRIBUTES) {
+ dwFlagsAndAttributes = p_desc->dwFileAttributes;
+ }
+
+ HANDLE file = CreateFileW(
+ (LPCWSTR)path16.ptr(),
+ GENERIC_WRITE,
+ 0,
+ nullptr,
+ CREATE_NEW,
+ dwFlagsAndAttributes,
+ nullptr);
+
+ if (!file) {
+ return false;
+ }
+
+ const int bufsize = 4096;
+ char buf[bufsize];
+ ULONG nread = 0;
+ DWORD nwritten = 0;
+ HRESULT read_result = S_OK;
+ bool result = true;
+
+ while (true) {
+ read_result = p_stream->Read(buf, bufsize, &nread);
+ if (read_result != S_OK && read_result != S_FALSE) {
+ result = false;
+ goto cleanup;
+ }
+
+ if (!nread) {
+ break;
+ }
+
+ while (nread > 0) {
+ if (!WriteFile(file, buf, nread, &nwritten, nullptr) || !nwritten) {
+ result = false;
+ goto cleanup;
+ }
+ nread -= nwritten;
+ }
+ }
+
+cleanup:
+ CloseHandle(file);
+ return result;
+}
+
+// DropTargetWindows
+
+bool DropTargetWindows::is_valid_filedescriptor() {
+ return cf_filedescriptor != 0 && cf_filecontents != 0;
+}
+
+HRESULT DropTargetWindows::handle_hdrop_format(Vector<String> *p_files, IDataObject *pDataObj) {
+ FORMATETC fmt = { CF_HDROP, nullptr, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
+ STGMEDIUM stg;
+ HRESULT res = S_OK;
+
+ if (pDataObj->GetData(&fmt, &stg) != S_OK) {
+ return E_UNEXPECTED;
+ }
+
+ HDROP hDropInfo = (HDROP)GlobalLock(stg.hGlobal);
+
+ Char16String buf;
+
+ if (hDropInfo == nullptr) {
+ ReleaseStgMedium(&stg);
+ return E_UNEXPECTED;
+ }
+
+ int fcount = DragQueryFileW(hDropInfo, 0xFFFFFFFF, nullptr, 0);
+
+ for (int i = 0; i < fcount; i++) {
+ int buffsize = DragQueryFileW(hDropInfo, i, nullptr, 0);
+ buf.resize(buffsize + 1);
+ if (DragQueryFileW(hDropInfo, i, (LPWSTR)buf.ptrw(), buffsize + 1) == 0) {
+ res = E_UNEXPECTED;
+ goto cleanup;
+ }
+ String file = String::utf16((const char16_t *)buf.ptr());
+ p_files->push_back(file);
+ }
+
+cleanup:
+ GlobalUnlock(stg.hGlobal);
+ ReleaseStgMedium(&stg);
+
+ return res;
+}
+
+HRESULT DropTargetWindows::handle_filedescriptor_format(Vector<String> *p_files, IDataObject *pDataObj) {
+ FORMATETC fmt = { cf_filedescriptor, nullptr, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
+ STGMEDIUM stg;
+ HRESULT res = S_OK;
+
+ if (pDataObj->GetData(&fmt, &stg) != S_OK) {
+ return E_UNEXPECTED;
+ }
+
+ FILEGROUPDESCRIPTORW *filegroup_desc = (FILEGROUPDESCRIPTORW *)GlobalLock(stg.hGlobal);
+
+ if (!filegroup_desc) {
+ ReleaseStgMedium(&stg);
+ return E_UNEXPECTED;
+ }
+
+ tmp_path = create_temp_dir();
+ Ref<DirAccess> dir_access = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
+ PackedStringArray copied;
+
+ if (dir_access->change_dir(tmp_path) != OK) {
+ res = E_UNEXPECTED;
+ goto cleanup;
+ }
+
+ for (int i = 0; i < (int)filegroup_desc->cItems; ++i) {
+ res = save_as_file(tmp_path, filegroup_desc->fgd + i, pDataObj, i);
+ if (res != S_OK) {
+ res = E_UNEXPECTED;
+ goto cleanup;
+ }
+ }
+
+ copied = dir_access->get_files();
+ for (const String &file : copied) {
+ p_files->push_back(tmp_path.path_join(file));
+ }
+
+ copied = dir_access->get_directories();
+ for (const String &dir : copied) {
+ p_files->push_back(tmp_path.path_join(dir));
+ }
+
+cleanup:
+ GlobalUnlock(filegroup_desc);
+ ReleaseStgMedium(&stg);
+ if (res != S_OK) {
+ remove_dir_recursive(tmp_path);
+ tmp_path.clear();
+ }
+ return res;
+}
+
+HRESULT DropTargetWindows::save_as_file(const String &p_out_dir, FILEDESCRIPTORW *p_file_desc, IDataObject *pDataObj, int p_file_idx) {
+ String relpath = String::utf16((const char16_t *)p_file_desc->cFileName);
+ String fullpath = p_out_dir.path_join(relpath);
+
+ if (p_file_desc->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
+ if (DirAccess::make_dir_recursive_absolute(fullpath) != OK) {
+ return E_UNEXPECTED;
+ }
+ return S_OK;
+ }
+
+ FORMATETC fmt = { cf_filecontents, nullptr, DVASPECT_CONTENT, p_file_idx, TYMED_ISTREAM };
+ STGMEDIUM stg;
+ HRESULT res = S_OK;
+
+ if (pDataObj->GetData(&fmt, &stg) != S_OK) {
+ return E_UNEXPECTED;
+ }
+
+ IStream *stream = stg.pstm;
+ if (stream == nullptr) {
+ res = E_UNEXPECTED;
+ goto cleanup;
+ }
+
+ if (!stream2file(stream, p_file_desc, fullpath)) {
+ res = E_UNEXPECTED;
+ goto cleanup;
+ }
+
+cleanup:
+ ReleaseStgMedium(&stg);
+ return res;
+}
+
+DropTargetWindows::DropTargetWindows(DisplayServerWindows::WindowData *p_window_data) :
+ ref_count(1), window_data(p_window_data) {
+ cf_filedescriptor = RegisterClipboardFormat(CFSTR_FILEDESCRIPTORW);
+ cf_filecontents = RegisterClipboardFormat(CFSTR_FILECONTENTS);
+}
+
+HRESULT STDMETHODCALLTYPE DropTargetWindows::QueryInterface(REFIID riid, void **ppvObject) {
+ if (riid == IID_IUnknown || riid == IID_IDropTarget) {
+ *ppvObject = static_cast<IDropTarget *>(this);
+ AddRef();
+ return S_OK;
+ }
+ *ppvObject = nullptr;
+ return E_NOINTERFACE;
+}
+
+ULONG STDMETHODCALLTYPE DropTargetWindows::AddRef() {
+ return InterlockedIncrement(&ref_count);
+}
+
+ULONG STDMETHODCALLTYPE DropTargetWindows::Release() {
+ ULONG count = InterlockedDecrement(&ref_count);
+ if (count == 0) {
+ memfree(this);
+ }
+ return count;
+}
+
+HRESULT STDMETHODCALLTYPE DropTargetWindows::DragEnter(IDataObject *pDataObj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect) {
+ (void)grfKeyState;
+ (void)pt;
+
+ FORMATETC hdrop_fmt = { CF_HDROP, nullptr, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
+ FORMATETC filedesc_fmt = { cf_filedescriptor, nullptr, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
+
+ if (!window_data->drop_files_callback.is_valid()) {
+ *pdwEffect = DROPEFFECT_NONE;
+ } else if (pDataObj->QueryGetData(&hdrop_fmt) == S_OK) {
+ *pdwEffect = DROPEFFECT_COPY;
+ } else if (is_valid_filedescriptor() && pDataObj->QueryGetData(&filedesc_fmt) == S_OK) {
+ *pdwEffect = DROPEFFECT_COPY;
+ } else {
+ *pdwEffect = DROPEFFECT_NONE;
+ }
+
+ return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE DropTargetWindows::DragOver(DWORD grfKeyState, POINTL pt, DWORD *pdwEffect) {
+ (void)grfKeyState;
+ (void)pt;
+
+ *pdwEffect = DROPEFFECT_COPY;
+ return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE DropTargetWindows::DragLeave() {
+ return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE DropTargetWindows::Drop(IDataObject *pDataObj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect) {
+ (void)grfKeyState;
+ (void)pt;
+
+ *pdwEffect = DROPEFFECT_NONE;
+
+ if (!window_data->drop_files_callback.is_valid()) {
+ return S_OK;
+ }
+
+ FORMATETC hdrop_fmt = { CF_HDROP, nullptr, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
+ FORMATETC filedesc_fmt = { cf_filedescriptor, nullptr, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
+ Vector<String> files;
+
+ if (pDataObj->QueryGetData(&hdrop_fmt) == S_OK) {
+ HRESULT res = handle_hdrop_format(&files, pDataObj);
+ if (res != S_OK) {
+ return res;
+ }
+ } else if (pDataObj->QueryGetData(&filedesc_fmt) == S_OK && is_valid_filedescriptor()) {
+ HRESULT res = handle_filedescriptor_format(&files, pDataObj);
+ if (res != S_OK) {
+ return res;
+ }
+ } else {
+ return E_UNEXPECTED;
+ }
+
+ if (!files.size()) {
+ return S_OK;
+ }
+
+ Variant v_files = files;
+ const Variant *v_args[1] = { &v_files };
+ Variant ret;
+ Callable::CallError ce;
+ window_data->drop_files_callback.callp((const Variant **)&v_args, 1, ret, ce);
+
+ if (!tmp_path.is_empty()) {
+ remove_dir_recursive(tmp_path);
+ tmp_path.clear();
+ }
+
+ if (ce.error != Callable::CallError::CALL_OK) {
+ ERR_PRINT(vformat("Failed to execute drop files callback: %s.", Variant::get_callable_error_text(window_data->drop_files_callback, v_args, 1, ce)));
+ return E_UNEXPECTED;
+ }
+
+ *pdwEffect = DROPEFFECT_COPY;
+ return S_OK;
+}
diff --git a/platform/windows/drop_target_windows.h b/platform/windows/drop_target_windows.h
new file mode 100644
index 0000000000..98780800fc
--- /dev/null
+++ b/platform/windows/drop_target_windows.h
@@ -0,0 +1,79 @@
+/**************************************************************************/
+/* drop_target_windows.h */
+/**************************************************************************/
+/* This file is part of: */
+/* REDOT ENGINE */
+/* https://redotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2024-present Redot Engine contributors */
+/* (see REDOT_AUTHORS.md) */
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* 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 DROP_TARGET_WINDOWS_H
+#define DROP_TARGET_WINDOWS_H
+
+#include "display_server_windows.h"
+
+#include <shlobj.h>
+
+// Silence warning due to a COM API weirdness.
+#if defined(__GNUC__) && !defined(__clang__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wnon-virtual-dtor"
+#endif
+
+// https://learn.microsoft.com/en-us/windows/win32/api/ole2/nf-ole2-dodragdrop#remarks
+class DropTargetWindows : public IDropTarget {
+ LONG ref_count;
+ DisplayServerWindows::WindowData *window_data = nullptr;
+ CLIPFORMAT cf_filedescriptor = 0;
+ CLIPFORMAT cf_filecontents = 0;
+ String tmp_path;
+
+ bool is_valid_filedescriptor();
+ HRESULT handle_hdrop_format(Vector<String> *p_files, IDataObject *pDataObj);
+ HRESULT handle_filedescriptor_format(Vector<String> *p_files, IDataObject *pDataObj);
+ HRESULT save_as_file(const String &p_out_dir, FILEDESCRIPTORW *p_file_desc, IDataObject *pDataObj, int p_file_idx);
+
+public:
+ DropTargetWindows(DisplayServerWindows::WindowData *p_window_data);
+ virtual ~DropTargetWindows() {}
+
+ // IUnknown
+ HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppvObject) override;
+ ULONG STDMETHODCALLTYPE AddRef() override;
+ ULONG STDMETHODCALLTYPE Release() override;
+
+ // IDropTarget
+ HRESULT STDMETHODCALLTYPE DragEnter(IDataObject *pDataObj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect) override;
+ HRESULT STDMETHODCALLTYPE DragOver(DWORD grfKeyState, POINTL pt, DWORD *pdwEffect) override;
+ HRESULT STDMETHODCALLTYPE DragLeave() override;
+ HRESULT STDMETHODCALLTYPE Drop(IDataObject *pDataObj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect) override;
+};
+
+#if defined(__GNUC__) && !defined(__clang__)
+#pragma GCC diagnostic pop
+#endif
+
+#endif // DROP_TARGET_WINDOWS_H
diff --git a/platform/windows/joypad_windows.cpp b/platform/windows/joypad_windows.cpp
index 922c2da774..5970ded080 100644
--- a/platform/windows/joypad_windows.cpp
+++ b/platform/windows/joypad_windows.cpp
@@ -124,8 +124,9 @@ bool JoypadWindows::is_xinput_device(const GUID *p_guid) {
memcmp(p_guid, &IID_XOneSWirelessGamepad, sizeof(*p_guid)) == 0 ||
memcmp(p_guid, &IID_XOneSBluetoothGamepad, sizeof(*p_guid)) == 0 ||
memcmp(p_guid, &IID_XOneEliteWirelessGamepad, sizeof(*p_guid)) == 0 ||
- memcmp(p_guid, &IID_XOneElite2WirelessGamepad, sizeof(*p_guid)) == 0)
+ memcmp(p_guid, &IID_XOneElite2WirelessGamepad, sizeof(*p_guid)) == 0) {
return true;
+ }
PRAWINPUTDEVICELIST dev_list = nullptr;
unsigned int dev_list_count = 0;
diff --git a/platform/windows/native_menu_windows.cpp b/platform/windows/native_menu_windows.cpp
index 10af1f5a33..92cbaec3fc 100644
--- a/platform/windows/native_menu_windows.cpp
+++ b/platform/windows/native_menu_windows.cpp
@@ -160,7 +160,7 @@ Size2 NativeMenuWindows::get_size(const RID &p_rid) const {
int count = GetMenuItemCount(md->menu);
for (int i = 0; i < count; i++) {
RECT rect;
- if (GetMenuItemRect(NULL, md->menu, i, &rect)) {
+ if (GetMenuItemRect(nullptr, md->menu, i, &rect)) {
size.x = MAX(size.x, rect.right - rect.left);
size.y += rect.bottom - rect.top;
}
@@ -994,7 +994,7 @@ void NativeMenuWindows::set_item_submenu(const RID &p_rid, int p_idx, const RID
if (p_submenu_rid.is_valid()) {
item.hSubMenu = md_sub->menu;
} else {
- item.hSubMenu = 0;
+ item.hSubMenu = nullptr;
}
SetMenuItemInfoW(md->menu, p_idx, true, &item);
}
@@ -1097,7 +1097,7 @@ void NativeMenuWindows::set_item_icon(const RID &p_rid, int p_idx, const Ref<Tex
item_data->bmp = _make_bitmap(item_data->img);
} else {
item_data->img = Ref<Image>();
- item_data->bmp = 0;
+ item_data->bmp = nullptr;
}
item.hbmpItem = item_data->bmp;
SetMenuItemInfoW(md->menu, p_idx, true, &item);
diff --git a/platform/windows/os_windows.cpp b/platform/windows/os_windows.cpp
index f53f9d8c25..a3af90c26d 100644
--- a/platform/windows/os_windows.cpp
+++ b/platform/windows/os_windows.cpp
@@ -141,13 +141,13 @@ void RedirectIOToConsole() {
if (AttachConsole(ATTACH_PARENT_PROCESS)) {
// Restore redirection (Note: if not redirected it's NULL handles not INVALID_HANDLE_VALUE).
- if (h_stdin != 0) {
+ if (h_stdin != nullptr) {
SetStdHandle(STD_INPUT_HANDLE, h_stdin);
}
- if (h_stdout != 0) {
+ if (h_stdout != nullptr) {
SetStdHandle(STD_OUTPUT_HANDLE, h_stdout);
}
- if (h_stderr != 0) {
+ if (h_stderr != nullptr) {
SetStdHandle(STD_ERROR_HANDLE, h_stderr);
}
@@ -910,9 +910,9 @@ Dictionary OS_Windows::execute_with_pipe(const String &p_path, const List<String
}
// Create pipes.
- HANDLE pipe_in[2] = { 0, 0 };
- HANDLE pipe_out[2] = { 0, 0 };
- HANDLE pipe_err[2] = { 0, 0 };
+ HANDLE pipe_in[2] = { nullptr, nullptr };
+ HANDLE pipe_out[2] = { nullptr, nullptr };
+ HANDLE pipe_err[2] = { nullptr, nullptr };
SECURITY_ATTRIBUTES sa;
sa.nLength = sizeof(SECURITY_ATTRIBUTES);
@@ -983,7 +983,7 @@ Dictionary OS_Windows::execute_with_pipe(const String &p_path, const List<String
Ref<FileAccessWindowsPipe> err_pipe;
err_pipe.instantiate();
- err_pipe->open_existing(pipe_err[0], 0, p_blocking);
+ err_pipe->open_existing(pipe_err[0], nullptr, p_blocking);
ret["stdio"] = main_pipe;
ret["stderr"] = err_pipe;
diff --git a/platform/windows/windows_utils.cpp b/platform/windows/windows_utils.cpp
index 38c7f189c1..1c112d44f2 100644
--- a/platform/windows/windows_utils.cpp
+++ b/platform/windows/windows_utils.cpp
@@ -69,11 +69,11 @@ Error WindowsUtils::copy_and_rename_pdb(const String &p_dll_path) {
{
// The custom LoadLibraryExW is used instead of open_dynamic_library
// to avoid loading the original PDB into the debugger.
- HMODULE library_ptr = LoadLibraryExW((LPCWSTR)(p_dll_path.utf16().get_data()), NULL, LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE);
+ HMODULE library_ptr = LoadLibraryExW((LPCWSTR)(p_dll_path.utf16().get_data()), nullptr, LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE);
ERR_FAIL_NULL_V_MSG(library_ptr, ERR_FILE_CANT_OPEN, vformat("Failed to load library '%s'.", p_dll_path));
- IMAGE_DEBUG_DIRECTORY *dbg_dir = (IMAGE_DEBUG_DIRECTORY *)ImageDirectoryEntryToDataEx(library_ptr, FALSE, IMAGE_DIRECTORY_ENTRY_DEBUG, &dbg_info_size, NULL);
+ IMAGE_DEBUG_DIRECTORY *dbg_dir = (IMAGE_DEBUG_DIRECTORY *)ImageDirectoryEntryToDataEx(library_ptr, FALSE, IMAGE_DIRECTORY_ENTRY_DEBUG, &dbg_info_size, nullptr);
bool has_debug = dbg_dir && dbg_dir->Type == IMAGE_DEBUG_TYPE_CODEVIEW;
if (has_debug) {
diff --git a/scene/2d/animated_sprite_2d.cpp b/scene/2d/animated_sprite_2d.cpp
index adc12be14e..8473d8a61d 100644
--- a/scene/2d/animated_sprite_2d.cpp
+++ b/scene/2d/animated_sprite_2d.cpp
@@ -58,7 +58,9 @@ Point2 AnimatedSprite2D::_edit_get_pivot() const {
bool AnimatedSprite2D::_edit_use_pivot() const {
return true;
}
+#endif // TOOLS_ENABLED
+#ifdef DEBUG_ENABLED
Rect2 AnimatedSprite2D::_edit_get_rect() const {
return _get_rect();
}
@@ -77,7 +79,7 @@ bool AnimatedSprite2D::_edit_use_rect() const {
}
return t.is_valid();
}
-#endif
+#endif // DEBUG_ENABLED
Rect2 AnimatedSprite2D::get_anchorable_rect() const {
return _get_rect();
@@ -595,7 +597,7 @@ void AnimatedSprite2D::get_argument_options(const StringName &p_function, int p_
}
Node2D::get_argument_options(p_function, p_idx, r_options);
}
-#endif
+#endif // TOOLS_ENABLED
#ifndef DISABLE_DEPRECATED
bool AnimatedSprite2D::_set(const StringName &p_name, const Variant &p_value) {
diff --git a/scene/2d/animated_sprite_2d.h b/scene/2d/animated_sprite_2d.h
index f3adc621d4..295cf23c6b 100644
--- a/scene/2d/animated_sprite_2d.h
+++ b/scene/2d/animated_sprite_2d.h
@@ -68,7 +68,7 @@ class AnimatedSprite2D : public Node2D {
protected:
#ifndef DISABLE_DEPRECATED
bool _set(const StringName &p_name, const Variant &p_value);
-#endif
+#endif // DISABLE_DEPRECATED
static void _bind_methods();
void _notification(int p_what);
void _validate_property(PropertyInfo &p_property) const;
@@ -81,9 +81,12 @@ public:
virtual void _edit_set_pivot(const Point2 &p_pivot) override;
virtual Point2 _edit_get_pivot() const override;
virtual bool _edit_use_pivot() const override;
+#endif // TOOLS_ENABLED
+
+#ifdef DEBUG_ENABLED
virtual Rect2 _edit_get_rect() const override;
virtual bool _edit_use_rect() const override;
-#endif
+#endif // DEBUG_ENABLED
virtual Rect2 get_anchorable_rect() const override;
@@ -131,7 +134,7 @@ public:
#ifdef TOOLS_ENABLED
virtual void get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const override;
-#endif
+#endif // TOOLS_ENABLED
AnimatedSprite2D();
};
diff --git a/scene/2d/back_buffer_copy.cpp b/scene/2d/back_buffer_copy.cpp
index f9d200c9d0..f69da09b82 100644
--- a/scene/2d/back_buffer_copy.cpp
+++ b/scene/2d/back_buffer_copy.cpp
@@ -47,7 +47,7 @@ void BackBufferCopy::_update_copy_mode() {
}
}
-#ifdef TOOLS_ENABLED
+#ifdef DEBUG_ENABLED
Rect2 BackBufferCopy::_edit_get_rect() const {
return rect;
}
@@ -55,7 +55,7 @@ Rect2 BackBufferCopy::_edit_get_rect() const {
bool BackBufferCopy::_edit_use_rect() const {
return true;
}
-#endif
+#endif // DEBUG_ENABLED
Rect2 BackBufferCopy::get_anchorable_rect() const {
return rect;
diff --git a/scene/2d/back_buffer_copy.h b/scene/2d/back_buffer_copy.h
index 8cc426bd34..c07836049b 100644
--- a/scene/2d/back_buffer_copy.h
+++ b/scene/2d/back_buffer_copy.h
@@ -56,10 +56,10 @@ protected:
void _validate_property(PropertyInfo &p_property) const;
public:
-#ifdef TOOLS_ENABLED
+#ifdef DEBUG_ENABLED
Rect2 _edit_get_rect() const override;
virtual bool _edit_use_rect() const override;
-#endif
+#endif // DEBUG_ENABLED
void set_rect(const Rect2 &p_rect);
Rect2 get_rect() const;
diff --git a/scene/2d/light_2d.cpp b/scene/2d/light_2d.cpp
index 94fc1c5ff2..ce35aebd71 100644
--- a/scene/2d/light_2d.cpp
+++ b/scene/2d/light_2d.cpp
@@ -56,7 +56,7 @@ void Light2D::_update_light_visibility() {
if (editor_only) {
editor_ok = false;
}
-#endif
+#endif // TOOLS_ENABLED
RS::get_singleton()->canvas_light_set_enabled(canvas_light, enabled && is_visible_in_tree() && editor_ok);
}
@@ -345,7 +345,6 @@ Light2D::~Light2D() {
//////////////////////////////
#ifdef TOOLS_ENABLED
-
Dictionary PointLight2D::_edit_get_state() const {
Dictionary state = Node2D::_edit_get_state();
state["offset"] = get_texture_offset();
@@ -369,7 +368,9 @@ Point2 PointLight2D::_edit_get_pivot() const {
bool PointLight2D::_edit_use_pivot() const {
return true;
}
+#endif // TOOLS_ENABLED
+#ifdef DEBUG_ENABLED
Rect2 PointLight2D::_edit_get_rect() const {
if (texture.is_null()) {
return Rect2();
@@ -382,7 +383,7 @@ Rect2 PointLight2D::_edit_get_rect() const {
bool PointLight2D::_edit_use_rect() const {
return !texture.is_null();
}
-#endif
+#endif // DEBUG_ENABLED
Rect2 PointLight2D::get_anchorable_rect() const {
if (texture.is_null()) {
diff --git a/scene/2d/light_2d.h b/scene/2d/light_2d.h
index 9eec41c69b..d812c49788 100644
--- a/scene/2d/light_2d.h
+++ b/scene/2d/light_2d.h
@@ -162,9 +162,12 @@ public:
virtual void _edit_set_pivot(const Point2 &p_pivot) override;
virtual Point2 _edit_get_pivot() const override;
virtual bool _edit_use_pivot() const override;
+#endif // TOOLS_ENABLED
+
+#ifdef DEBUG_ENABLED
virtual Rect2 _edit_get_rect() const override;
virtual bool _edit_use_rect() const override;
-#endif
+#endif // DEBUG_ENABLED
virtual Rect2 get_anchorable_rect() const override;
diff --git a/scene/2d/light_occluder_2d.cpp b/scene/2d/light_occluder_2d.cpp
index 77f181b28a..6bc2916c4a 100644
--- a/scene/2d/light_occluder_2d.cpp
+++ b/scene/2d/light_occluder_2d.cpp
@@ -37,7 +37,7 @@
#define LINE_GRAB_WIDTH 8
-#ifdef TOOLS_ENABLED
+#ifdef DEBUG_ENABLED
Rect2 OccluderPolygon2D::_edit_get_rect() const {
if (rect_cache_dirty) {
if (closed) {
@@ -85,7 +85,7 @@ bool OccluderPolygon2D::_edit_is_selected_on_click(const Point2 &p_point, double
return false;
}
}
-#endif
+#endif // DEBUG_ENABLED
void OccluderPolygon2D::set_polygon(const Vector<Vector2> &p_polygon) {
polygon = p_polygon;
@@ -158,7 +158,7 @@ OccluderPolygon2D::~OccluderPolygon2D() {
void LightOccluder2D::_poly_changed() {
#ifdef DEBUG_ENABLED
queue_redraw();
-#endif
+#endif // DEBUG_ENABLED
}
void LightOccluder2D::_physics_interpolated_changed() {
@@ -220,7 +220,7 @@ void LightOccluder2D::_notification(int p_what) {
}
}
-#ifdef TOOLS_ENABLED
+#ifdef DEBUG_ENABLED
Rect2 LightOccluder2D::_edit_get_rect() const {
return occluder_polygon.is_valid() ? occluder_polygon->_edit_get_rect() : Rect2();
}
@@ -228,14 +228,14 @@ Rect2 LightOccluder2D::_edit_get_rect() const {
bool LightOccluder2D::_edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const {
return occluder_polygon.is_valid() ? occluder_polygon->_edit_is_selected_on_click(p_point, p_tolerance) : false;
}
-#endif
+#endif // DEBUG_ENABLED
void LightOccluder2D::set_occluder_polygon(const Ref<OccluderPolygon2D> &p_polygon) {
#ifdef DEBUG_ENABLED
if (occluder_polygon.is_valid()) {
occluder_polygon->disconnect_changed(callable_mp(this, &LightOccluder2D::_poly_changed));
}
-#endif
+#endif // DEBUG_ENABLED
occluder_polygon = p_polygon;
if (occluder_polygon.is_valid()) {
@@ -249,7 +249,7 @@ void LightOccluder2D::set_occluder_polygon(const Ref<OccluderPolygon2D> &p_polyg
occluder_polygon->connect_changed(callable_mp(this, &LightOccluder2D::_poly_changed));
}
queue_redraw();
-#endif
+#endif // DEBUG_ENABLED
}
Ref<OccluderPolygon2D> LightOccluder2D::get_occluder_polygon() const {
diff --git a/scene/2d/light_occluder_2d.h b/scene/2d/light_occluder_2d.h
index 6855307606..996c0e70d7 100644
--- a/scene/2d/light_occluder_2d.h
+++ b/scene/2d/light_occluder_2d.h
@@ -58,11 +58,10 @@ protected:
static void _bind_methods();
public:
-#ifdef TOOLS_ENABLED
+#ifdef DEBUG_ENABLED
virtual Rect2 _edit_get_rect() const;
virtual bool _edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const;
-#endif
-
+#endif // DEBUG_ENABLED
void set_polygon(const Vector<Vector2> &p_polygon);
Vector<Vector2> get_polygon() const;
@@ -95,10 +94,10 @@ protected:
static void _bind_methods();
public:
-#ifdef TOOLS_ENABLED
+#ifdef DEBUG_ENABLED
virtual Rect2 _edit_get_rect() const override;
virtual bool _edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const override;
-#endif
+#endif // DEBUG_ENABLED
void set_occluder_polygon(const Ref<OccluderPolygon2D> &p_polygon);
Ref<OccluderPolygon2D> get_occluder_polygon() const;
diff --git a/scene/2d/line_2d.cpp b/scene/2d/line_2d.cpp
index a7df39af34..643d60b7ba 100644
--- a/scene/2d/line_2d.cpp
+++ b/scene/2d/line_2d.cpp
@@ -38,7 +38,7 @@
Line2D::Line2D() {
}
-#ifdef TOOLS_ENABLED
+#ifdef DEBUG_ENABLED
Rect2 Line2D::_edit_get_rect() const {
if (_points.size() == 0) {
return Rect2(0, 0, 0, 0);
diff --git a/scene/2d/line_2d.h b/scene/2d/line_2d.h
index 0a37a4e8e5..8c527ed906 100644
--- a/scene/2d/line_2d.h
+++ b/scene/2d/line_2d.h
@@ -57,7 +57,7 @@ public:
LINE_TEXTURE_STRETCH
};
-#ifdef TOOLS_ENABLED
+#ifdef DEBUG_ENABLED
virtual Rect2 _edit_get_rect() const override;
virtual bool _edit_use_rect() const override;
virtual bool _edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const override;
diff --git a/scene/2d/marker_2d.cpp b/scene/2d/marker_2d.cpp
index a7fe235fe1..e187f36e52 100644
--- a/scene/2d/marker_2d.cpp
+++ b/scene/2d/marker_2d.cpp
@@ -63,7 +63,7 @@ void Marker2D::_draw_cross() {
draw_multiline_colors(points, colors);
}
-#ifdef TOOLS_ENABLED
+#ifdef DEBUG_ENABLED
Rect2 Marker2D::_edit_get_rect() const {
real_t extents = get_gizmo_extents();
return Rect2(Point2(-extents, -extents), Size2(extents * 2, extents * 2));
@@ -72,7 +72,7 @@ Rect2 Marker2D::_edit_get_rect() const {
bool Marker2D::_edit_use_rect() const {
return false;
}
-#endif
+#endif // DEBUG_ENABLED
void Marker2D::_notification(int p_what) {
switch (p_what) {
diff --git a/scene/2d/marker_2d.h b/scene/2d/marker_2d.h
index 4c6351591b..a408185b61 100644
--- a/scene/2d/marker_2d.h
+++ b/scene/2d/marker_2d.h
@@ -47,10 +47,10 @@ protected:
static void _bind_methods();
public:
-#ifdef TOOLS_ENABLED
+#ifdef DEBUG_ENABLED
virtual Rect2 _edit_get_rect() const override;
virtual bool _edit_use_rect() const override;
-#endif
+#endif // DEBUG_ENABLED
void set_gizmo_extents(real_t p_extents);
real_t get_gizmo_extents() const;
diff --git a/scene/2d/mesh_instance_2d.cpp b/scene/2d/mesh_instance_2d.cpp
index aa28bb2908..a30e1576bf 100644
--- a/scene/2d/mesh_instance_2d.cpp
+++ b/scene/2d/mesh_instance_2d.cpp
@@ -90,7 +90,7 @@ Ref<Texture2D> MeshInstance2D::get_texture() const {
return texture;
}
-#ifdef TOOLS_ENABLED
+#ifdef DEBUG_ENABLED
Rect2 MeshInstance2D::_edit_get_rect() const {
if (mesh.is_valid()) {
AABB aabb = mesh->get_aabb();
@@ -103,7 +103,7 @@ Rect2 MeshInstance2D::_edit_get_rect() const {
bool MeshInstance2D::_edit_use_rect() const {
return mesh.is_valid();
}
-#endif
+#endif // DEBUG_ENABLED
MeshInstance2D::MeshInstance2D() {
}
diff --git a/scene/2d/mesh_instance_2d.h b/scene/2d/mesh_instance_2d.h
index 14260ac75c..1166dff4e0 100644
--- a/scene/2d/mesh_instance_2d.h
+++ b/scene/2d/mesh_instance_2d.h
@@ -47,10 +47,10 @@ protected:
static void _bind_methods();
public:
-#ifdef TOOLS_ENABLED
+#ifdef DEBUG_ENABLED
virtual Rect2 _edit_get_rect() const override;
virtual bool _edit_use_rect() const override;
-#endif
+#endif // DEBUG_ENABLED
void set_mesh(const Ref<Mesh> &p_mesh);
Ref<Mesh> get_mesh() const;
diff --git a/scene/2d/multimesh_instance_2d.cpp b/scene/2d/multimesh_instance_2d.cpp
index 078e7e52d1..2ab5612cff 100644
--- a/scene/2d/multimesh_instance_2d.cpp
+++ b/scene/2d/multimesh_instance_2d.cpp
@@ -86,7 +86,7 @@ Ref<Texture2D> MultiMeshInstance2D::get_texture() const {
return texture;
}
-#ifdef TOOLS_ENABLED
+#ifdef DEBUG_ENABLED
Rect2 MultiMeshInstance2D::_edit_get_rect() const {
if (multimesh.is_valid()) {
AABB aabb = multimesh->get_aabb();
@@ -95,7 +95,7 @@ Rect2 MultiMeshInstance2D::_edit_get_rect() const {
return Node2D::_edit_get_rect();
}
-#endif
+#endif // DEBUG_ENABLED
MultiMeshInstance2D::MultiMeshInstance2D() {
}
diff --git a/scene/2d/multimesh_instance_2d.h b/scene/2d/multimesh_instance_2d.h
index f4bbda30b4..dd3c6d2256 100644
--- a/scene/2d/multimesh_instance_2d.h
+++ b/scene/2d/multimesh_instance_2d.h
@@ -48,9 +48,9 @@ protected:
static void _bind_methods();
public:
-#ifdef TOOLS_ENABLED
+#ifdef DEBUG_ENABLED
virtual Rect2 _edit_get_rect() const override;
-#endif
+#endif // DEBUG_ENABLED
void set_multimesh(const Ref<MultiMesh> &p_multimesh);
Ref<MultiMesh> get_multimesh() const;
diff --git a/scene/2d/navigation_link_2d.cpp b/scene/2d/navigation_link_2d.cpp
index 4872c696b4..c8f1e5c192 100644
--- a/scene/2d/navigation_link_2d.cpp
+++ b/scene/2d/navigation_link_2d.cpp
@@ -134,7 +134,7 @@ void NavigationLink2D::_notification(int p_what) {
}
}
-#ifdef TOOLS_ENABLED
+#ifdef DEBUG_ENABLED
Rect2 NavigationLink2D::_edit_get_rect() const {
if (!is_inside_tree()) {
return Rect2();
@@ -154,7 +154,7 @@ bool NavigationLink2D::_edit_is_selected_on_click(const Point2 &p_point, double
Vector2 closest_point = Geometry2D::get_closest_point_to_segment(p_point, segment);
return p_point.distance_to(closest_point) < p_tolerance;
}
-#endif // TOOLS_ENABLED
+#endif // DEBUG_ENABLED
RID NavigationLink2D::get_rid() const {
return link;
diff --git a/scene/2d/navigation_link_2d.h b/scene/2d/navigation_link_2d.h
index 446850574a..d6826353d2 100644
--- a/scene/2d/navigation_link_2d.h
+++ b/scene/2d/navigation_link_2d.h
@@ -64,10 +64,10 @@ protected:
#endif // DISABLE_DEPRECATED
public:
-#ifdef TOOLS_ENABLED
+#ifdef DEBUG_ENABLED
virtual Rect2 _edit_get_rect() const override;
virtual bool _edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const override;
-#endif
+#endif // DEBUG_ENABLED
RID get_rid() const;
void set_enabled(bool p_enabled);
diff --git a/scene/2d/navigation_region_2d.cpp b/scene/2d/navigation_region_2d.cpp
index 01349b2618..cf28fd2227 100644
--- a/scene/2d/navigation_region_2d.cpp
+++ b/scene/2d/navigation_region_2d.cpp
@@ -144,7 +144,7 @@ RID NavigationRegion2D::get_region_rid() const {
return get_rid();
}
-#ifdef TOOLS_ENABLED
+#ifdef DEBUG_ENABLED
Rect2 NavigationRegion2D::_edit_get_rect() const {
return navigation_polygon.is_valid() ? navigation_polygon->_edit_get_rect() : Rect2();
}
@@ -152,7 +152,7 @@ Rect2 NavigationRegion2D::_edit_get_rect() const {
bool NavigationRegion2D::_edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const {
return navigation_polygon.is_valid() ? navigation_polygon->_edit_is_selected_on_click(p_point, p_tolerance) : false;
}
-#endif
+#endif // DEBUG_ENABLED
void NavigationRegion2D::_notification(int p_what) {
switch (p_what) {
diff --git a/scene/2d/navigation_region_2d.h b/scene/2d/navigation_region_2d.h
index e57e4de26d..7c27524fa9 100644
--- a/scene/2d/navigation_region_2d.h
+++ b/scene/2d/navigation_region_2d.h
@@ -77,10 +77,10 @@ protected:
#endif // DISABLE_DEPRECATED
public:
-#ifdef TOOLS_ENABLED
+#ifdef DEBUG_ENABLED
virtual Rect2 _edit_get_rect() const override;
virtual bool _edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const override;
-#endif
+#endif // DEBUG_ENABLED
RID get_rid() const;
void set_enabled(bool p_enabled);
diff --git a/scene/2d/path_2d.cpp b/scene/2d/path_2d.cpp
index c1fbd46d16..e89d963c3a 100644
--- a/scene/2d/path_2d.cpp
+++ b/scene/2d/path_2d.cpp
@@ -39,7 +39,7 @@
#include "editor/themes/editor_scale.h"
#endif
-#ifdef TOOLS_ENABLED
+#ifdef DEBUG_ENABLED
Rect2 Path2D::_edit_get_rect() const {
if (!curve.is_valid() || curve->get_point_count() == 0) {
return Rect2(0, 0, 0, 0);
diff --git a/scene/2d/path_2d.h b/scene/2d/path_2d.h
index 71229af2d8..39f63d8784 100644
--- a/scene/2d/path_2d.h
+++ b/scene/2d/path_2d.h
@@ -50,7 +50,7 @@ protected:
static void _bind_methods();
public:
-#ifdef TOOLS_ENABLED
+#ifdef DEBUG_ENABLED
virtual Rect2 _edit_get_rect() const override;
virtual bool _edit_use_rect() const override;
virtual bool _edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const override;
diff --git a/scene/2d/physics/collision_polygon_2d.cpp b/scene/2d/physics/collision_polygon_2d.cpp
index a2fb3b0f9f..1afbe8c960 100644
--- a/scene/2d/physics/collision_polygon_2d.cpp
+++ b/scene/2d/physics/collision_polygon_2d.cpp
@@ -219,7 +219,7 @@ CollisionPolygon2D::BuildMode CollisionPolygon2D::get_build_mode() const {
return build_mode;
}
-#ifdef TOOLS_ENABLED
+#ifdef DEBUG_ENABLED
Rect2 CollisionPolygon2D::_edit_get_rect() const {
return aabb;
}
diff --git a/scene/2d/physics/collision_polygon_2d.h b/scene/2d/physics/collision_polygon_2d.h
index 1eb52c3ea5..7754960fa7 100644
--- a/scene/2d/physics/collision_polygon_2d.h
+++ b/scene/2d/physics/collision_polygon_2d.h
@@ -67,7 +67,7 @@ protected:
static void _bind_methods();
public:
-#ifdef TOOLS_ENABLED
+#ifdef DEBUG_ENABLED
virtual Rect2 _edit_get_rect() const override;
virtual bool _edit_use_rect() const override;
virtual bool _edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const override;
diff --git a/scene/2d/physics/collision_shape_2d.h b/scene/2d/physics/collision_shape_2d.h
index e5f022934a..80916b0726 100644
--- a/scene/2d/physics/collision_shape_2d.h
+++ b/scene/2d/physics/collision_shape_2d.h
@@ -61,11 +61,11 @@ protected:
static void _bind_methods();
public:
-#ifdef TOOLS_ENABLED
+#ifdef DEBUG_ENABLED
virtual bool _edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const override;
#else
virtual bool _edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const;
-#endif // TOOLS_ENABLED
+#endif // DEBUG_ENABLED
void set_shape(const Ref<Shape2D> &p_shape);
Ref<Shape2D> get_shape() const;
diff --git a/scene/2d/polygon_2d.cpp b/scene/2d/polygon_2d.cpp
index 6a73e180e5..a2026179a6 100644
--- a/scene/2d/polygon_2d.cpp
+++ b/scene/2d/polygon_2d.cpp
@@ -59,7 +59,9 @@ Point2 Polygon2D::_edit_get_pivot() const {
bool Polygon2D::_edit_use_pivot() const {
return true;
}
+#endif // TOOLS_ENABLED
+#ifdef DEBUG_ENABLED
Rect2 Polygon2D::_edit_get_rect() const {
if (rect_cache_dirty) {
int l = polygon.size();
@@ -90,7 +92,7 @@ bool Polygon2D::_edit_is_selected_on_click(const Point2 &p_point, double p_toler
}
return Geometry2D::is_point_in_polygon(p_point - get_offset(), polygon2d);
}
-#endif
+#endif // DEBUG_ENABLED
void Polygon2D::_validate_property(PropertyInfo &p_property) const {
if (!invert && p_property.name == "invert_border") {
diff --git a/scene/2d/polygon_2d.h b/scene/2d/polygon_2d.h
index 4b65511a14..d5fd5b772b 100644
--- a/scene/2d/polygon_2d.h
+++ b/scene/2d/polygon_2d.h
@@ -89,11 +89,14 @@ public:
virtual void _edit_set_pivot(const Point2 &p_pivot) override;
virtual Point2 _edit_get_pivot() const override;
virtual bool _edit_use_pivot() const override;
+#endif // TOOLS_ENABLED
+
+#ifdef DEBUG_ENABLED
virtual Rect2 _edit_get_rect() const override;
virtual bool _edit_use_rect() const override;
virtual bool _edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const override;
-#endif
+#endif // DEBUG_ENABLED
void set_polygon(const Vector<Vector2> &p_polygon);
Vector<Vector2> get_polygon() const;
diff --git a/scene/2d/sprite_2d.cpp b/scene/2d/sprite_2d.cpp
index 90775a2e2d..b50476e6f7 100644
--- a/scene/2d/sprite_2d.cpp
+++ b/scene/2d/sprite_2d.cpp
@@ -58,7 +58,9 @@ Point2 Sprite2D::_edit_get_pivot() const {
bool Sprite2D::_edit_use_pivot() const {
return true;
}
+#endif // TOOLS_ENABLED
+#ifdef DEBUG_ENABLED
bool Sprite2D::_edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const {
return is_pixel_opaque(p_point);
}
@@ -70,7 +72,7 @@ Rect2 Sprite2D::_edit_get_rect() const {
bool Sprite2D::_edit_use_rect() const {
return texture.is_valid();
}
-#endif
+#endif // DEBUG_ENABLED
Rect2 Sprite2D::get_anchorable_rect() const {
return get_rect();
diff --git a/scene/2d/sprite_2d.h b/scene/2d/sprite_2d.h
index ae20038055..3aa687ce80 100644
--- a/scene/2d/sprite_2d.h
+++ b/scene/2d/sprite_2d.h
@@ -76,11 +76,14 @@ public:
virtual void _edit_set_pivot(const Point2 &p_pivot) override;
virtual Point2 _edit_get_pivot() const override;
virtual bool _edit_use_pivot() const override;
+#endif // TOOLS_ENABLED
+
+#ifdef DEBUG_ENABLED
virtual bool _edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const override;
virtual Rect2 _edit_get_rect() const override;
virtual bool _edit_use_rect() const override;
-#endif
+#endif // DEBUG_ENABLED
bool is_pixel_opaque(const Point2 &p_point) const;
diff --git a/scene/2d/tile_map.cpp b/scene/2d/tile_map.cpp
index 2b6ce77979..cb66cfe3b5 100644
--- a/scene/2d/tile_map.cpp
+++ b/scene/2d/tile_map.cpp
@@ -91,8 +91,8 @@ void TileMap::_set_tile_map_data_using_compatibility_format(int p_layer, TileMap
SWAP(local[8], local[11]);
SWAP(local[9], local[10]);
}
-#endif
- // Extracts position in TileMap.
+#endif // BIG_ENDIAN_ENABLED
+ // Extracts position in TileMap.
int16_t x = decode_uint16(&local[0]);
int16_t y = decode_uint16(&local[2]);
@@ -174,7 +174,7 @@ void TileMap::_notification(int p_what) {
bool in_editor = false;
#ifdef TOOLS_ENABLED
in_editor = Engine::get_singleton()->is_editor_hint();
-#endif
+#endif // TOOLS_ENABLED
if (is_inside_tree() && collision_animatable && !in_editor) {
// Update transform on the physics tick when in animatable mode.
last_valid_transform = new_transform;
@@ -190,7 +190,7 @@ void TileMap::_notification(int p_what) {
bool in_editor = false;
#ifdef TOOLS_ENABLED
in_editor = Engine::get_singleton()->is_editor_hint();
-#endif
+#endif // TOOLS_ENABLED
if (is_inside_tree() && collision_animatable && !in_editor) {
// Store last valid transform.
@@ -211,7 +211,7 @@ void TileMap::force_update(int p_layer) {
notify_runtime_tile_data_update(p_layer);
update_internals();
}
-#endif
+#endif // DISABLE_DEPRECATED
void TileMap::set_rendering_quadrant_size(int p_size) {
ERR_FAIL_COND_MSG(p_size < 1, "TileMapQuadrant size cannot be smaller than 1.");
@@ -651,7 +651,7 @@ void TileMap::notify_runtime_tile_data_update(int p_layer) {
}
}
-#ifdef TOOLS_ENABLED
+#ifdef DEBUG_ENABLED
Rect2 TileMap::_edit_get_rect() const {
// Return the visible rect of the tilemap.
if (layers.is_empty()) {
@@ -669,7 +669,7 @@ Rect2 TileMap::_edit_get_rect() const {
const_cast<TileMap *>(this)->item_rect_changed(any_changed);
return rect;
}
-#endif
+#endif // DEBUG_ENABLED
bool TileMap::_set(const StringName &p_name, const Variant &p_value) {
int index;
@@ -728,7 +728,7 @@ bool TileMap::_get(const StringName &p_name, Variant &r_ret) const {
r_ret = get_rendering_quadrant_size();
return true;
}
-#endif
+#endif // DISABLE_DEPRECATED
else {
return property_helper.property_get_value(sname, r_ret);
}
diff --git a/scene/2d/tile_map.h b/scene/2d/tile_map.h
index fb3e56622e..7783eba508 100644
--- a/scene/2d/tile_map.h
+++ b/scene/2d/tile_map.h
@@ -109,16 +109,16 @@ protected:
VisibilityMode _get_navigation_visibility_mode_bind_compat_87115();
static void _bind_compatibility_methods();
-#endif
+#endif // DISABLE_DEPRECATED
public:
-#ifdef TOOLS_ENABLED
+#ifdef DEBUG_ENABLED
virtual Rect2 _edit_get_rect() const override;
-#endif
+#endif // DEBUG_ENABLED
#ifndef DISABLE_DEPRECATED
void force_update(int p_layer);
-#endif
+#endif // DISABLE_DEPRECATED
void set_rendering_quadrant_size(int p_size);
int get_rendering_quadrant_size() const;
diff --git a/scene/2d/touch_screen_button.cpp b/scene/2d/touch_screen_button.cpp
index f003442265..c566f26399 100644
--- a/scene/2d/touch_screen_button.cpp
+++ b/scene/2d/touch_screen_button.cpp
@@ -332,7 +332,7 @@ void TouchScreenButton::_release(bool p_exiting_tree) {
}
}
-#ifdef TOOLS_ENABLED
+#ifdef DEBUG_ENABLED
Rect2 TouchScreenButton::_edit_get_rect() const {
if (texture_normal.is_null()) {
return CanvasItem::_edit_get_rect();
@@ -344,7 +344,7 @@ Rect2 TouchScreenButton::_edit_get_rect() const {
bool TouchScreenButton::_edit_use_rect() const {
return !texture_normal.is_null();
}
-#endif
+#endif // DEBUG_ENABLED
Rect2 TouchScreenButton::get_anchorable_rect() const {
if (texture_normal.is_null()) {
diff --git a/scene/2d/touch_screen_button.h b/scene/2d/touch_screen_button.h
index 678ab087b7..d859dd224b 100644
--- a/scene/2d/touch_screen_button.h
+++ b/scene/2d/touch_screen_button.h
@@ -78,10 +78,10 @@ protected:
#endif // DISABLE_DEPRECATED
public:
-#ifdef TOOLS_ENABLED
+#ifdef DEBUG_ENABLED
virtual Rect2 _edit_get_rect() const override;
virtual bool _edit_use_rect() const override;
-#endif
+#endif // DEBUG_ENABLED
void set_texture_normal(const Ref<Texture2D> &p_texture);
Ref<Texture2D> get_texture_normal() const;
diff --git a/scene/2d/visible_on_screen_notifier_2d.cpp b/scene/2d/visible_on_screen_notifier_2d.cpp
index b36b270db1..92f015a6c0 100644
--- a/scene/2d/visible_on_screen_notifier_2d.cpp
+++ b/scene/2d/visible_on_screen_notifier_2d.cpp
@@ -32,7 +32,7 @@
#include "visible_on_screen_notifier_2d.h"
-#ifdef TOOLS_ENABLED
+#ifdef DEBUG_ENABLED
Rect2 VisibleOnScreenNotifier2D::_edit_get_rect() const {
return rect;
}
@@ -40,7 +40,7 @@ Rect2 VisibleOnScreenNotifier2D::_edit_get_rect() const {
bool VisibleOnScreenNotifier2D::_edit_use_rect() const {
return true;
}
-#endif
+#endif // DEBUG_ENABLED
void VisibleOnScreenNotifier2D::_visibility_enter() {
if (!is_inside_tree() || Engine::get_singleton()->is_editor_hint()) {
diff --git a/scene/2d/visible_on_screen_notifier_2d.h b/scene/2d/visible_on_screen_notifier_2d.h
index 5a5e11e433..d980b3e0c8 100644
--- a/scene/2d/visible_on_screen_notifier_2d.h
+++ b/scene/2d/visible_on_screen_notifier_2d.h
@@ -56,10 +56,10 @@ protected:
static void _bind_methods();
public:
-#ifdef TOOLS_ENABLED
+#ifdef DEBUG_ENABLED
virtual Rect2 _edit_get_rect() const override;
virtual bool _edit_use_rect() const override;
-#endif
+#endif // DEBUG_ENABLED
void set_rect(const Rect2 &p_rect);
Rect2 get_rect() const;
diff --git a/scene/3d/skeleton_3d.cpp b/scene/3d/skeleton_3d.cpp
index 6f5af8c43f..abf1d217e5 100644
--- a/scene/3d/skeleton_3d.cpp
+++ b/scene/3d/skeleton_3d.cpp
@@ -185,17 +185,17 @@ bool Skeleton3D::_get(const StringName &p_path, Variant &r_ret) const {
void Skeleton3D::_get_property_list(List<PropertyInfo> *p_list) const {
for (uint32_t i = 0; i < bones.size(); i++) {
- const String prep = vformat("%s/%d/", PNAME("bones"), i);
- p_list->push_back(PropertyInfo(Variant::STRING, prep + PNAME("name"), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR));
- p_list->push_back(PropertyInfo(Variant::INT, prep + PNAME("parent"), PROPERTY_HINT_RANGE, "-1," + itos(bones.size() - 1) + ",1", PROPERTY_USAGE_NO_EDITOR));
- p_list->push_back(PropertyInfo(Variant::TRANSFORM3D, prep + PNAME("rest"), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR));
- p_list->push_back(PropertyInfo(Variant::BOOL, prep + PNAME("enabled"), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR));
- p_list->push_back(PropertyInfo(Variant::VECTOR3, prep + PNAME("position"), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR));
- p_list->push_back(PropertyInfo(Variant::QUATERNION, prep + PNAME("rotation"), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR));
- p_list->push_back(PropertyInfo(Variant::VECTOR3, prep + PNAME("scale"), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR));
+ const String prep = vformat("%s/%d/", "bones", i);
+ p_list->push_back(PropertyInfo(Variant::STRING, prep + "name", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR));
+ p_list->push_back(PropertyInfo(Variant::INT, prep + "parent", PROPERTY_HINT_RANGE, "-1," + itos(bones.size() - 1) + ",1", PROPERTY_USAGE_NO_EDITOR));
+ p_list->push_back(PropertyInfo(Variant::TRANSFORM3D, prep + "rest", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR));
+ p_list->push_back(PropertyInfo(Variant::BOOL, prep + "enabled", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR));
+ p_list->push_back(PropertyInfo(Variant::VECTOR3, prep + "position", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR));
+ p_list->push_back(PropertyInfo(Variant::QUATERNION, prep + "rotation", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR));
+ p_list->push_back(PropertyInfo(Variant::VECTOR3, prep + "scale", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR));
for (const KeyValue<StringName, Variant> &K : bones[i].metadata) {
- PropertyInfo pi = PropertyInfo(bones[i].metadata[K.key].get_type(), prep + PNAME("bone_meta/") + K.key, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR);
+ PropertyInfo pi = PropertyInfo(bones[i].metadata[K.key].get_type(), prep + "bone_meta/" + K.key, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR);
p_list->push_back(pi);
}
}
diff --git a/scene/3d/skeleton_3d.h b/scene/3d/skeleton_3d.h
index 7c9bc29fb4..4853bd202a 100644
--- a/scene/3d/skeleton_3d.h
+++ b/scene/3d/skeleton_3d.h
@@ -164,7 +164,7 @@ private:
Vector<int> parentless_bones;
AHashMap<String, int> name_to_bone_index;
- mutable StringName concatenated_bone_names = StringName();
+ mutable StringName concatenated_bone_names;
void _update_bone_names() const;
void _make_dirty();
diff --git a/scene/3d/skeleton_ik_3d.h b/scene/3d/skeleton_ik_3d.h
index d81b81117e..a36c672a8e 100644
--- a/scene/3d/skeleton_ik_3d.h
+++ b/scene/3d/skeleton_ik_3d.h
@@ -133,7 +133,7 @@ class SkeletonIK3D : public SkeletonModifier3D {
real_t min_distance = 0.01;
int max_iterations = 10;
- Variant target_node_override_ref = Variant();
+ Variant target_node_override_ref;
FabrikInverseKinematic::Task *task = nullptr;
#ifndef DISABLE_DEPRECATED
diff --git a/scene/gui/code_edit.h b/scene/gui/code_edit.h
index c1dea5e77c..253f933b66 100644
--- a/scene/gui/code_edit.h
+++ b/scene/gui/code_edit.h
@@ -253,13 +253,13 @@ private:
Ref<Texture2D> completion_color_bg;
Color breakpoint_color = Color(1, 1, 1);
- Ref<Texture2D> breakpoint_icon = Ref<Texture2D>();
+ Ref<Texture2D> breakpoint_icon;
Color bookmark_color = Color(1, 1, 1);
- Ref<Texture2D> bookmark_icon = Ref<Texture2D>();
+ Ref<Texture2D> bookmark_icon;
Color executing_line_color = Color(1, 1, 1);
- Ref<Texture2D> executing_line_icon = Ref<Texture2D>();
+ Ref<Texture2D> executing_line_icon;
Color line_number_color = Color(1, 1, 1);
diff --git a/scene/gui/control.cpp b/scene/gui/control.cpp
index 57fcba6f5d..d2a260b750 100644
--- a/scene/gui/control.cpp
+++ b/scene/gui/control.cpp
@@ -46,7 +46,7 @@
#ifdef TOOLS_ENABLED
#include "editor/plugins/control_editor_plugin.h"
-#endif
+#endif // TOOLS_ENABLED
// Editor plugin interoperability.
@@ -142,14 +142,6 @@ void Control::_edit_set_rect(const Rect2 &p_edit_rect) {
set_size(p_edit_rect.size.snappedf(1), ControlEditorToolbar::get_singleton()->is_anchors_mode_enabled());
}
-Rect2 Control::_edit_get_rect() const {
- return Rect2(Point2(), get_size());
-}
-
-bool Control::_edit_use_rect() const {
- return true;
-}
-
void Control::_edit_set_rotation(real_t p_rotation) {
set_rotation(p_rotation);
}
@@ -180,7 +172,17 @@ bool Control::_edit_use_pivot() const {
Size2 Control::_edit_get_minimum_size() const {
return get_combined_minimum_size();
}
-#endif
+#endif // TOOLS_ENABLED
+
+#ifdef DEBUG_ENABLED
+Rect2 Control::_edit_get_rect() const {
+ return Rect2(Point2(), get_size());
+}
+
+bool Control::_edit_use_rect() const {
+ return true;
+}
+#endif // DEBUG_ENABLED
void Control::reparent(Node *p_parent, bool p_keep_global_transform) {
ERR_MAIN_THREAD_GUARD;
@@ -241,7 +243,7 @@ void Control::get_argument_options(const StringName &p_function, int p_idx, List
}
CanvasItem::get_argument_options(p_function, p_idx, r_options);
}
-#endif
+#endif // TOOLS_ENABLED
PackedStringArray Control::get_configuration_warnings() const {
ERR_READ_THREAD_GUARD_V(PackedStringArray());
@@ -665,7 +667,7 @@ Rect2 Control::get_parent_anchorable_rect() const {
#else
parent_rect = get_viewport()->get_visible_rect();
-#endif
+#endif // TOOLS_ENABLED
}
return parent_rect;
@@ -1398,7 +1400,7 @@ void Control::set_position(const Point2 &p_point, bool p_keep_offsets) {
data.pos_cache = p_point;
return;
}
-#endif
+#endif // TOOLS_ENABLED
if (p_keep_offsets) {
_compute_anchors(Rect2(p_point, data.size_cache), data.offset, data.anchor);
@@ -1442,7 +1444,7 @@ void Control::_set_size(const Size2 &p_size) {
if (data.size_warning && (data.anchor[SIDE_LEFT] != data.anchor[SIDE_RIGHT] || data.anchor[SIDE_TOP] != data.anchor[SIDE_BOTTOM])) {
WARN_PRINT("Nodes with non-equal opposite anchors will have their size overridden after _ready(). \nIf you want to set size, change the anchors or consider using set_deferred().");
}
-#endif
+#endif // DEBUG_ENABLED
set_size(p_size);
}
@@ -1464,7 +1466,7 @@ void Control::set_size(const Size2 &p_size, bool p_keep_offsets) {
data.size_cache = new_size;
return;
}
-#endif
+#endif // TOOLS_ENABLED
if (p_keep_offsets) {
_compute_anchors(Rect2(data.pos_cache, new_size), data.offset, data.anchor);
@@ -2747,7 +2749,7 @@ Variant Control::get_theme_item(Theme::DataType p_data_type, const StringName &p
Ref<Texture2D> Control::get_editor_theme_icon(const StringName &p_name) const {
return get_theme_icon(p_name, SNAME("EditorIcons"));
}
-#endif
+#endif // TOOLS_ENABLED
bool Control::has_theme_icon(const StringName &p_name, const StringName &p_theme_type) const {
ERR_READ_THREAD_GUARD_V(false);
@@ -3089,7 +3091,7 @@ bool Control::is_layout_rtl() const {
const_cast<Control *>(this)->data.is_rtl = true;
return data.is_rtl;
}
-#endif
+#endif // TOOLS_ENABLED
Node *parent_node = get_parent();
while (parent_node) {
Control *parent_control = Object::cast_to<Control>(parent_node);
@@ -3164,7 +3166,7 @@ bool Control::is_auto_translating() const {
ERR_READ_THREAD_GUARD_V(false);
return can_auto_translate();
}
-#endif
+#endif // DISABLE_DEPRECATED
void Control::set_tooltip_auto_translate_mode(AutoTranslateMode p_mode) {
ERR_MAIN_THREAD_GUARD;
@@ -3217,7 +3219,7 @@ void Control::_notification(int p_notification) {
case NOTIFICATION_EDITOR_POST_SAVE: {
saving = false;
} break;
-#endif
+#endif // TOOLS_ENABLED
case NOTIFICATION_POSTINITIALIZE: {
data.initialized = true;
@@ -3263,7 +3265,7 @@ void Control::_notification(int p_notification) {
case NOTIFICATION_READY: {
#ifdef DEBUG_ENABLED
connect(SceneStringName(ready), callable_mp(this, &Control::_clear_size_warning), CONNECT_DEFERRED | CONNECT_ONE_SHOT);
-#endif
+#endif // DEBUG_ENABLED
} break;
case NOTIFICATION_ENTER_CANVAS: {
@@ -3575,7 +3577,7 @@ void Control::_bind_methods() {
#ifndef DISABLE_DEPRECATED
ClassDB::bind_method(D_METHOD("set_auto_translate", "enable"), &Control::set_auto_translate);
ClassDB::bind_method(D_METHOD("is_auto_translating"), &Control::is_auto_translating);
-#endif
+#endif // DISABLE_DEPRECATED
ClassDB::bind_method(D_METHOD("set_localize_numeral_system", "enable"), &Control::set_localize_numeral_system);
ClassDB::bind_method(D_METHOD("is_localizing_numeral_system"), &Control::is_localizing_numeral_system);
@@ -3630,7 +3632,7 @@ void Control::_bind_methods() {
#ifndef DISABLE_DEPRECATED
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "auto_translate", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "set_auto_translate", "is_auto_translating");
-#endif
+#endif // DISABLE_DEPRECATED
ADD_GROUP("Tooltip", "tooltip_");
ADD_PROPERTY(PropertyInfo(Variant::STRING, "tooltip_text", PROPERTY_HINT_MULTILINE_TEXT), "set_tooltip_text", "get_tooltip_text");
diff --git a/scene/gui/control.h b/scene/gui/control.h
index 8740ee41f1..e22bb6ba04 100644
--- a/scene/gui/control.h
+++ b/scene/gui/control.h
@@ -50,7 +50,7 @@ class Control : public CanvasItem {
#ifdef TOOLS_ENABLED
bool saving = false;
-#endif
+#endif //TOOLS_ENABLED
public:
enum Anchor {
@@ -398,8 +398,6 @@ public:
virtual Size2 _edit_get_scale() const override;
virtual void _edit_set_rect(const Rect2 &p_edit_rect) override;
- virtual Rect2 _edit_get_rect() const override;
- virtual bool _edit_use_rect() const override;
virtual void _edit_set_rotation(real_t p_rotation) override;
virtual real_t _edit_get_rotation() const override;
@@ -410,7 +408,13 @@ public:
virtual bool _edit_use_pivot() const override;
virtual Size2 _edit_get_minimum_size() const override;
-#endif
+#endif //TOOLS_ENABLED
+
+#ifdef DEBUG_ENABLED
+ virtual Rect2 _edit_get_rect() const override;
+ virtual bool _edit_use_rect() const override;
+#endif // DEBUG_ENABLED
+
virtual void reparent(Node *p_parent, bool p_keep_global_transform = true) override;
// Editor integration.
@@ -420,7 +424,7 @@ public:
PackedStringArray get_configuration_warnings() const override;
#ifdef TOOLS_ENABLED
virtual void get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const override;
-#endif
+#endif //TOOLS_ENABLED
virtual bool is_text_field() const;
@@ -607,7 +611,7 @@ public:
Variant get_theme_item(Theme::DataType p_data_type, const StringName &p_name, const StringName &p_theme_type = StringName()) const;
#ifdef TOOLS_ENABLED
Ref<Texture2D> get_editor_theme_icon(const StringName &p_name) const;
-#endif
+#endif //TOOLS_ENABLED
bool has_theme_icon_override(const StringName &p_name) const;
bool has_theme_stylebox_override(const StringName &p_name) const;
@@ -639,7 +643,7 @@ public:
#ifndef DISABLE_DEPRECATED
void set_auto_translate(bool p_enable);
bool is_auto_translating() const;
-#endif
+#endif //DISABLE_DEPRECATED
void set_tooltip_auto_translate_mode(AutoTranslateMode p_mode);
AutoTranslateMode get_tooltip_auto_translate_mode() const;
diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp
index 12cbc3948e..2e8c24d177 100644
--- a/scene/gui/text_edit.cpp
+++ b/scene/gui/text_edit.cpp
@@ -5071,7 +5071,7 @@ bool TextEdit::multicaret_edit_ignore_caret(int p_caret) const {
}
bool TextEdit::is_caret_visible(int p_caret) const {
- ERR_FAIL_INDEX_V(p_caret, carets.size(), 0);
+ ERR_FAIL_INDEX_V(p_caret, carets.size(), false);
return carets[p_caret].visible;
}
@@ -5738,7 +5738,7 @@ TextServer::AutowrapMode TextEdit::get_autowrap_mode() const {
}
bool TextEdit::is_line_wrapped(int p_line) const {
- ERR_FAIL_INDEX_V(p_line, text.size(), 0);
+ ERR_FAIL_INDEX_V(p_line, text.size(), false);
if (get_line_wrapping_mode() == LineWrappingMode::LINE_WRAPPING_NONE) {
return false;
}
diff --git a/scene/gui/text_edit.h b/scene/gui/text_edit.h
index 8886cbec2b..74f376ea74 100644
--- a/scene/gui/text_edit.h
+++ b/scene/gui/text_edit.h
@@ -141,7 +141,7 @@ private:
Variant metadata;
bool clickable = false;
- Ref<Texture2D> icon = Ref<Texture2D>();
+ Ref<Texture2D> icon;
String text = "";
Color color = Color(1, 1, 1);
};
diff --git a/scene/main/canvas_item.cpp b/scene/main/canvas_item.cpp
index d9f3bfdea0..be2b694f67 100644
--- a/scene/main/canvas_item.cpp
+++ b/scene/main/canvas_item.cpp
@@ -46,7 +46,7 @@
#define ERR_DRAW_GUARD \
ERR_FAIL_COND_MSG(!drawing, "Drawing is only allowed inside this node's `_draw()`, functions connected to its `draw` signal, or when it receives NOTIFICATION_DRAW.")
-#ifdef TOOLS_ENABLED
+#ifdef DEBUG_ENABLED
bool CanvasItem::_edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const {
if (_edit_use_rect()) {
return _edit_get_rect().has_point(p_point);
@@ -54,11 +54,13 @@ bool CanvasItem::_edit_is_selected_on_click(const Point2 &p_point, double p_tole
return p_point.length() < p_tolerance;
}
}
+#endif // DEBUG_ENABLED
+#ifdef TOOLS_ENABLED
Transform2D CanvasItem::_edit_get_transform() const {
return Transform2D(_edit_get_rotation(), _edit_get_position() + _edit_get_pivot());
}
-#endif
+#endif //TOOLS_ENABLED
bool CanvasItem::is_visible_in_tree() const {
ERR_READ_THREAD_GUARD_V(false);
@@ -1160,7 +1162,7 @@ void CanvasItem::_bind_methods() {
ClassDB::bind_method(D_METHOD("_edit_get_pivot"), &CanvasItem::_edit_get_pivot);
ClassDB::bind_method(D_METHOD("_edit_use_pivot"), &CanvasItem::_edit_use_pivot);
ClassDB::bind_method(D_METHOD("_edit_get_transform"), &CanvasItem::_edit_get_transform);
-#endif
+#endif //TOOLS_ENABLED
ClassDB::bind_method(D_METHOD("get_canvas_item"), &CanvasItem::get_canvas_item);
diff --git a/scene/main/canvas_item.h b/scene/main/canvas_item.h
index 3211962618..76dd6092a2 100644
--- a/scene/main/canvas_item.h
+++ b/scene/main/canvas_item.h
@@ -176,7 +176,7 @@ protected:
void _draw_multiline_bind_compat_84523(const Vector<Point2> &p_points, const Color &p_color, real_t p_width);
void _draw_multiline_colors_bind_compat_84523(const Vector<Point2> &p_points, const Vector<Color> &p_colors, real_t p_width);
static void _bind_compatibility_methods();
-#endif
+#endif // DISABLE_DEPRECATED
void _validate_property(PropertyInfo &p_property) const;
@@ -195,11 +195,9 @@ public:
NOTIFICATION_WORLD_2D_CHANGED = 36,
};
- /* EDITOR */
-#ifdef TOOLS_ENABLED
- // Select the node
- virtual bool _edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const;
+ /* EDITOR AND DEBUGGING */
+#ifdef TOOLS_ENABLED
// Save and restore a CanvasItem state
virtual void _edit_set_state(const Dictionary &p_state) {}
virtual Dictionary _edit_get_state() const { return Dictionary(); }
@@ -218,9 +216,7 @@ public:
virtual real_t _edit_get_rotation() const { return 0.0; }
// Used to resize/move the node
- virtual bool _edit_use_rect() const { return false; } // MAYBE REPLACE BY A _edit_get_editmode()
virtual void _edit_set_rect(const Rect2 &p_rect) {}
- virtual Rect2 _edit_get_rect() const { return Rect2(0, 0, 0, 0); }
virtual Size2 _edit_get_minimum_size() const { return Size2(-1, -1); } // LOOKS WEIRD
// Used to set a pivot
@@ -229,7 +225,18 @@ public:
virtual Point2 _edit_get_pivot() const { return Point2(); }
virtual Transform2D _edit_get_transform() const;
-#endif
+#endif // TOOLS_ENABLED
+
+#ifdef DEBUG_ENABLED
+ // Those need to be available in debug runtime, to allow for node selection.
+
+ // Select the node.
+ virtual bool _edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const;
+
+ // Used to resize/move the node.
+ virtual bool _edit_use_rect() const { return false; } // Maybe replace with _edit_get_editmode().
+ virtual Rect2 _edit_get_rect() const { return Rect2(0, 0, 0, 0); }
+#endif // DEBUG_ENABLED
void update_draw_order();
diff --git a/scene/resources/2d/navigation_polygon.cpp b/scene/resources/2d/navigation_polygon.cpp
index 8eb548a044..b3d7305199 100644
--- a/scene/resources/2d/navigation_polygon.cpp
+++ b/scene/resources/2d/navigation_polygon.cpp
@@ -38,7 +38,7 @@
#include "thirdparty/misc/polypartition.h"
-#ifdef TOOLS_ENABLED
+#ifdef DEBUG_ENABLED
Rect2 NavigationPolygon::_edit_get_rect() const {
RWLockRead read_lock(rwlock);
if (rect_cache_dirty) {
@@ -81,7 +81,7 @@ bool NavigationPolygon::_edit_is_selected_on_click(const Point2 &p_point, double
}
return false;
}
-#endif
+#endif // DEBUG_ENABLED
void NavigationPolygon::set_vertices(const Vector<Vector2> &p_vertices) {
RWLockWrite write_lock(rwlock);
diff --git a/scene/resources/2d/navigation_polygon.h b/scene/resources/2d/navigation_polygon.h
index 9b04b993a3..272dc1a040 100644
--- a/scene/resources/2d/navigation_polygon.h
+++ b/scene/resources/2d/navigation_polygon.h
@@ -70,10 +70,11 @@ protected:
TypedArray<Vector<Vector2>> _get_outlines() const;
public:
-#ifdef TOOLS_ENABLED
+#ifdef DEBUG_ENABLED
Rect2 _edit_get_rect() const;
bool _edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const;
-#endif
+#endif // DEBUG_ENABLED
+
enum SamplePartitionType {
SAMPLE_PARTITION_CONVEX_PARTITION = 0,
SAMPLE_PARTITION_TRIANGULATE,
diff --git a/scene/resources/2d/skeleton/skeleton_modification_stack_2d.h b/scene/resources/2d/skeleton/skeleton_modification_stack_2d.h
index f9021cc82a..406891d8ec 100644
--- a/scene/resources/2d/skeleton/skeleton_modification_stack_2d.h
+++ b/scene/resources/2d/skeleton/skeleton_modification_stack_2d.h
@@ -66,7 +66,7 @@ public:
execution_mode_physics_process
};
- Vector<Ref<SkeletonModification2D>> modifications = Vector<Ref<SkeletonModification2D>>();
+ Vector<Ref<SkeletonModification2D>> modifications;
void setup();
void execute(float p_delta, int p_execution_mode);
diff --git a/scene/resources/2d/tile_set.cpp b/scene/resources/2d/tile_set.cpp
index 5e9668955c..bc3954523e 100644
--- a/scene/resources/2d/tile_set.cpp
+++ b/scene/resources/2d/tile_set.cpp
@@ -4222,10 +4222,10 @@ void TileSet::_get_property_list(List<PropertyInfo> *p_list) const {
// Tile Proxies.
// Note: proxies need to be set after sources are set.
- p_list->push_back(PropertyInfo(Variant::NIL, GNAME("Tile Proxies", ""), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_GROUP));
- p_list->push_back(PropertyInfo(Variant::ARRAY, PNAME("tile_proxies/source_level"), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR));
- p_list->push_back(PropertyInfo(Variant::ARRAY, PNAME("tile_proxies/coords_level"), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR));
- p_list->push_back(PropertyInfo(Variant::ARRAY, PNAME("tile_proxies/alternative_level"), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR));
+ p_list->push_back(PropertyInfo(Variant::NIL, "Tile Proxies", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_GROUP));
+ p_list->push_back(PropertyInfo(Variant::ARRAY, "tile_proxies/source_level", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR));
+ p_list->push_back(PropertyInfo(Variant::ARRAY, "tile_proxies/coords_level", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR));
+ p_list->push_back(PropertyInfo(Variant::ARRAY, "tile_proxies/alternative_level", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR));
// Patterns.
for (unsigned int pattern_index = 0; pattern_index < patterns.size(); pattern_index++) {
diff --git a/scene/resources/2d/tile_set.h b/scene/resources/2d/tile_set.h
index af0812e05b..d5a30c4b7f 100644
--- a/scene/resources/2d/tile_set.h
+++ b/scene/resources/2d/tile_set.h
@@ -838,7 +838,7 @@ private:
bool flip_v = false;
bool transpose = false;
Vector2i texture_origin;
- Ref<Material> material = Ref<Material>();
+ Ref<Material> material;
Color modulate = Color(1.0, 1.0, 1.0, 1.0);
int z_index = 0;
int y_sort_origin = 0;
diff --git a/scene/resources/3d/importer_mesh.cpp b/scene/resources/3d/importer_mesh.cpp
index 1658eeabef..fc8e7aa20b 100644
--- a/scene/resources/3d/importer_mesh.cpp
+++ b/scene/resources/3d/importer_mesh.cpp
@@ -170,10 +170,56 @@ void ImporterMesh::set_surface_material(int p_surface, const Ref<Material> &p_ma
mesh.unref();
}
-void ImporterMesh::optimize_indices_for_cache() {
+template <typename T>
+static Vector<T> _remap_array(Vector<T> p_array, const Vector<uint32_t> &p_remap, uint32_t p_vertex_count) {
+ ERR_FAIL_COND_V(p_array.size() % p_remap.size() != 0, p_array);
+ int num_elements = p_array.size() / p_remap.size();
+ T *data = p_array.ptrw();
+ SurfaceTool::remap_vertex_func(data, data, p_remap.size(), sizeof(T) * num_elements, p_remap.ptr());
+ p_array.resize(p_vertex_count * num_elements);
+ return p_array;
+}
+
+static void _remap_arrays(Array &r_arrays, const Vector<uint32_t> &p_remap, uint32_t p_vertex_count) {
+ for (int i = 0; i < r_arrays.size(); i++) {
+ if (i == RS::ARRAY_INDEX) {
+ continue;
+ }
+
+ switch (r_arrays[i].get_type()) {
+ case Variant::NIL:
+ break;
+ case Variant::PACKED_VECTOR3_ARRAY:
+ r_arrays[i] = _remap_array<Vector3>(r_arrays[i], p_remap, p_vertex_count);
+ break;
+ case Variant::PACKED_VECTOR2_ARRAY:
+ r_arrays[i] = _remap_array<Vector2>(r_arrays[i], p_remap, p_vertex_count);
+ break;
+ case Variant::PACKED_FLOAT32_ARRAY:
+ r_arrays[i] = _remap_array<float>(r_arrays[i], p_remap, p_vertex_count);
+ break;
+ case Variant::PACKED_INT32_ARRAY:
+ r_arrays[i] = _remap_array<int32_t>(r_arrays[i], p_remap, p_vertex_count);
+ break;
+ case Variant::PACKED_BYTE_ARRAY:
+ r_arrays[i] = _remap_array<uint8_t>(r_arrays[i], p_remap, p_vertex_count);
+ break;
+ case Variant::PACKED_COLOR_ARRAY:
+ r_arrays[i] = _remap_array<Color>(r_arrays[i], p_remap, p_vertex_count);
+ break;
+ default:
+ ERR_FAIL_MSG("Unhandled array type.");
+ }
+ }
+}
+
+void ImporterMesh::optimize_indices() {
if (!SurfaceTool::optimize_vertex_cache_func) {
return;
}
+ if (!SurfaceTool::optimize_vertex_fetch_remap_func || !SurfaceTool::remap_vertex_func || !SurfaceTool::remap_index_func) {
+ return;
+ }
for (int i = 0; i < surfaces.size(); i++) {
if (surfaces[i].primitive != Mesh::PRIMITIVE_TRIANGLES) {
@@ -190,10 +236,48 @@ void ImporterMesh::optimize_indices_for_cache() {
continue;
}
+ // Optimize indices for vertex cache to establish final triangle order.
int *indices_ptr = indices.ptrw();
SurfaceTool::optimize_vertex_cache_func((unsigned int *)indices_ptr, (const unsigned int *)indices_ptr, index_count, vertex_count);
+ surfaces.write[i].arrays[RS::ARRAY_INDEX] = indices;
+
+ for (int j = 0; j < surfaces[i].lods.size(); ++j) {
+ Surface::LOD &lod = surfaces.write[i].lods.write[j];
+ int *lod_indices_ptr = lod.indices.ptrw();
+ SurfaceTool::optimize_vertex_cache_func((unsigned int *)lod_indices_ptr, (const unsigned int *)lod_indices_ptr, lod.indices.size(), vertex_count);
+ }
+ // Concatenate indices for all LODs in the order of coarse->fine; this establishes the effective order of vertices,
+ // and is important to optimize for vertex fetch (all GPUs) and shading (Mali GPUs)
+ PackedInt32Array merged_indices;
+ for (int j = surfaces[i].lods.size() - 1; j >= 0; --j) {
+ merged_indices.append_array(surfaces[i].lods[j].indices);
+ }
+ merged_indices.append_array(indices);
+
+ // Generate remap array that establishes optimal vertex order according to the order of indices above.
+ Vector<uint32_t> remap;
+ remap.resize(vertex_count);
+ unsigned int new_vertex_count = SurfaceTool::optimize_vertex_fetch_remap_func(remap.ptrw(), (const unsigned int *)merged_indices.ptr(), merged_indices.size(), vertex_count);
+
+ // We need to remap all vertex and index arrays in lockstep according to the remap.
+ SurfaceTool::remap_index_func((unsigned int *)indices_ptr, (const unsigned int *)indices_ptr, index_count, remap.ptr());
surfaces.write[i].arrays[RS::ARRAY_INDEX] = indices;
+
+ for (int j = 0; j < surfaces[i].lods.size(); ++j) {
+ Surface::LOD &lod = surfaces.write[i].lods.write[j];
+ int *lod_indices_ptr = lod.indices.ptrw();
+ SurfaceTool::remap_index_func((unsigned int *)lod_indices_ptr, (const unsigned int *)lod_indices_ptr, lod.indices.size(), remap.ptr());
+ }
+
+ _remap_arrays(surfaces.write[i].arrays, remap, new_vertex_count);
+ for (int j = 0; j < surfaces[i].blend_shape_data.size(); j++) {
+ _remap_arrays(surfaces.write[i].blend_shape_data.write[j].arrays, remap, new_vertex_count);
+ }
+ }
+
+ if (shadow_mesh.is_valid()) {
+ shadow_mesh->optimize_indices();
}
}
@@ -217,9 +301,6 @@ void ImporterMesh::generate_lods(float p_normal_merge_angle, Array p_bone_transf
if (!SurfaceTool::simplify_with_attrib_func) {
return;
}
- if (!SurfaceTool::optimize_vertex_cache_func) {
- return;
- }
LocalVector<Transform3D> bone_transform_vector;
for (int i = 0; i < p_bone_transform_array.size(); i++) {
@@ -433,12 +514,6 @@ void ImporterMesh::generate_lods(float p_normal_merge_angle, Array p_bone_transf
}
surfaces.write[i].lods.sort_custom<Surface::LODComparator>();
-
- for (int j = 0; j < surfaces.write[i].lods.size(); j++) {
- Surface::LOD &lod = surfaces.write[i].lods.write[j];
- unsigned int *lod_indices_ptr = (unsigned int *)lod.indices.ptrw();
- SurfaceTool::optimize_vertex_cache_func(lod_indices_ptr, lod_indices_ptr, lod.indices.size(), vertex_count);
- }
}
}
@@ -576,10 +651,6 @@ void ImporterMesh::create_shadow_mesh() {
index_wptr[j] = vertex_remap[index];
}
- if (SurfaceTool::optimize_vertex_cache_func && surfaces[i].primitive == Mesh::PRIMITIVE_TRIANGLES) {
- SurfaceTool::optimize_vertex_cache_func((unsigned int *)index_wptr, (const unsigned int *)index_wptr, index_count, new_vertices.size());
- }
-
new_surface[RS::ARRAY_INDEX] = new_indices;
// Make sure the same LODs as the full version are used.
@@ -598,10 +669,6 @@ void ImporterMesh::create_shadow_mesh() {
index_wptr[k] = vertex_remap[index];
}
- if (SurfaceTool::optimize_vertex_cache_func && surfaces[i].primitive == Mesh::PRIMITIVE_TRIANGLES) {
- SurfaceTool::optimize_vertex_cache_func((unsigned int *)index_wptr, (const unsigned int *)index_wptr, index_count, new_vertices.size());
- }
-
lods[surfaces[i].lods[j].distance] = new_indices;
}
}
diff --git a/scene/resources/3d/importer_mesh.h b/scene/resources/3d/importer_mesh.h
index b4f3a03d27..8827cd7385 100644
--- a/scene/resources/3d/importer_mesh.h
+++ b/scene/resources/3d/importer_mesh.h
@@ -115,7 +115,7 @@ public:
void set_surface_material(int p_surface, const Ref<Material> &p_material);
- void optimize_indices_for_cache();
+ void optimize_indices();
void generate_lods(float p_normal_merge_angle, Array p_skin_pose_transform_array);
diff --git a/scene/resources/environment.cpp b/scene/resources/environment.cpp
index 88f830a8d1..75fba69915 100644
--- a/scene/resources/environment.cpp
+++ b/scene/resources/environment.cpp
@@ -132,10 +132,7 @@ int Environment::get_canvas_max_layer() const {
void Environment::set_camera_feed_id(int p_id) {
bg_camera_feed_id = p_id;
-// FIXME: Disabled during Vulkan refactoring, should be ported.
-#if 0
- RS::get_singleton()->environment_set_camera_feed_id(environment, camera_feed_id);
-#endif
+ RS::get_singleton()->environment_set_camera_feed_id(environment, bg_camera_feed_id);
}
int Environment::get_camera_feed_id() const {
diff --git a/scene/resources/font.cpp b/scene/resources/font.cpp
index a32bf672a4..910ab0a497 100644
--- a/scene/resources/font.cpp
+++ b/scene/resources/font.cpp
@@ -649,13 +649,13 @@ void FontFile::_convert_packed_8bit(Ref<Image> &p_source, int p_page, int p_sz)
wa[ofs_dst + 1] = r[ofs_src + 3];
}
}
- Ref<Image> img_r = memnew(Image(w, h, 0, Image::FORMAT_LA8, imgdata_r));
+ Ref<Image> img_r = memnew(Image(w, h, false, Image::FORMAT_LA8, imgdata_r));
set_texture_image(0, Vector2i(p_sz, 0), p_page * 4 + 0, img_r);
- Ref<Image> img_g = memnew(Image(w, h, 0, Image::FORMAT_LA8, imgdata_g));
+ Ref<Image> img_g = memnew(Image(w, h, false, Image::FORMAT_LA8, imgdata_g));
set_texture_image(0, Vector2i(p_sz, 0), p_page * 4 + 1, img_g);
- Ref<Image> img_b = memnew(Image(w, h, 0, Image::FORMAT_LA8, imgdata_b));
+ Ref<Image> img_b = memnew(Image(w, h, false, Image::FORMAT_LA8, imgdata_b));
set_texture_image(0, Vector2i(p_sz, 0), p_page * 4 + 2, img_b);
- Ref<Image> img_a = memnew(Image(w, h, 0, Image::FORMAT_LA8, imgdata_a));
+ Ref<Image> img_a = memnew(Image(w, h, false, Image::FORMAT_LA8, imgdata_a));
set_texture_image(0, Vector2i(p_sz, 0), p_page * 4 + 3, img_a);
}
@@ -740,22 +740,22 @@ void FontFile::_convert_packed_4bit(Ref<Image> &p_source, int p_page, int p_sz)
}
}
}
- Ref<Image> img_r = memnew(Image(w, h, 0, Image::FORMAT_LA8, imgdata_r));
+ Ref<Image> img_r = memnew(Image(w, h, false, Image::FORMAT_LA8, imgdata_r));
set_texture_image(0, Vector2i(p_sz, 0), p_page * 4 + 0, img_r);
- Ref<Image> img_g = memnew(Image(w, h, 0, Image::FORMAT_LA8, imgdata_g));
+ Ref<Image> img_g = memnew(Image(w, h, false, Image::FORMAT_LA8, imgdata_g));
set_texture_image(0, Vector2i(p_sz, 0), p_page * 4 + 1, img_g);
- Ref<Image> img_b = memnew(Image(w, h, 0, Image::FORMAT_LA8, imgdata_b));
+ Ref<Image> img_b = memnew(Image(w, h, false, Image::FORMAT_LA8, imgdata_b));
set_texture_image(0, Vector2i(p_sz, 0), p_page * 4 + 2, img_b);
- Ref<Image> img_a = memnew(Image(w, h, 0, Image::FORMAT_LA8, imgdata_a));
+ Ref<Image> img_a = memnew(Image(w, h, false, Image::FORMAT_LA8, imgdata_a));
set_texture_image(0, Vector2i(p_sz, 0), p_page * 4 + 3, img_a);
- Ref<Image> img_ro = memnew(Image(w, h, 0, Image::FORMAT_LA8, imgdata_ro));
+ Ref<Image> img_ro = memnew(Image(w, h, false, Image::FORMAT_LA8, imgdata_ro));
set_texture_image(0, Vector2i(p_sz, 1), p_page * 4 + 0, img_ro);
- Ref<Image> img_go = memnew(Image(w, h, 0, Image::FORMAT_LA8, imgdata_go));
+ Ref<Image> img_go = memnew(Image(w, h, false, Image::FORMAT_LA8, imgdata_go));
set_texture_image(0, Vector2i(p_sz, 1), p_page * 4 + 1, img_go);
- Ref<Image> img_bo = memnew(Image(w, h, 0, Image::FORMAT_LA8, imgdata_bo));
+ Ref<Image> img_bo = memnew(Image(w, h, false, Image::FORMAT_LA8, imgdata_bo));
set_texture_image(0, Vector2i(p_sz, 1), p_page * 4 + 2, img_bo);
- Ref<Image> img_ao = memnew(Image(w, h, 0, Image::FORMAT_LA8, imgdata_ao));
+ Ref<Image> img_ao = memnew(Image(w, h, false, Image::FORMAT_LA8, imgdata_ao));
set_texture_image(0, Vector2i(p_sz, 1), p_page * 4 + 3, img_ao);
}
@@ -808,10 +808,10 @@ void FontFile::_convert_rgba_4bit(Ref<Image> &p_source, int p_page, int p_sz) {
}
}
}
- Ref<Image> img_g = memnew(Image(w, h, 0, Image::FORMAT_RGBA8, imgdata_g));
+ Ref<Image> img_g = memnew(Image(w, h, false, Image::FORMAT_RGBA8, imgdata_g));
set_texture_image(0, Vector2i(p_sz, 0), p_page, img_g);
- Ref<Image> img_o = memnew(Image(w, h, 0, Image::FORMAT_RGBA8, imgdata_o));
+ Ref<Image> img_o = memnew(Image(w, h, false, Image::FORMAT_RGBA8, imgdata_o));
set_texture_image(0, Vector2i(p_sz, 1), p_page, img_o);
}
@@ -840,7 +840,7 @@ void FontFile::_convert_mono_8bit(Ref<Image> &p_source, int p_page, int p_ch, in
wg[ofs_dst + 1] = r[ofs_src + p_ch];
}
}
- Ref<Image> img_g = memnew(Image(w, h, 0, Image::FORMAT_LA8, imgdata_g));
+ Ref<Image> img_g = memnew(Image(w, h, false, Image::FORMAT_LA8, imgdata_g));
set_texture_image(0, Vector2i(p_sz, p_ol), p_page, img_g);
}
@@ -880,10 +880,10 @@ void FontFile::_convert_mono_4bit(Ref<Image> &p_source, int p_page, int p_ch, in
}
}
}
- Ref<Image> img_g = memnew(Image(w, h, 0, Image::FORMAT_LA8, imgdata_g));
+ Ref<Image> img_g = memnew(Image(w, h, false, Image::FORMAT_LA8, imgdata_g));
set_texture_image(0, Vector2i(p_sz, 0), p_page, img_g);
- Ref<Image> img_o = memnew(Image(w, h, 0, Image::FORMAT_LA8, imgdata_o));
+ Ref<Image> img_o = memnew(Image(w, h, false, Image::FORMAT_LA8, imgdata_o));
set_texture_image(0, Vector2i(p_sz, p_ol), p_page, img_o);
}
diff --git a/scene/resources/surface_tool.cpp b/scene/resources/surface_tool.cpp
index 6d74e3472f..7ddf98f0f6 100644
--- a/scene/resources/surface_tool.cpp
+++ b/scene/resources/surface_tool.cpp
@@ -35,10 +35,10 @@
#define EQ_VERTEX_DIST 0.00001
SurfaceTool::OptimizeVertexCacheFunc SurfaceTool::optimize_vertex_cache_func = nullptr;
+SurfaceTool::OptimizeVertexFetchRemapFunc SurfaceTool::optimize_vertex_fetch_remap_func = nullptr;
SurfaceTool::SimplifyFunc SurfaceTool::simplify_func = nullptr;
SurfaceTool::SimplifyWithAttribFunc SurfaceTool::simplify_with_attrib_func = nullptr;
SurfaceTool::SimplifyScaleFunc SurfaceTool::simplify_scale_func = nullptr;
-SurfaceTool::SimplifySloppyFunc SurfaceTool::simplify_sloppy_func = nullptr;
SurfaceTool::GenerateRemapFunc SurfaceTool::generate_remap_func = nullptr;
SurfaceTool::RemapVertexFunc SurfaceTool::remap_vertex_func = nullptr;
SurfaceTool::RemapIndexFunc SurfaceTool::remap_index_func = nullptr;
diff --git a/scene/resources/surface_tool.h b/scene/resources/surface_tool.h
index e7113bf820..a29fed2533 100644
--- a/scene/resources/surface_tool.h
+++ b/scene/resources/surface_tool.h
@@ -92,14 +92,14 @@ public:
typedef void (*OptimizeVertexCacheFunc)(unsigned int *destination, const unsigned int *indices, size_t index_count, size_t vertex_count);
static OptimizeVertexCacheFunc optimize_vertex_cache_func;
+ typedef size_t (*OptimizeVertexFetchRemapFunc)(unsigned int *destination, const unsigned int *indices, size_t index_count, size_t vertex_count);
+ static OptimizeVertexFetchRemapFunc optimize_vertex_fetch_remap_func;
typedef size_t (*SimplifyFunc)(unsigned int *destination, const unsigned int *indices, size_t index_count, const float *vertex_positions, size_t vertex_count, size_t vertex_positions_stride, size_t target_index_count, float target_error, unsigned int options, float *r_error);
static SimplifyFunc simplify_func;
typedef size_t (*SimplifyWithAttribFunc)(unsigned int *destination, const unsigned int *indices, size_t index_count, const float *vertex_data, size_t vertex_count, size_t vertex_stride, const float *attributes, size_t attribute_stride, const float *attribute_weights, size_t attribute_count, const unsigned char *vertex_lock, size_t target_index_count, float target_error, unsigned int options, float *result_error);
static SimplifyWithAttribFunc simplify_with_attrib_func;
typedef float (*SimplifyScaleFunc)(const float *vertex_positions, size_t vertex_count, size_t vertex_positions_stride);
static SimplifyScaleFunc simplify_scale_func;
- typedef size_t (*SimplifySloppyFunc)(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);
- static SimplifySloppyFunc simplify_sloppy_func;
typedef size_t (*GenerateRemapFunc)(unsigned int *destination, const unsigned int *indices, size_t index_count, const void *vertices, size_t vertex_count, size_t vertex_size);
static GenerateRemapFunc generate_remap_func;
typedef void (*RemapVertexFunc)(void *destination, const void *vertices, size_t vertex_count, size_t vertex_size, const unsigned int *remap);
@@ -224,7 +224,9 @@ public:
void clear();
- LocalVector<Vertex> &get_vertex_array() { return vertex_array; }
+ LocalVector<Vertex> &get_vertex_array() {
+ return vertex_array;
+ }
void create_from_triangle_arrays(const Array &p_arrays);
void create_from_arrays(const Array &p_arrays, Mesh::PrimitiveType p_primitive_type = Mesh::PRIMITIVE_TRIANGLES);
diff --git a/scene/resources/visual_shader.cpp b/scene/resources/visual_shader.cpp
index 9f04ff6761..fc5fcfe670 100644
--- a/scene/resources/visual_shader.cpp
+++ b/scene/resources/visual_shader.cpp
@@ -1923,13 +1923,13 @@ void VisualShader::_get_property_list(List<PropertyInfo> *p_list) const {
}
for (const KeyValue<String, Varying> &E : varyings) {
- p_list->push_back(PropertyInfo(Variant::STRING, vformat("%s/%s", PNAME("varyings"), E.key), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR));
+ p_list->push_back(PropertyInfo(Variant::STRING, vformat("%s/%s", "varyings", E.key), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR));
}
#ifdef TOOLS_ENABLED
if (Engine::get_singleton()->is_editor_hint()) {
for (const KeyValue<String, Variant> &E : preview_params) {
- p_list->push_back(PropertyInfo(Variant::STRING, vformat("%s/%s", PNAME("preview_params"), E.key), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR));
+ p_list->push_back(PropertyInfo(Variant::STRING, vformat("%s/%s", "preview_params", E.key), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR));
}
}
#endif // TOOLS_ENABLED
diff --git a/servers/audio/audio_stream.cpp b/servers/audio/audio_stream.cpp
index 30e98cc0e9..be10b6d4c8 100644
--- a/servers/audio/audio_stream.cpp
+++ b/servers/audio/audio_stream.cpp
@@ -283,7 +283,7 @@ double AudioStream::get_bpm() const {
}
bool AudioStream::has_loop() const {
- bool ret = 0;
+ bool ret = false;
GDVIRTUAL_CALL(_has_loop, ret);
return ret;
}
diff --git a/servers/audio_server.cpp b/servers/audio_server.cpp
index e553d69df4..098343f112 100644
--- a/servers/audio_server.cpp
+++ b/servers/audio_server.cpp
@@ -373,10 +373,14 @@ void AudioServer::_mix_step() {
bus->soloed = false;
}
}
+ // This is legacy code from 3.x that allows video players and other audio sources that do not implement AudioStreamPlayback to output audio.
for (CallbackItem *ci : mix_callback_list) {
ci->callback(ci->userdata);
}
+ // Main mixing loop for audio streams.
+ // The basic idea here is to copy the samples returned by the AudioStreamPlayback's mix function into the audio buffers,
+ // while always maintaining a lookahead buffer of size LOOKAHEAD_BUFFER_SIZE to allow fade-outs for sudden stoppages.
for (AudioStreamPlaybackListNode *playback : playback_list) {
// Paused streams are no-ops. Don't even mix audio from the stream playback.
if (playback->state.load() == AudioStreamPlaybackListNode::PAUSED) {
@@ -387,22 +391,26 @@ void AudioServer::_mix_step() {
continue;
}
+ // If `fading_out` is true, we're in the process of fading out the stream playback.
+ // TODO: Currently this sets the volume of the stream to 0 which creates a linear interpolation between its previous volume and silence.
+ // A more punchy option for fading out could be to just use the lookahead buffer.
bool fading_out = playback->state.load() == AudioStreamPlaybackListNode::FADE_OUT_TO_DELETION || playback->state.load() == AudioStreamPlaybackListNode::FADE_OUT_TO_PAUSE;
AudioFrame *buf = mix_buffer.ptrw();
- // Copy the lookeahead buffer into the mix buffer.
+ // Copy the old contents of the lookahead buffer into the beginning of the mix buffer.
for (int i = 0; i < LOOKAHEAD_BUFFER_SIZE; i++) {
buf[i] = playback->lookahead[i];
}
- // Mix the audio stream
+ // Mix the audio stream.
unsigned int mixed_frames = playback->stream_playback->mix(&buf[LOOKAHEAD_BUFFER_SIZE], playback->pitch_scale.get(), buffer_size);
if (tag_used_audio_streams && playback->stream_playback->is_playing()) {
playback->stream_playback->tag_used_streams();
}
+ // Check to see if the stream has run out of samples.
if (mixed_frames != buffer_size) {
// We know we have at least the size of our lookahead buffer for fade-out purposes.
@@ -418,42 +426,52 @@ void AudioServer::_mix_step() {
new_state = AudioStreamPlaybackListNode::AWAITING_DELETION;
playback->state.store(new_state);
} else {
- // Move the last little bit of what we just mixed into our lookahead buffer.
+ // Move the last little bit of what we just mixed into our lookahead buffer for the next call to _mix_step.
for (int i = 0; i < LOOKAHEAD_BUFFER_SIZE; i++) {
playback->lookahead[i] = buf[buffer_size + i];
}
}
- AudioStreamPlaybackBusDetails *ptr = playback->bus_details.load();
- ERR_FAIL_NULL(ptr);
- // By putting null into the bus details pointers, we're taking ownership of their memory for the duration of this mix.
- AudioStreamPlaybackBusDetails bus_details = *ptr;
+ // Get the bus details for this playback. This contains information about which buses the playback is assigned to and the volume of the playback on each bus.
+ AudioStreamPlaybackBusDetails *bus_details_ptr = playback->bus_details.load();
+ ERR_FAIL_NULL(bus_details_ptr);
+ // Make a copy of the bus details so we can modify it without worrying about other threads.
+ AudioStreamPlaybackBusDetails bus_details = *bus_details_ptr;
// Mix to any active buses.
for (int idx = 0; idx < MAX_BUSES_PER_PLAYBACK; idx++) {
if (!bus_details.bus_active[idx]) {
continue;
}
+ // This is the AudioServer-internal index of the bus we're mixing to in this step of the loop. Not to be confused with `idx` which is an index into `AudioStreamPlaybackBusDetails` member var arrays.
int bus_idx = thread_find_bus_index(bus_details.bus[idx]);
+ // It's important to know whether or not this bus was active in the previous mix step of this stream. If it was, we need to perform volume interpolation to avoid pops.
int prev_bus_idx = -1;
for (int search_idx = 0; search_idx < MAX_BUSES_PER_PLAYBACK; search_idx++) {
if (!playback->prev_bus_details->bus_active[search_idx]) {
continue;
}
+ // If the StringNames of the buses match, we've found the previous bus index. This indicates that this playback mixed to `prev_bus_details->bus[prev_bus_index]` in the previous mix step, which gives us a way to look up the playback's previous volume.
if (playback->prev_bus_details->bus[search_idx].hash() == bus_details.bus[idx].hash()) {
prev_bus_idx = search_idx;
+ break;
}
}
+ // It's now time to mix to the bus. We do this by going through each channel of the bus and mixing to it.
+ // The channels correspond to output channels of the audio device, e.g. stereo or 5.1. To reduce needless nesting, this is done with a helper method named `_mix_step_for_channel`.
for (int channel_idx = 0; channel_idx < channel_count; channel_idx++) {
AudioFrame *channel_buf = thread_get_channel_mix_buffer(bus_idx, channel_idx);
+ // TODO: This `fading_out` check could be replaced with with an exponential fadeout of the samples from the lookahead buffer for more punchy results.
if (fading_out) {
bus_details.volume[idx][channel_idx] = AudioFrame(0, 0);
}
AudioFrame channel_vol = bus_details.volume[idx][channel_idx];
- AudioFrame prev_channel_vol = AudioFrame(0, 0);
+ // If this bus was not active in the previous mix step, we want to start playback at the full volume to avoid crushing transients.
+ AudioFrame prev_channel_vol = channel_vol;
+ // If this bus was active in the previous mix step, we need to interpolate between the previous volume and the current volume to avoid pops. Set `prev_channel_volume` accordingly.
if (prev_bus_idx != -1) {
prev_channel_vol = playback->prev_bus_details->volume[prev_bus_idx][channel_idx];
}
@@ -482,7 +500,7 @@ void AudioServer::_mix_step() {
for (int channel_idx = 0; channel_idx < channel_count; channel_idx++) {
AudioFrame *channel_buf = thread_get_channel_mix_buffer(bus_idx, channel_idx);
AudioFrame prev_channel_vol = playback->prev_bus_details->volume[idx][channel_idx];
- // Fade out to silence
+ // Fade out to silence. This could be replaced with an exponential fadeout of the samples from the lookahead buffer for more punchy results.
_mix_step_for_channel(channel_buf, buf, prev_channel_vol, AudioFrame(0, 0), playback->attenuation_filter_cutoff_hz.get(), playback->highshelf_gain.get(), &playback->filter_process[channel_idx * 2], &playback->filter_process[channel_idx * 2 + 1]);
}
}
@@ -503,15 +521,12 @@ void AudioServer::_mix_step() {
switch (playback->state.load()) {
case AudioStreamPlaybackListNode::AWAITING_DELETION:
case AudioStreamPlaybackListNode::FADE_OUT_TO_DELETION:
+ // Remove the playback from the list.
_delete_stream_playback_list_node(playback);
break;
case AudioStreamPlaybackListNode::FADE_OUT_TO_PAUSE: {
// Pause the stream.
- AudioStreamPlaybackListNode::PlaybackState old_state, new_state;
- do {
- old_state = playback->state.load();
- new_state = AudioStreamPlaybackListNode::PAUSED;
- } while (!playback->state.compare_exchange_strong(/* expected= */ old_state, new_state));
+ playback->state.store(AudioStreamPlaybackListNode::PAUSED);
} break;
case AudioStreamPlaybackListNode::PLAYING:
case AudioStreamPlaybackListNode::PAUSED:
@@ -520,13 +535,13 @@ void AudioServer::_mix_step() {
}
}
+ // Now that all of the buses have their audio sources mixed into them, we can process the effects and bus sends.
for (int i = buses.size() - 1; i >= 0; i--) {
- //go bus by bus
Bus *bus = buses[i];
for (int k = 0; k < bus->channels.size(); k++) {
if (bus->channels[k].active && !bus->channels[k].used) {
- //buffer was not used, but it's still active, so it must be cleaned
+ // Buffer was not used, but it's still active, so it must be cleaned.
AudioFrame *buf = bus->channels.write[k].buffer.ptrw();
for (uint32_t j = 0; j < buffer_size; j++) {
@@ -535,7 +550,7 @@ void AudioServer::_mix_step() {
}
}
- //process effects
+ // Process effects.
if (!bus->bypass) {
for (int j = 0; j < bus->effects.size(); j++) {
if (!bus->effects[j].enabled) {
@@ -553,7 +568,7 @@ void AudioServer::_mix_step() {
bus->channels.write[k].effect_instances.write[j]->process(bus->channels[k].buffer.ptr(), temp_buffer.write[k].ptrw(), buffer_size);
}
- //swap buffers, so internal buffer always has the right data
+ // Swap buffers, so internal buffer always has the right data.
for (int k = 0; k < bus->channels.size(); k++) {
if (!(buses[i]->channels[k].active || bus->channels[k].effect_instances[j]->process_silence())) {
continue;
@@ -567,17 +582,17 @@ void AudioServer::_mix_step() {
}
}
- //process send
+ // Process send.
Bus *send = nullptr;
if (i > 0) {
- //everything has a send save for master bus
+ // Everything has a send except for the master bus.
if (!bus_map.has(bus->send)) {
send = buses[0];
} else {
send = bus_map[bus->send];
- if (send->index_cache >= bus->index_cache) { //invalid, send to master
+ if (send->index_cache >= bus->index_cache) { // Invalid, send to master.
send = buses[0];
}
}
@@ -605,7 +620,7 @@ void AudioServer::_mix_step() {
}
}
- //apply volume and compute peak
+ // Apply volume and compute peak.
for (uint32_t j = 0; j < buffer_size; j++) {
buf[j] *= volume;
@@ -622,7 +637,7 @@ void AudioServer::_mix_step() {
bus->channels.write[k].peak_volume = AudioFrame(Math::linear_to_db(peak.left + AUDIO_PEAK_OFFSET), Math::linear_to_db(peak.right + AUDIO_PEAK_OFFSET));
if (!bus->channels[k].used) {
- //see if any audio is contained, because channel was not used
+ // See if any audio is contained, because channel was not used.
if (MAX(peak.right, peak.left) > Math::db_to_linear(channel_disable_threshold_db)) {
bus->channels.write[k].last_mix_with_audio = mix_frames;
@@ -633,7 +648,7 @@ void AudioServer::_mix_step() {
}
if (send) {
- //if not master bus, send
+ // If not master bus, send.
AudioFrame *target_buf = thread_get_channel_mix_buffer(send->index_cache, k);
for (uint32_t j = 0; j < buffer_size; j++) {
@@ -648,6 +663,7 @@ void AudioServer::_mix_step() {
}
void AudioServer::_mix_step_for_channel(AudioFrame *p_out_buf, AudioFrame *p_source_buf, AudioFrame p_vol_start, AudioFrame p_vol_final, float p_attenuation_filter_cutoff_hz, float p_highshelf_gain, AudioFilterSW::Processor *p_processor_l, AudioFilterSW::Processor *p_processor_r) {
+ // TODO: In the future it could be nice to replace all of these hardcoded effects with something a bit cleaner and more flexible, but for now this is what we do to support 3D audio players.
if (p_highshelf_gain != 0) {
AudioFilterSW filter;
filter.set_mode(AudioFilterSW::HIGHSHELF);
@@ -667,7 +683,7 @@ void AudioServer::_mix_step_for_channel(AudioFrame *p_out_buf, AudioFrame *p_sou
p_processor_r->update_coeffs(buffer_size);
for (unsigned int frame_idx = 0; frame_idx < buffer_size; frame_idx++) {
- // Make this buffer size invariant if buffer_size ever becomes a project setting.
+ // TODO: Make lerp speed buffer-size-invariant if buffer_size ever becomes a project setting to avoid very small buffer sizes causing pops due to too-fast lerps.
float lerp_param = (float)frame_idx / buffer_size;
AudioFrame vol = p_vol_final * lerp_param + (1 - lerp_param) * p_vol_start;
AudioFrame mixed = vol * p_source_buf[frame_idx];
@@ -678,7 +694,7 @@ void AudioServer::_mix_step_for_channel(AudioFrame *p_out_buf, AudioFrame *p_sou
} else {
for (unsigned int frame_idx = 0; frame_idx < buffer_size; frame_idx++) {
- // Make this buffer size invariant if buffer_size ever becomes a project setting.
+ // TODO: Make lerp speed buffer-size-invariant if buffer_size ever becomes a project setting to avoid very small buffer sizes causing pops due to too-fast lerps.
float lerp_param = (float)frame_idx / buffer_size;
p_out_buf[frame_idx] += (p_vol_final * lerp_param + (1 - lerp_param) * p_vol_start) * p_source_buf[frame_idx];
}
@@ -703,6 +719,7 @@ void AudioServer::_delete_stream_playback(Ref<AudioStreamPlayback> p_playback) {
}
void AudioServer::_delete_stream_playback_list_node(AudioStreamPlaybackListNode *p_playback_node) {
+ // Remove the playback from the list, registering a destructor to be run on the main thread.
playback_list.erase(p_playback_node, [](AudioStreamPlaybackListNode *p) {
delete p->prev_bus_details;
delete p->bus_details.load();
@@ -1473,7 +1490,9 @@ void AudioServer::init_channels_and_buffers() {
void AudioServer::init() {
channel_disable_threshold_db = GLOBAL_DEF_RST("audio/buses/channel_disable_threshold_db", -60.0);
channel_disable_frames = float(GLOBAL_DEF_RST(PropertyInfo(Variant::FLOAT, "audio/buses/channel_disable_time", PROPERTY_HINT_RANGE, "0,5,0.01,or_greater"), 2.0)) * get_mix_rate();
- buffer_size = 512; //hardcoded for now
+ // TODO: Buffer size is hardcoded for now. This would be really nice to have as a project setting because currently it limits audio latency to an absolute minimum of 11ms with default mix rate, but there's some additional work required to make that happen. See TODOs in `_mix_step_for_channel`.
+ // When this becomes a project setting, it should be specified in milliseconds rather than raw sample count, because 512 samples at 192khz is shorter than it is at 48khz, for example.
+ buffer_size = 512;
init_channels_and_buffers();
diff --git a/servers/audio_server.h b/servers/audio_server.h
index bb8aad52c4..f9cd5906a7 100644
--- a/servers/audio_server.h
+++ b/servers/audio_server.h
@@ -273,6 +273,14 @@ private:
};
struct AudioStreamPlaybackListNode {
+ // The state machine for audio stream playbacks is as follows:
+ // 1. The playback is created and added to the playback list in the playing state.
+ // 2. The playback is (maybe) paused, and the state is set to FADE_OUT_TO_PAUSE.
+ // 2.1. The playback is mixed after being paused, and the audio server thread atomically sets the state to PAUSED after performing a brief fade-out.
+ // 3. The playback is (maybe) deleted, and the state is set to FADE_OUT_TO_DELETION.
+ // 3.1. The playback is mixed after being deleted, and the audio server thread atomically sets the state to AWAITING_DELETION after performing a brief fade-out.
+ // NOTE: The playback is not deallocated at this time because allocation and deallocation are not realtime-safe.
+ // 4. The playback is removed and deallocated on the main thread using the SafeList maybe_cleanup method.
enum PlaybackState {
PAUSED = 0, // Paused. Keep this stream playback around though so it can be restarted.
PLAYING = 1, // Playing. Fading may still be necessary if volume changes!
diff --git a/servers/camera/camera_feed.cpp b/servers/camera/camera_feed.cpp
index 355728beea..00deef0a7f 100644
--- a/servers/camera/camera_feed.cpp
+++ b/servers/camera/camera_feed.cpp
@@ -52,6 +52,8 @@ void CameraFeed::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_rgb_image", "rgb_image"), &CameraFeed::set_rgb_image);
ClassDB::bind_method(D_METHOD("set_ycbcr_image", "ycbcr_image"), &CameraFeed::set_ycbcr_image);
+ ClassDB::bind_method(D_METHOD("set_external", "width", "height"), &CameraFeed::set_external);
+ ClassDB::bind_method(D_METHOD("get_texture_tex_id", "feed_image_type"), &CameraFeed::get_texture_tex_id);
ClassDB::bind_method(D_METHOD("get_datatype"), &CameraFeed::get_datatype);
@@ -70,6 +72,7 @@ void CameraFeed::_bind_methods() {
BIND_ENUM_CONSTANT(FEED_RGB);
BIND_ENUM_CONSTANT(FEED_YCBCR);
BIND_ENUM_CONSTANT(FEED_YCBCR_SEP);
+ BIND_ENUM_CONSTANT(FEED_EXTERNAL);
BIND_ENUM_CONSTANT(FEED_UNSPECIFIED);
BIND_ENUM_CONSTANT(FEED_FRONT);
@@ -139,6 +142,10 @@ RID CameraFeed::get_texture(CameraServer::FeedImage p_which) {
return texture[p_which];
}
+uint64_t CameraFeed::get_texture_tex_id(CameraServer::FeedImage p_which) {
+ return RenderingServer::get_singleton()->texture_get_native_handle(texture[p_which]);
+}
+
CameraFeed::CameraFeed() {
// initialize our feed
id = CameraServer::get_singleton()->get_free_id();
@@ -254,6 +261,19 @@ void CameraFeed::set_ycbcr_images(const Ref<Image> &p_y_img, const Ref<Image> &p
}
}
+void CameraFeed::set_external(int p_width, int p_height) {
+ if ((base_width != p_width) || (base_height != p_height)) {
+ // We're assuming here that our camera image doesn't change around formats etc, allocate the whole lot...
+ base_width = p_width;
+ base_height = p_height;
+
+ auto new_texture = RenderingServer::get_singleton()->texture_external_create(p_width, p_height, 0);
+ RenderingServer::get_singleton()->texture_replace(texture[CameraServer::FEED_YCBCR_IMAGE], new_texture);
+ }
+
+ datatype = CameraFeed::FEED_EXTERNAL;
+}
+
bool CameraFeed::activate_feed() {
// nothing to do here
return true;
diff --git a/servers/camera/camera_feed.h b/servers/camera/camera_feed.h
index 7063d295d2..506b956b59 100644
--- a/servers/camera/camera_feed.h
+++ b/servers/camera/camera_feed.h
@@ -51,7 +51,8 @@ public:
FEED_NOIMAGE, // we don't have an image yet
FEED_RGB, // our texture will contain a normal RGB texture that can be used directly
FEED_YCBCR, // our texture will contain a YCbCr texture that needs to be converted to RGB before output
- FEED_YCBCR_SEP // our camera is split into two textures, first plane contains Y data, second plane contains CbCr data
+ FEED_YCBCR_SEP, // our camera is split into two textures, first plane contains Y data, second plane contains CbCr data
+ FEED_EXTERNAL, // specific for android atm, camera feed is managed externally, assumed RGB for now
};
enum FeedPosition {
@@ -106,6 +107,7 @@ public:
void set_transform(const Transform2D &p_transform);
RID get_texture(CameraServer::FeedImage p_which);
+ uint64_t get_texture_tex_id(CameraServer::FeedImage p_which);
CameraFeed();
CameraFeed(String p_name, FeedPosition p_position = CameraFeed::FEED_UNSPECIFIED);
@@ -115,6 +117,7 @@ public:
void set_rgb_image(const Ref<Image> &p_rgb_img);
void set_ycbcr_image(const Ref<Image> &p_ycbcr_img);
void set_ycbcr_images(const Ref<Image> &p_y_img, const Ref<Image> &p_cbcr_img);
+ void set_external(int p_width, int p_height);
virtual bool set_format(int p_index, const Dictionary &p_parameters);
virtual Array get_formats() const;
diff --git a/servers/display_server.h b/servers/display_server.h
index 10695e5573..bb354f3f9c 100644
--- a/servers/display_server.h
+++ b/servers/display_server.h
@@ -363,13 +363,6 @@ public:
virtual void screen_set_orientation(ScreenOrientation p_orientation, int p_screen = SCREEN_OF_MAIN_WINDOW);
virtual ScreenOrientation screen_get_orientation(int p_screen = SCREEN_OF_MAIN_WINDOW) const;
- // Note: The "internal" current orientation is not necessarily the current orientation and will often be 0 for most platforms.
- //
- // Some Android GPUs come with a HW-based rotator which means the screen gets rotated for free to
- // whatever orientation the device is currently facing. But many Android GPUs emulate it via SW instead,
- // which costs performance and power. This value is an optimization that tells Godot's compositor how to
- // rotate the render texture before presenting to screen so that Android's compositor doesn't have to.
- virtual int screen_get_internal_current_rotation(int p_screen = SCREEN_OF_MAIN_WINDOW) const { return 0; }
virtual void screen_set_keep_on(bool p_enable); //disable screensaver
virtual bool screen_is_kept_on() const;
diff --git a/servers/rendering/renderer_canvas_cull.cpp b/servers/rendering/renderer_canvas_cull.cpp
index 90fc2999e2..51f7d21cfc 100644
--- a/servers/rendering/renderer_canvas_cull.cpp
+++ b/servers/rendering/renderer_canvas_cull.cpp
@@ -614,8 +614,9 @@ void RendererCanvasCull::canvas_item_set_visibility_layer(RID p_item, uint32_t p
uint32_t RendererCanvasCull::canvas_item_get_visibility_layer(RID p_item) {
Item *canvas_item = canvas_item_owner.get_or_null(p_item);
- if (!canvas_item)
+ if (!canvas_item) {
return 0;
+ }
return canvas_item->visibility_layer;
}
diff --git a/servers/rendering/renderer_rd/effects/copy_effects.cpp b/servers/rendering/renderer_rd/effects/copy_effects.cpp
index 9089d9482e..f20591c224 100644
--- a/servers/rendering/renderer_rd/effects/copy_effects.cpp
+++ b/servers/rendering/renderer_rd/effects/copy_effects.cpp
@@ -1004,15 +1004,19 @@ void CopyEffects::copy_cubemap_to_dp(RID p_source_rd_texture, RID p_dst_framebuf
MaterialStorage *material_storage = MaterialStorage::get_singleton();
ERR_FAIL_NULL(material_storage);
+ Rect2i screen_rect;
+ float atlas_width = p_dst_size.width / p_rect.size.width;
+ float atlas_height = p_dst_size.height / p_rect.size.height;
+ screen_rect.position.x = (int32_t)(Math::round(p_rect.position.x * atlas_width));
+ screen_rect.position.y = (int32_t)(Math::round(p_rect.position.y * atlas_height));
+ screen_rect.size.width = (int32_t)(Math::round(p_dst_size.width));
+ screen_rect.size.height = (int32_t)(Math::round(p_dst_size.height));
+
CopyToDPPushConstant push_constant;
- push_constant.screen_rect[0] = p_rect.position.x;
- push_constant.screen_rect[1] = p_rect.position.y;
- push_constant.screen_rect[2] = p_rect.size.width;
- push_constant.screen_rect[3] = p_rect.size.height;
push_constant.z_far = p_z_far;
push_constant.z_near = p_z_near;
- push_constant.texel_size[0] = 1.0f / p_dst_size.x;
- push_constant.texel_size[1] = 1.0f / p_dst_size.y;
+ push_constant.texel_size[0] = 1.0f / p_dst_size.width;
+ push_constant.texel_size[1] = 1.0f / p_dst_size.height;
push_constant.texel_size[0] *= p_dp_flip ? -1.0f : 1.0f; // Encode dp flip as x size sign
// setup our uniforms
@@ -1023,7 +1027,7 @@ void CopyEffects::copy_cubemap_to_dp(RID p_source_rd_texture, RID p_dst_framebuf
RID shader = cube_to_dp.shader.version_get_shader(cube_to_dp.shader_version, 0);
ERR_FAIL_COND(shader.is_null());
- RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_dst_framebuffer, RD::INITIAL_ACTION_DISCARD, RD::FINAL_ACTION_DISCARD, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_STORE);
+ RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_dst_framebuffer, RD::INITIAL_ACTION_DISCARD, RD::FINAL_ACTION_DISCARD, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_STORE, Vector<Color>(), 1.0f, 0, screen_rect);
RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, cube_to_dp.pipeline.get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(p_dst_framebuffer)));
RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 0, u_source_rd_texture), 0);
RD::get_singleton()->draw_list_bind_index_array(draw_list, material_storage->get_quad_index_array());
diff --git a/servers/rendering/renderer_rd/effects/copy_effects.h b/servers/rendering/renderer_rd/effects/copy_effects.h
index 8ec8e04073..12f7d25ef6 100644
--- a/servers/rendering/renderer_rd/effects/copy_effects.h
+++ b/servers/rendering/renderer_rd/effects/copy_effects.h
@@ -219,7 +219,6 @@ private:
float z_far;
float z_near;
float texel_size[2];
- float screen_rect[4];
};
struct CopyToDP {
diff --git a/servers/rendering/renderer_rd/environment/gi.cpp b/servers/rendering/renderer_rd/environment/gi.cpp
index 0ca16f59e3..cc8501e0e7 100644
--- a/servers/rendering/renderer_rd/environment/gi.cpp
+++ b/servers/rendering/renderer_rd/environment/gi.cpp
@@ -352,7 +352,7 @@ bool GI::voxel_gi_is_using_two_bounces(RID p_voxel_gi) const {
bool GI::voxel_gi_is_interior(RID p_voxel_gi) const {
VoxelGI *voxel_gi = voxel_gi_owner.get_or_null(p_voxel_gi);
- ERR_FAIL_NULL_V(voxel_gi, 0);
+ ERR_FAIL_NULL_V(voxel_gi, false);
return voxel_gi->interior;
}
diff --git a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp
index c1309eff48..73c2b8aa39 100644
--- a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp
+++ b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp
@@ -397,6 +397,10 @@ void RenderForwardClustered::_render_list_template(RenderingDevice::DrawListID p
RID xforms_uniform_set = surf->owner->transforms_uniform_set;
SceneShaderForwardClustered::ShaderSpecialization pipeline_specialization = p_params->base_specialization;
+ pipeline_specialization.multimesh = bool(surf->owner->base_flags & INSTANCE_DATA_FLAG_MULTIMESH);
+ pipeline_specialization.multimesh_format_2d = bool(surf->owner->base_flags & INSTANCE_DATA_FLAG_MULTIMESH_FORMAT_2D);
+ pipeline_specialization.multimesh_has_color = bool(surf->owner->base_flags & INSTANCE_DATA_FLAG_MULTIMESH_HAS_COLOR);
+ pipeline_specialization.multimesh_has_custom_data = bool(surf->owner->base_flags & INSTANCE_DATA_FLAG_MULTIMESH_HAS_CUSTOM_DATA);
if constexpr (p_pass_mode == PASS_MODE_COLOR) {
pipeline_specialization.use_light_soft_shadows = element_info.uses_softshadow;
@@ -1963,7 +1967,13 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co
sky.setup_sky(p_render_data->environment, rb, *p_render_data->lights, p_render_data->camera_attributes, 1, &projection, &eye_offset, p_render_data->scene_data->cam_transform, projection, screen_size, Vector2(0.0f, 0.0f), this);
} else {
- sky.setup_sky(p_render_data->environment, rb, *p_render_data->lights, p_render_data->camera_attributes, p_render_data->scene_data->view_count, p_render_data->scene_data->view_projection, p_render_data->scene_data->view_eye_offset, p_render_data->scene_data->cam_transform, p_render_data->scene_data->cam_projection, screen_size, p_render_data->scene_data->taa_jitter, this);
+ Projection projection = p_render_data->scene_data->cam_projection;
+ if (p_render_data->scene_data->cam_frustum) {
+ // Sky is drawn upside down, the frustum offset doesn't know the image is upside down so needs a flip.
+ projection[2].y = -projection[2].y;
+ }
+
+ sky.setup_sky(p_render_data->environment, rb, *p_render_data->lights, p_render_data->camera_attributes, p_render_data->scene_data->view_count, &projection, p_render_data->scene_data->view_eye_offset, p_render_data->scene_data->cam_transform, projection, screen_size, p_render_data->scene_data->taa_jitter, this);
}
sky_energy_multiplier *= bg_energy_multiplier;
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 c19e04d7b8..44f4013ded 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
@@ -376,6 +376,10 @@ void SceneShaderForwardClustered::ShaderData::_create_pipeline(PipelineKey p_pip
sc.type = RD::PIPELINE_SPECIALIZATION_CONSTANT_TYPE_INT;
specialization_constants.push_back(sc);
+ sc.constant_id = 1;
+ sc.int_value = p_pipeline_key.shader_specialization.packed_1;
+ specialization_constants.push_back(sc);
+
RID shader_rid = get_shader_variant(p_pipeline_key.version, p_pipeline_key.color_pass_flags, p_pipeline_key.ubershader);
ERR_FAIL_COND(shader_rid.is_null());
diff --git a/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.h b/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.h
index 014bdeba8d..379536b82c 100644
--- a/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.h
+++ b/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.h
@@ -119,7 +119,17 @@ public:
uint32_t packed_0;
};
- uint32_t packed_1;
+ union {
+ struct {
+ uint32_t multimesh : 1;
+ uint32_t multimesh_format_2d : 1;
+ uint32_t multimesh_has_color : 1;
+ uint32_t multimesh_has_custom_data : 1;
+ };
+
+ uint32_t packed_1;
+ };
+
uint32_t packed_2;
};
diff --git a/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp b/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp
index aae65bb97a..79fe54277a 100644
--- a/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp
+++ b/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp
@@ -913,14 +913,19 @@ void RenderForwardMobile::_render_scene(RenderDataRD *p_render_data, const Color
Color clear_color = p_default_bg_color;
bool load_color = false;
bool copy_canvas = false;
+ bool use_ambient_cubemap = false;
+ bool use_reflection_cubemap = false;
if (get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_OVERDRAW) {
clear_color = Color(0, 0, 0, 1); //in overdraw mode, BG should always be black
} else if (is_environment(p_render_data->environment)) {
+ RS::EnvironmentAmbientSource ambient_source = environment_get_ambient_source(p_render_data->environment);
RS::EnvironmentBG bg_mode = environment_get_background(p_render_data->environment);
float bg_energy_multiplier = environment_get_bg_energy_multiplier(p_render_data->environment);
bg_energy_multiplier *= environment_get_bg_intensity(p_render_data->environment);
RS::EnvironmentReflectionSource reflection_source = environment_get_reflection_source(p_render_data->environment);
+ use_ambient_cubemap = (ambient_source == RS::ENV_AMBIENT_SOURCE_BG && bg_mode == RS::ENV_BG_SKY) || ambient_source == RS::ENV_AMBIENT_SOURCE_SKY;
+ use_reflection_cubemap = (reflection_source == RS::ENV_REFLECTION_SOURCE_BG && bg_mode == RS::ENV_BG_SKY) || reflection_source == RS::ENV_REFLECTION_SOURCE_SKY;
if (p_render_data->camera_attributes.is_valid()) {
bg_energy_multiplier *= RSG::camera_attributes->camera_attributes_get_exposure_normalization_factor(p_render_data->camera_attributes);
@@ -965,7 +970,7 @@ void RenderForwardMobile::_render_scene(RenderDataRD *p_render_data, const Color
}
// setup sky if used for ambient, reflections, or background
- if (draw_sky || draw_sky_fog_only || (reflection_source == RS::ENV_REFLECTION_SOURCE_BG && bg_mode == RS::ENV_BG_SKY) || reflection_source == RS::ENV_REFLECTION_SOURCE_SKY || environment_get_ambient_source(p_render_data->environment) == RS::ENV_AMBIENT_SOURCE_SKY) {
+ if (draw_sky || draw_sky_fog_only || (reflection_source == RS::ENV_REFLECTION_SOURCE_BG && bg_mode == RS::ENV_BG_SKY) || reflection_source == RS::ENV_REFLECTION_SOURCE_SKY || ambient_source == RS::ENV_AMBIENT_SOURCE_SKY) {
RENDER_TIMESTAMP("Setup Sky");
RD::get_singleton()->draw_command_begin_label("Setup Sky");
@@ -978,7 +983,13 @@ void RenderForwardMobile::_render_scene(RenderDataRD *p_render_data, const Color
sky.setup_sky(p_render_data->environment, p_render_data->render_buffers, *p_render_data->lights, p_render_data->camera_attributes, 1, &projection, &eye_offset, p_render_data->scene_data->cam_transform, projection, screen_size, Vector2(0.0f, 0.0f), this);
} else {
- sky.setup_sky(p_render_data->environment, p_render_data->render_buffers, *p_render_data->lights, p_render_data->camera_attributes, p_render_data->scene_data->view_count, p_render_data->scene_data->view_projection, p_render_data->scene_data->view_eye_offset, p_render_data->scene_data->cam_transform, p_render_data->scene_data->cam_projection, screen_size, p_render_data->scene_data->taa_jitter, this);
+ Projection projection = p_render_data->scene_data->cam_projection;
+ if (p_render_data->scene_data->cam_frustum) {
+ // Sky is drawn upside down, the frustum offset doesn't know the image is upside down so needs a flip.
+ projection[2].y = -projection[2].y;
+ }
+
+ sky.setup_sky(p_render_data->environment, p_render_data->render_buffers, *p_render_data->lights, p_render_data->camera_attributes, p_render_data->scene_data->view_count, &projection, p_render_data->scene_data->view_eye_offset, p_render_data->scene_data->cam_transform, projection, screen_size, p_render_data->scene_data->taa_jitter, this);
}
sky_energy_multiplier *= bg_energy_multiplier;
@@ -1010,13 +1021,8 @@ void RenderForwardMobile::_render_scene(RenderDataRD *p_render_data, const Color
SceneShaderForwardMobile::ShaderSpecialization base_specialization = scene_shader.default_specialization;
{
- //figure out spec constants
-
- if (p_render_data->directional_light_count > 0) {
- base_specialization.use_directional_soft_shadows = p_render_data->directional_light_soft_shadows;
- } else {
- base_specialization.disable_directional_lights = true;
- }
+ base_specialization.use_directional_soft_shadows = p_render_data->directional_light_count > 0 ? p_render_data->directional_light_soft_shadows : false;
+ base_specialization.directional_lights = p_render_data->directional_light_count;
if (!is_environment(p_render_data->environment) || !environment_get_fog_enabled(p_render_data->environment)) {
base_specialization.disable_fog = true;
@@ -1025,6 +1031,10 @@ void RenderForwardMobile::_render_scene(RenderDataRD *p_render_data, const Color
if (p_render_data->environment.is_valid() && environment_get_fog_mode(p_render_data->environment) == RS::EnvironmentFogMode::ENV_FOG_MODE_DEPTH) {
base_specialization.use_depth_fog = true;
}
+
+ base_specialization.scene_use_ambient_cubemap = use_ambient_cubemap;
+ base_specialization.scene_use_reflection_cubemap = use_reflection_cubemap;
+ base_specialization.scene_roughness_limiter_enabled = p_render_data->render_buffers.is_valid() && screen_space_roughness_limiter_is_active();
}
{
@@ -2146,7 +2156,10 @@ void RenderForwardMobile::_render_list_template(RenderingDevice::DrawListID p_dr
}
SceneShaderForwardMobile::ShaderSpecialization pipeline_specialization = p_params->base_specialization;
- pipeline_specialization.is_multimesh = bool(inst->flags_cache & INSTANCE_DATA_FLAG_MULTIMESH);
+ pipeline_specialization.multimesh = bool(inst->flags_cache & INSTANCE_DATA_FLAG_MULTIMESH);
+ pipeline_specialization.multimesh_format_2d = bool(inst->flags_cache & INSTANCE_DATA_FLAG_MULTIMESH_FORMAT_2D);
+ pipeline_specialization.multimesh_has_color = bool(inst->flags_cache & INSTANCE_DATA_FLAG_MULTIMESH_HAS_COLOR);
+ pipeline_specialization.multimesh_has_custom_data = bool(inst->flags_cache & INSTANCE_DATA_FLAG_MULTIMESH_HAS_CUSTOM_DATA);
SceneState::PushConstant push_constant;
push_constant.base_index = i + p_params->element_offset;
@@ -2167,10 +2180,10 @@ void RenderForwardMobile::_render_list_template(RenderingDevice::DrawListID p_dr
} else {
pipeline_specialization.use_light_projector = inst->use_projector;
pipeline_specialization.use_light_soft_shadows = inst->use_soft_shadow;
- pipeline_specialization.disable_omni_lights = inst->omni_light_count == 0;
- pipeline_specialization.disable_spot_lights = inst->spot_light_count == 0;
- pipeline_specialization.disable_reflection_probes = inst->reflection_probe_count == 0;
- pipeline_specialization.disable_decals = inst->decals_count == 0;
+ pipeline_specialization.omni_lights = inst->omni_light_count;
+ pipeline_specialization.spot_lights = inst->spot_light_count;
+ pipeline_specialization.reflection_probes = inst->reflection_probe_count;
+ pipeline_specialization.decals = inst->decals_count;
#ifdef DEBUG_ENABLED
if (unlikely(get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_LIGHTING)) {
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 71f071e44e..e5d208d3a5 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
@@ -239,6 +239,7 @@ void SceneShaderForwardMobile::ShaderData::_create_pipeline(PipelineKey p_pipeli
"VERSION:", p_pipeline_key.version,
"SPEC PACKED #0:", p_pipeline_key.shader_specialization.packed_0,
"SPEC PACKED #1:", p_pipeline_key.shader_specialization.packed_1,
+ "SPEC PACKED #2:", p_pipeline_key.shader_specialization.packed_2,
"RENDER PASS:", p_pipeline_key.render_pass,
"WIREFRAME:", p_pipeline_key.wireframe);
#endif
diff --git a/servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.h b/servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.h
index 60216e9b48..6b40ae5198 100644
--- a/servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.h
+++ b/servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.h
@@ -67,25 +67,35 @@ public:
uint32_t use_directional_soft_shadows : 1;
uint32_t decal_use_mipmaps : 1;
uint32_t projector_use_mipmaps : 1;
- uint32_t disable_omni_lights : 1;
- uint32_t disable_spot_lights : 1;
- uint32_t disable_reflection_probes : 1;
- uint32_t disable_directional_lights : 1;
- uint32_t disable_decals : 1;
uint32_t disable_fog : 1;
uint32_t use_depth_fog : 1;
- uint32_t is_multimesh : 1;
uint32_t use_lightmap_bicubic_filter : 1;
+ uint32_t multimesh : 1;
+ uint32_t multimesh_format_2d : 1;
+ uint32_t multimesh_has_color : 1;
+ uint32_t multimesh_has_custom_data : 1;
+ uint32_t scene_use_ambient_cubemap : 1;
+ uint32_t scene_use_reflection_cubemap : 1;
+ uint32_t scene_roughness_limiter_enabled : 1;
+ uint32_t padding : 5;
uint32_t soft_shadow_samples : 6;
uint32_t penumbra_shadow_samples : 6;
- uint32_t directional_soft_shadow_samples : 6;
};
uint32_t packed_0;
};
union {
- uint32_t directional_penumbra_shadow_samples : 6;
+ struct {
+ uint32_t directional_soft_shadow_samples : 6;
+ uint32_t directional_penumbra_shadow_samples : 6;
+ uint32_t omni_lights : 4;
+ uint32_t spot_lights : 4;
+ uint32_t reflection_probes : 4;
+ uint32_t directional_lights : 4;
+ uint32_t decals : 4;
+ };
+
uint32_t packed_1;
};
diff --git a/servers/rendering/renderer_rd/renderer_compositor_rd.cpp b/servers/rendering/renderer_rd/renderer_compositor_rd.cpp
index 7514e1d5af..325cf0b8b6 100644
--- a/servers/rendering/renderer_rd/renderer_compositor_rd.cpp
+++ b/servers/rendering/renderer_rd/renderer_compositor_rd.cpp
@@ -69,7 +69,7 @@ void RendererCompositorRD::blit_render_targets_to_screen(DisplayServer::WindowID
RD::get_singleton()->draw_list_bind_uniform_set(draw_list, render_target_descriptors[rd_texture], 0);
// We need to invert the phone rotation.
- int screen_rotation_degrees = -DisplayServer::get_singleton()->screen_get_internal_current_rotation();
+ const int screen_rotation_degrees = -RD::get_singleton()->screen_get_pre_rotation_degrees(p_screen);
float screen_rotation = Math::deg_to_rad((float)screen_rotation_degrees);
blit.push_constant.rotation_cos = Math::cos(screen_rotation);
@@ -240,7 +240,7 @@ void RendererCompositorRD::set_boot_image(const Ref<Image> &p_image, const Color
RD::get_singleton()->draw_list_bind_index_array(draw_list, blit.array);
RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uset, 0);
- int screen_rotation_degrees = DisplayServer::get_singleton()->screen_get_internal_current_rotation();
+ const int screen_rotation_degrees = -RD::get_singleton()->screen_get_pre_rotation_degrees(DisplayServer::MAIN_WINDOW_ID);
float screen_rotation = Math::deg_to_rad((float)screen_rotation_degrees);
blit.push_constant.rotation_cos = Math::cos(screen_rotation);
blit.push_constant.rotation_sin = Math::sin(screen_rotation);
diff --git a/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp b/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp
index 65f9891fb2..0c7fced803 100644
--- a/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp
+++ b/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp
@@ -1132,6 +1132,7 @@ void RendererSceneRenderRD::render_scene(const Ref<RenderSceneBuffers> &p_render
scene_data.cam_transform = p_camera_data->main_transform;
scene_data.cam_projection = p_camera_data->main_projection;
scene_data.cam_orthogonal = p_camera_data->is_orthogonal;
+ scene_data.cam_frustum = p_camera_data->is_frustum;
scene_data.camera_visible_layers = p_camera_data->visible_layers;
scene_data.taa_jitter = p_camera_data->taa_jitter;
scene_data.taa_frame_count = p_camera_data->taa_frame_count;
diff --git a/servers/rendering/renderer_rd/shaders/effects/cube_to_dp.glsl b/servers/rendering/renderer_rd/shaders/effects/cube_to_dp.glsl
index 3fb93dda35..b45e310b61 100644
--- a/servers/rendering/renderer_rd/shaders/effects/cube_to_dp.glsl
+++ b/servers/rendering/renderer_rd/shaders/effects/cube_to_dp.glsl
@@ -8,7 +8,6 @@ layout(push_constant, std430) uniform Params {
float z_far;
float z_near;
vec2 texel_size;
- vec4 screen_rect;
}
params;
@@ -17,8 +16,7 @@ layout(location = 0) out vec2 uv_interp;
void main() {
vec2 base_arr[4] = vec2[](vec2(0.0, 0.0), vec2(0.0, 1.0), vec2(1.0, 1.0), vec2(1.0, 0.0));
uv_interp = base_arr[gl_VertexIndex];
- vec2 screen_pos = uv_interp * params.screen_rect.zw + params.screen_rect.xy;
- gl_Position = vec4(screen_pos * 2.0 - 1.0, 0.0, 1.0);
+ gl_Position = vec4(uv_interp * 2.0 - 1.0, 0.0, 1.0);
}
#[fragment]
@@ -35,7 +33,6 @@ layout(push_constant, std430) uniform Params {
float z_far;
float z_near;
vec2 texel_size;
- vec4 screen_rect;
}
params;
diff --git a/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered.glsl b/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered.glsl
index 72236dcd9f..81d3d87a22 100644
--- a/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered.glsl
+++ b/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered.glsl
@@ -211,6 +211,13 @@ vec3 double_add_vec3(vec3 base_a, vec3 prec_a, vec3 base_b, vec3 prec_b, out vec
}
#endif
+uint multimesh_stride() {
+ uint stride = sc_multimesh_format_2d() ? 2 : 3;
+ stride += sc_multimesh_has_color() ? 1 : 0;
+ stride += sc_multimesh_has_custom_data() ? 1 : 0;
+ return stride;
+}
+
void vertex_shader(vec3 vertex_input,
#ifdef NORMAL_USED
in vec3 normal_input,
@@ -219,7 +226,7 @@ void vertex_shader(vec3 vertex_input,
in vec3 tangent_input,
in vec3 binormal_input,
#endif
- in uint instance_index, in bool is_multimesh, in uint multimesh_offset, in SceneData scene_data, in mat4 model_matrix, out vec4 screen_pos) {
+ in uint instance_index, in uint multimesh_offset, in SceneData scene_data, in mat4 model_matrix, out vec4 screen_pos) {
vec4 instance_custom = vec4(0.0);
#if defined(COLOR_USED)
color_interp = color_attrib;
@@ -248,7 +255,7 @@ void vertex_shader(vec3 vertex_input,
mat4 matrix;
mat4 read_model_matrix = model_matrix;
- if (is_multimesh) {
+ if (sc_multimesh()) {
//multimesh, instances are for it
#ifdef USE_PARTICLE_TRAILS
@@ -296,25 +303,10 @@ void vertex_shader(vec3 vertex_input,
#endif
#else
- uint stride = 0;
- {
- //TODO implement a small lookup table for the stride
- if (bool(instances.data[instance_index].flags & INSTANCE_FLAGS_MULTIMESH_FORMAT_2D)) {
- stride += 2;
- } else {
- stride += 3;
- }
- if (bool(instances.data[instance_index].flags & INSTANCE_FLAGS_MULTIMESH_HAS_COLOR)) {
- stride += 1;
- }
- if (bool(instances.data[instance_index].flags & INSTANCE_FLAGS_MULTIMESH_HAS_CUSTOM_DATA)) {
- stride += 1;
- }
- }
-
+ uint stride = multimesh_stride();
uint offset = stride * (gl_InstanceIndex + multimesh_offset);
- if (bool(instances.data[instance_index].flags & INSTANCE_FLAGS_MULTIMESH_FORMAT_2D)) {
+ if (sc_multimesh_format_2d()) {
matrix = mat4(transforms.data[offset + 0], transforms.data[offset + 1], vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0));
offset += 2;
} else {
@@ -322,14 +314,14 @@ void vertex_shader(vec3 vertex_input,
offset += 3;
}
- if (bool(instances.data[instance_index].flags & INSTANCE_FLAGS_MULTIMESH_HAS_COLOR)) {
+ if (sc_multimesh_has_color()) {
#ifdef COLOR_USED
color_interp *= transforms.data[offset];
#endif
offset += 1;
}
- if (bool(instances.data[instance_index].flags & INSTANCE_FLAGS_MULTIMESH_HAS_CUSTOM_DATA)) {
+ if (sc_multimesh_has_custom_data()) {
instance_custom = transforms.data[offset];
}
@@ -427,7 +419,7 @@ void vertex_shader(vec3 vertex_input,
// Then we combine the translations from the model matrix and the view matrix using emulated doubles.
// We add the result to the vertex and ignore the final lost precision.
vec3 model_origin = model_matrix[3].xyz;
- if (is_multimesh) {
+ if (sc_multimesh()) {
vertex = mat3(matrix) * vertex;
model_origin = double_add_vec3(model_origin, model_precision, matrix[3].xyz, vec3(0.0), model_precision);
}
@@ -708,9 +700,7 @@ void _unpack_vertex_attributes(vec4 p_vertex_in, vec3 p_compressed_aabb_position
void main() {
uint instance_index = draw_call.instance_index;
-
- bool is_multimesh = bool(instances.data[instance_index].flags & INSTANCE_FLAGS_MULTIMESH);
- if (!is_multimesh) {
+ if (!sc_multimesh()) {
instance_index += gl_InstanceIndex;
}
@@ -753,7 +743,7 @@ void main() {
prev_tangent,
prev_binormal,
#endif
- instance_index, is_multimesh, draw_call.multimesh_motion_vectors_previous_offset, scene_data_block.prev_data, instances.data[instance_index].prev_transform, prev_screen_position);
+ instance_index, draw_call.multimesh_motion_vectors_previous_offset, scene_data_block.prev_data, instances.data[instance_index].prev_transform, prev_screen_position);
#else
// Unused output.
vec4 screen_position;
@@ -792,7 +782,7 @@ void main() {
tangent,
binormal,
#endif
- instance_index, is_multimesh, draw_call.multimesh_motion_vectors_current_offset, scene_data_block.data, model_matrix, screen_position);
+ instance_index, draw_call.multimesh_motion_vectors_current_offset, scene_data_block.data, model_matrix, screen_position);
}
#[fragment]
diff --git a/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered_inc.glsl b/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered_inc.glsl
index 7bfcb2fb12..8f153f7ed5 100644
--- a/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered_inc.glsl
+++ b/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered_inc.glsl
@@ -59,6 +59,10 @@ uint sc_packed_0() {
return draw_call.sc_packed_0;
}
+uint sc_packed_1() {
+ return draw_call.sc_packed_1;
+}
+
uint uc_cull_mode() {
return (draw_call.uc_packed_0 >> 0) & 3U;
}
@@ -67,11 +71,16 @@ uint uc_cull_mode() {
// Pull the constants from the pipeline's specialization constants.
layout(constant_id = 0) const uint pso_sc_packed_0 = 0;
+layout(constant_id = 1) const uint pso_sc_packed_1 = 0;
uint sc_packed_0() {
return pso_sc_packed_0;
}
+uint sc_packed_1() {
+ return pso_sc_packed_1;
+}
+
#endif
bool sc_use_forward_gi() {
@@ -122,6 +131,22 @@ uint sc_directional_penumbra_shadow_samples() {
return (sc_packed_0() >> 26) & 63U;
}
+bool sc_multimesh() {
+ return ((sc_packed_1() >> 0) & 1U) != 0;
+}
+
+bool sc_multimesh_format_2d() {
+ return ((sc_packed_1() >> 1) & 1U) != 0;
+}
+
+bool sc_multimesh_has_color() {
+ return ((sc_packed_1() >> 2) & 1U) != 0;
+}
+
+bool sc_multimesh_has_custom_data() {
+ return ((sc_packed_1() >> 3) & 1U) != 0;
+}
+
float sc_luminance_multiplier() {
// Not used in clustered renderer but we share some code with the mobile renderer that requires this.
return 1.0;
@@ -144,10 +169,6 @@ layout(set = 0, binding = 2) uniform sampler shadow_sampler;
#define INSTANCE_FLAGS_USE_SH_LIGHTMAP (1 << 9)
#define INSTANCE_FLAGS_USE_VOXEL_GI (1 << 10)
#define INSTANCE_FLAGS_PARTICLES (1 << 11)
-#define INSTANCE_FLAGS_MULTIMESH (1 << 12)
-#define INSTANCE_FLAGS_MULTIMESH_FORMAT_2D (1 << 13)
-#define INSTANCE_FLAGS_MULTIMESH_HAS_COLOR (1 << 14)
-#define INSTANCE_FLAGS_MULTIMESH_HAS_CUSTOM_DATA (1 << 15)
#define INSTANCE_FLAGS_PARTICLE_TRAIL_SHIFT 16
#define INSTANCE_FLAGS_FADE_SHIFT 24
//3 bits of stride
diff --git a/servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile.glsl b/servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile.glsl
index 2f0e4e0bea..404f658fa6 100644
--- a/servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile.glsl
+++ b/servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile.glsl
@@ -178,6 +178,13 @@ vec3 double_add_vec3(vec3 base_a, vec3 prec_a, vec3 base_b, vec3 prec_b, out vec
}
#endif
+uint multimesh_stride() {
+ uint stride = sc_multimesh_format_2d() ? 2 : 3;
+ stride += sc_multimesh_has_color() ? 1 : 0;
+ stride += sc_multimesh_has_custom_data() ? 1 : 0;
+ return stride;
+}
+
void main() {
vec4 instance_custom = vec4(0.0);
#if defined(COLOR_USED)
@@ -208,7 +215,7 @@ void main() {
mat4 matrix;
mat4 read_model_matrix = model_matrix;
- if (sc_is_multimesh()) {
+ if (sc_multimesh()) {
//multimesh, instances are for it
#ifdef USE_PARTICLE_TRAILS
@@ -256,25 +263,10 @@ void main() {
#endif
#else
- uint stride = 0;
- {
- //TODO implement a small lookup table for the stride
- if (bool(instances.data[draw_call.instance_index].flags & INSTANCE_FLAGS_MULTIMESH_FORMAT_2D)) {
- stride += 2;
- } else {
- stride += 3;
- }
- if (bool(instances.data[draw_call.instance_index].flags & INSTANCE_FLAGS_MULTIMESH_HAS_COLOR)) {
- stride += 1;
- }
- if (bool(instances.data[draw_call.instance_index].flags & INSTANCE_FLAGS_MULTIMESH_HAS_CUSTOM_DATA)) {
- stride += 1;
- }
- }
-
+ uint stride = multimesh_stride();
uint offset = stride * gl_InstanceIndex;
- if (bool(instances.data[draw_call.instance_index].flags & INSTANCE_FLAGS_MULTIMESH_FORMAT_2D)) {
+ if (sc_multimesh_format_2d()) {
matrix = mat4(transforms.data[offset + 0], transforms.data[offset + 1], vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0));
offset += 2;
} else {
@@ -282,14 +274,14 @@ void main() {
offset += 3;
}
- if (bool(instances.data[draw_call.instance_index].flags & INSTANCE_FLAGS_MULTIMESH_HAS_COLOR)) {
+ if (sc_multimesh_has_color()) {
#ifdef COLOR_USED
color_interp *= transforms.data[offset];
#endif
offset += 1;
}
- if (bool(instances.data[draw_call.instance_index].flags & INSTANCE_FLAGS_MULTIMESH_HAS_CUSTOM_DATA)) {
+ if (sc_multimesh_has_custom_data()) {
instance_custom = transforms.data[offset];
}
@@ -404,7 +396,7 @@ void main() {
// Then we combine the translations from the model matrix and the view matrix using emulated doubles.
// We add the result to the vertex and ignore the final lost precision.
vec3 model_origin = model_matrix[3].xyz;
- if (sc_is_multimesh()) {
+ if (sc_multimesh()) {
vertex = mat3(matrix) * vertex;
model_origin = double_add_vec3(model_origin, model_precision, matrix[3].xyz, vec3(0.0), model_precision);
}
@@ -461,50 +453,24 @@ void main() {
diffuse_light_interp = vec4(0.0);
specular_light_interp = vec4(0.0);
- if (!sc_disable_omni_lights()) {
- uint light_indices = instances.data[draw_call.instance_index].omni_lights.x;
- for (uint i = 0; i < 8; i++) {
- uint light_index = light_indices & 0xFF;
- if (i == 3) {
- light_indices = instances.data[draw_call.instance_index].omni_lights.y;
- } else {
- light_indices = light_indices >> 8;
- }
-
- if (light_index == 0xFF) {
- break;
- }
-
- light_process_omni_vertex(light_index, vertex, view, normal, roughness,
- diffuse_light_interp.rgb, specular_light_interp.rgb);
- }
+ uvec2 omni_light_indices = instances.data[draw_call.instance_index].omni_lights;
+ for (uint i = 0; i < sc_omni_lights(); i++) {
+ uint light_index = (i > 3) ? ((omni_light_indices.y >> ((i - 4) * 8)) & 0xFF) : ((omni_light_indices.x >> (i * 8)) & 0xFF);
+ light_process_omni_vertex(light_index, vertex, view, normal, roughness, diffuse_light_interp.rgb, specular_light_interp.rgb);
}
- if (!sc_disable_spot_lights()) {
- uint light_indices = instances.data[draw_call.instance_index].spot_lights.x;
- for (uint i = 0; i < 8; i++) {
- uint light_index = light_indices & 0xFF;
- if (i == 3) {
- light_indices = instances.data[draw_call.instance_index].spot_lights.y;
- } else {
- light_indices = light_indices >> 8;
- }
-
- if (light_index == 0xFF) {
- break;
- }
-
- light_process_spot_vertex(light_index, vertex, view, normal, roughness,
- diffuse_light_interp.rgb, specular_light_interp.rgb);
- }
+ uvec2 spot_light_indices = instances.data[draw_call.instance_index].spot_lights;
+ for (uint i = 0; i < sc_spot_lights(); i++) {
+ uint light_index = (i > 3) ? ((spot_light_indices.y >> ((i - 4) * 8)) & 0xFF) : ((spot_light_indices.x >> (i * 8)) & 0xFF);
+ light_process_spot_vertex(light_index, vertex, view, normal, roughness, diffuse_light_interp.rgb, specular_light_interp.rgb);
}
- if (!sc_disable_directional_lights()) {
+ if (sc_directional_lights() > 0) {
// We process the first directional light separately as it may have shadows.
vec3 directional_diffuse = vec3(0.0);
vec3 directional_specular = vec3(0.0);
- for (uint i = 0; i < scene_data.directional_light_count; i++) {
+ for (uint i = 0; i < sc_directional_lights(); i++) {
if (!bool(directional_lights.data[i].mask & instances.data[draw_call.instance_index].layer_mask)) {
continue; // Not masked, skip.
}
@@ -823,7 +789,7 @@ vec4 fog_process(vec3 vertex) {
float sun_total = 0.0;
vec3 view = normalize(vertex);
- for (uint i = 0; i < scene_data_block.data.directional_light_count; i++) {
+ for (uint i = 0; i < sc_directional_lights(); i++) {
vec3 light_color = directional_lights.data[i].color * directional_lights.data[i].energy;
float light_amount = pow(max(dot(view, directional_lights.data[i].direction), 0.0), 8.0);
fog_color += light_color * light_amount * scene_data_block.data.fog_sun_scatter;
@@ -1110,97 +1076,83 @@ void main() {
vec3 vertex_ddx = dFdx(vertex);
vec3 vertex_ddy = dFdy(vertex);
- if (!sc_disable_decals()) { //Decals
- // must implement
-
- uint decal_indices = instances.data[draw_call.instance_index].decals.x;
- for (uint i = 0; i < 8; i++) {
- uint decal_index = decal_indices & 0xFF;
- if (i == 3) {
- decal_indices = instances.data[draw_call.instance_index].decals.y;
- } else {
- decal_indices = decal_indices >> 8;
- }
+ uvec2 decal_indices = instances.data[draw_call.instance_index].decals;
+ for (uint i = 0; i < sc_decals(); i++) {
+ uint decal_index = (i > 3) ? ((decal_indices.y >> ((i - 4) * 8)) & 0xFF) : ((decal_indices.x >> (i * 8)) & 0xFF);
+ if (!bool(decals.data[decal_index].mask & instances.data[draw_call.instance_index].layer_mask)) {
+ continue; //not masked
+ }
- if (decal_index == 0xFF) {
- break;
- }
+ vec3 uv_local = (decals.data[decal_index].xform * vec4(vertex, 1.0)).xyz;
+ if (any(lessThan(uv_local, vec3(0.0, -1.0, 0.0))) || any(greaterThan(uv_local, vec3(1.0)))) {
+ continue; //out of decal
+ }
- if (!bool(decals.data[decal_index].mask & instances.data[draw_call.instance_index].layer_mask)) {
- continue; //not masked
- }
+ float fade = pow(1.0 - (uv_local.y > 0.0 ? uv_local.y : -uv_local.y), uv_local.y > 0.0 ? decals.data[decal_index].upper_fade : decals.data[decal_index].lower_fade);
- vec3 uv_local = (decals.data[decal_index].xform * vec4(vertex, 1.0)).xyz;
- if (any(lessThan(uv_local, vec3(0.0, -1.0, 0.0))) || any(greaterThan(uv_local, vec3(1.0)))) {
- continue; //out of decal
- }
+ if (decals.data[decal_index].normal_fade > 0.0) {
+ fade *= smoothstep(decals.data[decal_index].normal_fade, 1.0, dot(normal_interp, decals.data[decal_index].normal) * 0.5 + 0.5);
+ }
- float fade = pow(1.0 - (uv_local.y > 0.0 ? uv_local.y : -uv_local.y), uv_local.y > 0.0 ? decals.data[decal_index].upper_fade : decals.data[decal_index].lower_fade);
+ //we need ddx/ddy for mipmaps, so simulate them
+ vec2 ddx = (decals.data[decal_index].xform * vec4(vertex_ddx, 0.0)).xz;
+ vec2 ddy = (decals.data[decal_index].xform * vec4(vertex_ddy, 0.0)).xz;
- if (decals.data[decal_index].normal_fade > 0.0) {
- fade *= smoothstep(decals.data[decal_index].normal_fade, 1.0, dot(normal_interp, decals.data[decal_index].normal) * 0.5 + 0.5);
+ if (decals.data[decal_index].albedo_rect != vec4(0.0)) {
+ //has albedo
+ vec4 decal_albedo;
+ if (sc_decal_use_mipmaps()) {
+ decal_albedo = textureGrad(sampler2D(decal_atlas_srgb, decal_sampler), uv_local.xz * decals.data[decal_index].albedo_rect.zw + decals.data[decal_index].albedo_rect.xy, ddx * decals.data[decal_index].albedo_rect.zw, ddy * decals.data[decal_index].albedo_rect.zw);
+ } else {
+ decal_albedo = textureLod(sampler2D(decal_atlas_srgb, decal_sampler), uv_local.xz * decals.data[decal_index].albedo_rect.zw + decals.data[decal_index].albedo_rect.xy, 0.0);
}
+ decal_albedo *= decals.data[decal_index].modulate;
+ decal_albedo.a *= fade;
+ albedo = mix(albedo, decal_albedo.rgb, decal_albedo.a * decals.data[decal_index].albedo_mix);
- //we need ddx/ddy for mipmaps, so simulate them
- vec2 ddx = (decals.data[decal_index].xform * vec4(vertex_ddx, 0.0)).xz;
- vec2 ddy = (decals.data[decal_index].xform * vec4(vertex_ddy, 0.0)).xz;
-
- if (decals.data[decal_index].albedo_rect != vec4(0.0)) {
- //has albedo
- vec4 decal_albedo;
+ if (decals.data[decal_index].normal_rect != vec4(0.0)) {
+ vec3 decal_normal;
if (sc_decal_use_mipmaps()) {
- decal_albedo = textureGrad(sampler2D(decal_atlas_srgb, decal_sampler), uv_local.xz * decals.data[decal_index].albedo_rect.zw + decals.data[decal_index].albedo_rect.xy, ddx * decals.data[decal_index].albedo_rect.zw, ddy * decals.data[decal_index].albedo_rect.zw);
+ decal_normal = textureGrad(sampler2D(decal_atlas, decal_sampler), uv_local.xz * decals.data[decal_index].normal_rect.zw + decals.data[decal_index].normal_rect.xy, ddx * decals.data[decal_index].normal_rect.zw, ddy * decals.data[decal_index].normal_rect.zw).xyz;
} else {
- decal_albedo = textureLod(sampler2D(decal_atlas_srgb, decal_sampler), uv_local.xz * decals.data[decal_index].albedo_rect.zw + decals.data[decal_index].albedo_rect.xy, 0.0);
- }
- decal_albedo *= decals.data[decal_index].modulate;
- decal_albedo.a *= fade;
- albedo = mix(albedo, decal_albedo.rgb, decal_albedo.a * decals.data[decal_index].albedo_mix);
-
- if (decals.data[decal_index].normal_rect != vec4(0.0)) {
- vec3 decal_normal;
- if (sc_decal_use_mipmaps()) {
- decal_normal = textureGrad(sampler2D(decal_atlas, decal_sampler), uv_local.xz * decals.data[decal_index].normal_rect.zw + decals.data[decal_index].normal_rect.xy, ddx * decals.data[decal_index].normal_rect.zw, ddy * decals.data[decal_index].normal_rect.zw).xyz;
- } else {
- decal_normal = textureLod(sampler2D(decal_atlas, decal_sampler), uv_local.xz * decals.data[decal_index].normal_rect.zw + decals.data[decal_index].normal_rect.xy, 0.0).xyz;
- }
- decal_normal.xy = decal_normal.xy * vec2(2.0, -2.0) - vec2(1.0, -1.0); //users prefer flipped y normal maps in most authoring software
- decal_normal.z = sqrt(max(0.0, 1.0 - dot(decal_normal.xy, decal_normal.xy)));
- //convert to view space, use xzy because y is up
- decal_normal = (decals.data[decal_index].normal_xform * decal_normal.xzy).xyz;
-
- normal = normalize(mix(normal, decal_normal, decal_albedo.a));
+ decal_normal = textureLod(sampler2D(decal_atlas, decal_sampler), uv_local.xz * decals.data[decal_index].normal_rect.zw + decals.data[decal_index].normal_rect.xy, 0.0).xyz;
}
+ decal_normal.xy = decal_normal.xy * vec2(2.0, -2.0) - vec2(1.0, -1.0); //users prefer flipped y normal maps in most authoring software
+ decal_normal.z = sqrt(max(0.0, 1.0 - dot(decal_normal.xy, decal_normal.xy)));
+ //convert to view space, use xzy because y is up
+ decal_normal = (decals.data[decal_index].normal_xform * decal_normal.xzy).xyz;
- if (decals.data[decal_index].orm_rect != vec4(0.0)) {
- vec3 decal_orm;
- if (sc_decal_use_mipmaps()) {
- decal_orm = textureGrad(sampler2D(decal_atlas, decal_sampler), uv_local.xz * decals.data[decal_index].orm_rect.zw + decals.data[decal_index].orm_rect.xy, ddx * decals.data[decal_index].orm_rect.zw, ddy * decals.data[decal_index].orm_rect.zw).xyz;
- } else {
- decal_orm = textureLod(sampler2D(decal_atlas, decal_sampler), uv_local.xz * decals.data[decal_index].orm_rect.zw + decals.data[decal_index].orm_rect.xy, 0.0).xyz;
- }
- ao = mix(ao, decal_orm.r, decal_albedo.a);
- roughness = mix(roughness, decal_orm.g, decal_albedo.a);
- metallic = mix(metallic, decal_orm.b, decal_albedo.a);
- }
+ normal = normalize(mix(normal, decal_normal, decal_albedo.a));
}
- if (decals.data[decal_index].emission_rect != vec4(0.0)) {
- //emission is additive, so its independent from albedo
+ if (decals.data[decal_index].orm_rect != vec4(0.0)) {
+ vec3 decal_orm;
if (sc_decal_use_mipmaps()) {
- emission += textureGrad(sampler2D(decal_atlas_srgb, decal_sampler), uv_local.xz * decals.data[decal_index].emission_rect.zw + decals.data[decal_index].emission_rect.xy, ddx * decals.data[decal_index].emission_rect.zw, ddy * decals.data[decal_index].emission_rect.zw).xyz * decals.data[decal_index].emission_energy * fade;
+ decal_orm = textureGrad(sampler2D(decal_atlas, decal_sampler), uv_local.xz * decals.data[decal_index].orm_rect.zw + decals.data[decal_index].orm_rect.xy, ddx * decals.data[decal_index].orm_rect.zw, ddy * decals.data[decal_index].orm_rect.zw).xyz;
} else {
- emission += textureLod(sampler2D(decal_atlas_srgb, decal_sampler), uv_local.xz * decals.data[decal_index].emission_rect.zw + decals.data[decal_index].emission_rect.xy, 0.0).xyz * decals.data[decal_index].emission_energy * fade;
+ decal_orm = textureLod(sampler2D(decal_atlas, decal_sampler), uv_local.xz * decals.data[decal_index].orm_rect.zw + decals.data[decal_index].orm_rect.xy, 0.0).xyz;
}
+ ao = mix(ao, decal_orm.r, decal_albedo.a);
+ roughness = mix(roughness, decal_orm.g, decal_albedo.a);
+ metallic = mix(metallic, decal_orm.b, decal_albedo.a);
+ }
+ }
+
+ if (decals.data[decal_index].emission_rect != vec4(0.0)) {
+ //emission is additive, so its independent from albedo
+ if (sc_decal_use_mipmaps()) {
+ emission += textureGrad(sampler2D(decal_atlas_srgb, decal_sampler), uv_local.xz * decals.data[decal_index].emission_rect.zw + decals.data[decal_index].emission_rect.xy, ddx * decals.data[decal_index].emission_rect.zw, ddy * decals.data[decal_index].emission_rect.zw).xyz * decals.data[decal_index].emission_energy * fade;
+ } else {
+ emission += textureLod(sampler2D(decal_atlas_srgb, decal_sampler), uv_local.xz * decals.data[decal_index].emission_rect.zw + decals.data[decal_index].emission_rect.xy, 0.0).xyz * decals.data[decal_index].emission_energy * fade;
}
}
- } //Decals
+ }
#endif //!MODE_RENDER_DEPTH
/////////////////////// LIGHTING //////////////////////////////
#ifdef NORMAL_USED
- if (scene_data.roughness_limiter_enabled) {
+ if (sc_scene_roughness_limiter_enabled()) {
//https://www.jp.square-enix.com/tech/library/pdf/ImprovedGeometricSpecularAA.pdf
float roughness2 = roughness * roughness;
vec3 dndu = dFdx(normal), dndv = dFdy(normal);
@@ -1225,7 +1177,7 @@ void main() {
#ifndef AMBIENT_LIGHT_DISABLED
- if (scene_data.use_reflection_cubemap) {
+ if (sc_scene_use_reflection_cubemap()) {
#ifdef LIGHT_ANISOTROPY_USED
// https://google.github.io/filament/Filament.html#lighting/imagebasedlights/anisotropy
vec3 anisotropic_direction = anisotropy >= 0.0 ? binormal : tangent;
@@ -1266,7 +1218,7 @@ void main() {
if (scene_data.use_ambient_light) {
ambient_light = scene_data.ambient_light_color_energy.rgb;
- if (scene_data.use_ambient_cubemap) {
+ if (sc_scene_use_ambient_cubemap()) {
vec3 ambient_dir = scene_data.radiance_inverse_xform * normal;
#ifdef USE_RADIANCE_CUBEMAP_ARRAY
vec3 cubemap_ambient = texture(samplerCubeArray(radiance_cubemap, DEFAULT_SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP), vec4(ambient_dir, MAX_ROUGHNESS_LOD)).rgb;
@@ -1285,7 +1237,7 @@ void main() {
#endif // CUSTOM_IRRADIANCE_USED
#ifdef LIGHT_CLEARCOAT_USED
- if (scene_data.use_reflection_cubemap) {
+ if (sc_scene_use_reflection_cubemap()) {
vec3 n = normalize(normal_interp); // We want to use geometric normal, not normal_map
float NoV = max(dot(n, view), 0.0001);
vec3 ref_vec = reflect(-view, n);
@@ -1393,12 +1345,10 @@ void main() {
// skipping ssao, do we remove ssao totally?
- if (!sc_disable_reflection_probes()) { //Reflection probes
+ if (sc_reflection_probes() > 0) {
vec4 reflection_accum = vec4(0.0, 0.0, 0.0, 0.0);
vec4 ambient_accum = vec4(0.0, 0.0, 0.0, 0.0);
- uint reflection_indices = instances.data[draw_call.instance_index].reflection_probes.x;
-
#ifdef LIGHT_ANISOTROPY_USED
// https://google.github.io/filament/Filament.html#lighting/imagebasedlights/anisotropy
vec3 anisotropic_direction = anisotropy >= 0.0 ? binormal : tangent;
@@ -1411,18 +1361,9 @@ void main() {
vec3 ref_vec = normalize(reflect(-view, bent_normal));
ref_vec = mix(ref_vec, bent_normal, roughness * roughness);
- for (uint i = 0; i < 8; i++) {
- uint reflection_index = reflection_indices & 0xFF;
- if (i == 3) {
- reflection_indices = instances.data[draw_call.instance_index].reflection_probes.y;
- } else {
- reflection_indices = reflection_indices >> 8;
- }
-
- if (reflection_index == 0xFF) {
- break;
- }
-
+ uvec2 reflection_indices = instances.data[draw_call.instance_index].reflection_probes;
+ for (uint i = 0; i < sc_reflection_probes(); i++) {
+ uint reflection_index = (i > 3) ? ((reflection_indices.y >> ((i - 4) * 8)) & 0xFF) : ((reflection_indices.x >> (i * 8)) & 0xFF);
reflection_process(reflection_index, vertex, ref_vec, bent_normal, roughness, ambient_light, specular_light, ambient_accum, reflection_accum);
}
@@ -1487,7 +1428,7 @@ void main() {
specular_light += specular_light_interp.rgb * f0;
#endif
- if (!sc_disable_directional_lights()) { //directional light
+ if (sc_directional_lights() > 0) {
#ifndef SHADOWS_DISABLED
// Do shadow and lighting in two passes to reduce register pressure
uint shadow0 = 0;
@@ -1497,7 +1438,7 @@ void main() {
// Only process the first light's shadow for vertex lighting.
for (uint i = 0; i < 1; i++) {
#else
- for (uint i = 0; i < scene_data.directional_light_count; i++) {
+ for (uint i = 0; i < sc_directional_lights(); i++) {
#endif
if (!bool(directional_lights.data[i].mask & instances.data[draw_call.instance_index].layer_mask)) {
@@ -1612,7 +1553,7 @@ void main() {
#endif // SHADOWS_DISABLED
#ifndef USE_VERTEX_LIGHTING
- for (uint i = 0; i < scene_data.directional_light_count; i++) {
+ for (uint i = 0; i < sc_directional_lights(); i++) {
if (!bool(directional_lights.data[i].mask & instances.data[draw_call.instance_index].layer_mask)) {
continue; //not masked
}
@@ -1678,95 +1619,72 @@ void main() {
} //directional light
#ifndef USE_VERTEX_LIGHTING
- if (!sc_disable_omni_lights()) { //omni lights
- uint light_indices = instances.data[draw_call.instance_index].omni_lights.x;
- for (uint i = 0; i < 8; i++) {
- uint light_index = light_indices & 0xFF;
- if (i == 3) {
- light_indices = instances.data[draw_call.instance_index].omni_lights.y;
- } else {
- light_indices = light_indices >> 8;
- }
-
- if (light_index == 0xFF) {
- break;
- }
+ uvec2 omni_indices = instances.data[draw_call.instance_index].omni_lights;
+ for (uint i = 0; i < sc_omni_lights(); i++) {
+ uint light_index = (i > 3) ? ((omni_indices.y >> ((i - 4) * 8)) & 0xFF) : ((omni_indices.x >> (i * 8)) & 0xFF);
- float shadow = light_process_omni_shadow(light_index, vertex, normal, scene_data.taa_frame_count);
+ float shadow = light_process_omni_shadow(light_index, vertex, normal, scene_data.taa_frame_count);
- shadow = blur_shadow(shadow);
+ shadow = blur_shadow(shadow);
- // Fragment lighting
- light_process_omni(light_index, vertex, view, normal, vertex_ddx, vertex_ddy, f0, orms, shadow, albedo, alpha,
+ // Fragment lighting
+ light_process_omni(light_index, vertex, view, normal, vertex_ddx, vertex_ddy, f0, orms, shadow, albedo, alpha,
#ifdef LIGHT_BACKLIGHT_USED
- backlight,
+ backlight,
#endif
/*
#ifdef LIGHT_TRANSMITTANCE_USED
- transmittance_color,
- transmittance_depth,
- transmittance_boost,
+ transmittance_color,
+ transmittance_depth,
+ transmittance_boost,
#endif
*/
#ifdef LIGHT_RIM_USED
- rim,
- rim_tint,
+ rim,
+ rim_tint,
#endif
#ifdef LIGHT_CLEARCOAT_USED
- clearcoat, clearcoat_roughness, normalize(normal_interp),
+ clearcoat, clearcoat_roughness, normalize(normal_interp),
#endif
#ifdef LIGHT_ANISOTROPY_USED
- tangent,
- binormal, anisotropy,
+ tangent,
+ binormal, anisotropy,
#endif
- diffuse_light, specular_light);
- }
- } //omni lights
-
- if (!sc_disable_spot_lights()) { //spot lights
-
- uint light_indices = instances.data[draw_call.instance_index].spot_lights.x;
- for (uint i = 0; i < 8; i++) {
- uint light_index = light_indices & 0xFF;
- if (i == 3) {
- light_indices = instances.data[draw_call.instance_index].spot_lights.y;
- } else {
- light_indices = light_indices >> 8;
- }
+ diffuse_light, specular_light);
+ }
- if (light_index == 0xFF) {
- break;
- }
+ uvec2 spot_indices = instances.data[draw_call.instance_index].spot_lights;
+ for (uint i = 0; i < sc_spot_lights(); i++) {
+ uint light_index = (i > 3) ? ((spot_indices.y >> ((i - 4) * 8)) & 0xFF) : ((spot_indices.x >> (i * 8)) & 0xFF);
- float shadow = light_process_spot_shadow(light_index, vertex, normal, scene_data.taa_frame_count);
+ float shadow = light_process_spot_shadow(light_index, vertex, normal, scene_data.taa_frame_count);
- shadow = blur_shadow(shadow);
+ shadow = blur_shadow(shadow);
- light_process_spot(light_index, vertex, view, normal, vertex_ddx, vertex_ddy, f0, orms, shadow, albedo, alpha,
+ light_process_spot(light_index, vertex, view, normal, vertex_ddx, vertex_ddy, f0, orms, shadow, albedo, alpha,
#ifdef LIGHT_BACKLIGHT_USED
- backlight,
+ backlight,
#endif
/*
#ifdef LIGHT_TRANSMITTANCE_USED
- transmittance_color,
- transmittance_depth,
- transmittance_boost,
+ transmittance_color,
+ transmittance_depth,
+ transmittance_boost,
#endif
*/
#ifdef LIGHT_RIM_USED
- rim,
- rim_tint,
+ rim,
+ rim_tint,
#endif
#ifdef LIGHT_CLEARCOAT_USED
- clearcoat, clearcoat_roughness, normalize(normal_interp),
+ clearcoat, clearcoat_roughness, normalize(normal_interp),
#endif
#ifdef LIGHT_ANISOTROPY_USED
- tangent,
- binormal, anisotropy,
+ tangent,
+ binormal, anisotropy,
#endif
- diffuse_light, specular_light);
- }
- } //spot lights
+ diffuse_light, specular_light);
+ }
#endif // !VERTEX_LIGHTING
#endif //!defined(MODE_RENDER_DEPTH) && !defined(MODE_UNSHADED)
diff --git a/servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile_inc.glsl b/servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile_inc.glsl
index df528973da..2cc86482f6 100644
--- a/servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile_inc.glsl
+++ b/servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile_inc.glsl
@@ -95,56 +95,80 @@ bool sc_projector_use_mipmaps() {
return ((sc_packed_0() >> 4) & 1U) != 0;
}
-bool sc_disable_omni_lights() {
+bool sc_disable_fog() {
return ((sc_packed_0() >> 5) & 1U) != 0;
}
-bool sc_disable_spot_lights() {
+bool sc_use_depth_fog() {
return ((sc_packed_0() >> 6) & 1U) != 0;
}
-bool sc_disable_reflection_probes() {
+bool sc_use_lightmap_bicubic_filter() {
return ((sc_packed_0() >> 7) & 1U) != 0;
}
-bool sc_disable_directional_lights() {
+bool sc_multimesh() {
return ((sc_packed_0() >> 8) & 1U) != 0;
}
-bool sc_disable_decals() {
+bool sc_multimesh_format_2d() {
return ((sc_packed_0() >> 9) & 1U) != 0;
}
-bool sc_disable_fog() {
+bool sc_multimesh_has_color() {
return ((sc_packed_0() >> 10) & 1U) != 0;
}
-bool sc_use_depth_fog() {
+bool sc_multimesh_has_custom_data() {
return ((sc_packed_0() >> 11) & 1U) != 0;
}
-bool sc_is_multimesh() {
+bool sc_scene_use_ambient_cubemap() {
return ((sc_packed_0() >> 12) & 1U) != 0;
}
-bool sc_use_lightmap_bicubic_filter() {
+bool sc_scene_use_reflection_cubemap() {
return ((sc_packed_0() >> 13) & 1U) != 0;
}
+bool sc_scene_roughness_limiter_enabled() {
+ return ((sc_packed_0() >> 14) & 1U) != 0;
+}
+
uint sc_soft_shadow_samples() {
- return (sc_packed_0() >> 14) & 63U;
+ return (sc_packed_0() >> 20) & 63U;
}
uint sc_penumbra_shadow_samples() {
- return (sc_packed_0() >> 20) & 63U;
+ return (sc_packed_0() >> 26) & 63U;
}
uint sc_directional_soft_shadow_samples() {
- return (sc_packed_0() >> 26) & 63U;
+ return (sc_packed_1() >> 0) & 63U;
}
uint sc_directional_penumbra_shadow_samples() {
- return (sc_packed_1() >> 0) & 63U;
+ return (sc_packed_1() >> 6) & 63U;
+}
+
+uint sc_omni_lights() {
+ return (sc_packed_1() >> 12) & 15U;
+}
+
+uint sc_spot_lights() {
+ return (sc_packed_1() >> 16) & 15U;
+}
+
+uint sc_reflection_probes() {
+ return (sc_packed_1() >> 20) & 15U;
+}
+
+uint sc_directional_lights() {
+ return (sc_packed_1() >> 24) & 15U;
+}
+
+uint sc_decals() {
+ return (sc_packed_1() >> 28) & 15U;
}
float sc_luminance_multiplier() {
@@ -166,10 +190,6 @@ layout(set = 0, binding = 2) uniform sampler shadow_sampler;
#define INSTANCE_FLAGS_USE_SH_LIGHTMAP (1 << 9)
#define INSTANCE_FLAGS_USE_VOXEL_GI (1 << 10)
#define INSTANCE_FLAGS_PARTICLES (1 << 11)
-#define INSTANCE_FLAGS_MULTIMESH (1 << 12)
-#define INSTANCE_FLAGS_MULTIMESH_FORMAT_2D (1 << 13)
-#define INSTANCE_FLAGS_MULTIMESH_HAS_COLOR (1 << 14)
-#define INSTANCE_FLAGS_MULTIMESH_HAS_CUSTOM_DATA (1 << 15)
#define INSTANCE_FLAGS_PARTICLE_TRAIL_SHIFT 16
//3 bits of stride
#define INSTANCE_FLAGS_PARTICLE_TRAIL_MASK 0xFF
diff --git a/servers/rendering/renderer_rd/storage_rd/particles_storage.cpp b/servers/rendering/renderer_rd/storage_rd/particles_storage.cpp
index af12ac1ce4..13e358538a 100644
--- a/servers/rendering/renderer_rd/storage_rd/particles_storage.cpp
+++ b/servers/rendering/renderer_rd/storage_rd/particles_storage.cpp
@@ -308,9 +308,14 @@ void ParticlesStorage::_particles_free_data(Particles *particles) {
particles->emission_storage_buffer = RID();
}
- if (particles->unused_storage_buffer.is_valid()) {
- RD::get_singleton()->free(particles->unused_storage_buffer);
- particles->unused_storage_buffer = RID();
+ if (particles->unused_emission_storage_buffer.is_valid()) {
+ RD::get_singleton()->free(particles->unused_emission_storage_buffer);
+ particles->unused_emission_storage_buffer = RID();
+ }
+
+ if (particles->unused_trail_storage_buffer.is_valid()) {
+ RD::get_singleton()->free(particles->unused_trail_storage_buffer);
+ particles->unused_trail_storage_buffer = RID();
}
if (RD::get_singleton()->uniform_set_is_valid(particles->particles_material_uniform_set)) {
@@ -536,9 +541,15 @@ void ParticlesStorage::_particles_allocate_emission_buffer(Particles *particles)
}
}
-void ParticlesStorage::_particles_ensure_unused_buffer(Particles *particles) {
- if (particles->unused_storage_buffer.is_null()) {
- particles->unused_storage_buffer = RD::get_singleton()->storage_buffer_create(sizeof(uint32_t) * 4);
+void ParticlesStorage::_particles_ensure_unused_emission_buffer(Particles *particles) {
+ if (particles->unused_emission_storage_buffer.is_null()) {
+ particles->unused_emission_storage_buffer = RD::get_singleton()->storage_buffer_create(sizeof(uint32_t) * 4);
+ }
+}
+
+void ParticlesStorage::_particles_ensure_unused_trail_buffer(Particles *particles) {
+ if (particles->unused_trail_storage_buffer.is_null()) {
+ particles->unused_trail_storage_buffer = RD::get_singleton()->storage_buffer_create(sizeof(uint32_t) * 4);
}
}
@@ -765,8 +776,8 @@ void ParticlesStorage::_particles_process(Particles *p_particles, double p_delta
if (p_particles->emission_storage_buffer.is_valid()) {
u.append_id(p_particles->emission_storage_buffer);
} else {
- _particles_ensure_unused_buffer(p_particles);
- u.append_id(p_particles->unused_storage_buffer);
+ _particles_ensure_unused_emission_buffer(p_particles);
+ u.append_id(p_particles->unused_emission_storage_buffer);
}
uniforms.push_back(u);
}
@@ -781,8 +792,8 @@ void ParticlesStorage::_particles_process(Particles *p_particles, double p_delta
}
u.append_id(sub_emitter->emission_storage_buffer);
} else {
- _particles_ensure_unused_buffer(p_particles);
- u.append_id(p_particles->unused_storage_buffer);
+ _particles_ensure_unused_emission_buffer(p_particles);
+ u.append_id(p_particles->unused_emission_storage_buffer);
}
uniforms.push_back(u);
}
@@ -1483,8 +1494,8 @@ void ParticlesStorage::update_particles() {
if (particles->trail_bind_pose_buffer.is_valid()) {
u.append_id(particles->trail_bind_pose_buffer);
} else {
- _particles_ensure_unused_buffer(particles);
- u.append_id(particles->unused_storage_buffer);
+ _particles_ensure_unused_trail_buffer(particles);
+ u.append_id(particles->unused_trail_storage_buffer);
}
uniforms.push_back(u);
}
diff --git a/servers/rendering/renderer_rd/storage_rd/particles_storage.h b/servers/rendering/renderer_rd/storage_rd/particles_storage.h
index 55e8278153..73989e4011 100644
--- a/servers/rendering/renderer_rd/storage_rd/particles_storage.h
+++ b/servers/rendering/renderer_rd/storage_rd/particles_storage.h
@@ -249,7 +249,8 @@ private:
ParticleEmissionBuffer *emission_buffer = nullptr;
RID emission_storage_buffer;
- RID unused_storage_buffer;
+ RID unused_emission_storage_buffer;
+ RID unused_trail_storage_buffer;
HashSet<RID> collisions;
@@ -267,7 +268,8 @@ private:
void _particles_process(Particles *p_particles, double p_delta);
void _particles_allocate_emission_buffer(Particles *particles);
- void _particles_ensure_unused_buffer(Particles *particles);
+ void _particles_ensure_unused_emission_buffer(Particles *particles);
+ void _particles_ensure_unused_trail_buffer(Particles *particles);
void _particles_free_data(Particles *particles);
void _particles_update_buffers(Particles *particles);
@@ -511,7 +513,7 @@ public:
_FORCE_INLINE_ bool particles_has_collision(RID p_particles) {
Particles *particles = particles_owner.get_or_null(p_particles);
- ERR_FAIL_NULL_V(particles, 0);
+ ERR_FAIL_NULL_V(particles, false);
return particles->has_collision_cache;
}
diff --git a/servers/rendering/renderer_rd/storage_rd/render_scene_data_rd.h b/servers/rendering/renderer_rd/storage_rd/render_scene_data_rd.h
index 73c0771aee..c99f1ba59b 100644
--- a/servers/rendering/renderer_rd/storage_rd/render_scene_data_rd.h
+++ b/servers/rendering/renderer_rd/storage_rd/render_scene_data_rd.h
@@ -53,6 +53,7 @@ public:
float taa_frame_count = 0.0f;
uint32_t camera_visible_layers;
bool cam_orthogonal = false;
+ bool cam_frustum = false;
bool flip_y = false;
// For billboards to cast correct shadows.
diff --git a/servers/rendering/renderer_rd/storage_rd/texture_storage.h b/servers/rendering/renderer_rd/storage_rd/texture_storage.h
index 2d59b822a0..d33065164d 100644
--- a/servers/rendering/renderer_rd/storage_rd/texture_storage.h
+++ b/servers/rendering/renderer_rd/storage_rd/texture_storage.h
@@ -104,9 +104,9 @@ private:
/* Canvas Texture API */
struct CanvasTextureCache {
- RID diffuse = RID();
- RID normal = RID();
- RID specular = RID();
+ RID diffuse;
+ RID normal;
+ RID specular;
};
class CanvasTexture {
diff --git a/servers/rendering/renderer_scene_cull.cpp b/servers/rendering/renderer_scene_cull.cpp
index 2e8e77132d..2e4e29d174 100644
--- a/servers/rendering/renderer_scene_cull.cpp
+++ b/servers/rendering/renderer_scene_cull.cpp
@@ -2740,6 +2740,7 @@ void RendererSceneCull::render_camera(const Ref<RenderSceneBuffers> &p_render_bu
Projection projection;
bool vaspect = camera->vaspect;
bool is_orthogonal = false;
+ bool is_frustum = false;
switch (camera->type) {
case Camera::ORTHOGONAL: {
@@ -2768,10 +2769,11 @@ void RendererSceneCull::render_camera(const Ref<RenderSceneBuffers> &p_render_bu
camera->znear,
camera->zfar,
camera->vaspect);
+ is_frustum = true;
} break;
}
- camera_data.set_camera(transform, projection, is_orthogonal, vaspect, jitter, taa_frame_count, camera->visible_layers);
+ camera_data.set_camera(transform, projection, is_orthogonal, is_frustum, vaspect, jitter, taa_frame_count, camera->visible_layers);
} else {
// Setup our camera for our XR interface.
// We can support multiple views here each with their own camera
@@ -2793,9 +2795,9 @@ void RendererSceneCull::render_camera(const Ref<RenderSceneBuffers> &p_render_bu
}
if (view_count == 1) {
- camera_data.set_camera(transforms[0], projections[0], false, camera->vaspect, jitter, p_jitter_phase_count, camera->visible_layers);
+ camera_data.set_camera(transforms[0], projections[0], false, false, camera->vaspect, jitter, p_jitter_phase_count, camera->visible_layers);
} else if (view_count == 2) {
- camera_data.set_multiview_camera(view_count, transforms, projections, false, camera->vaspect);
+ camera_data.set_multiview_camera(view_count, transforms, projections, false, false, camera->vaspect);
} else {
// this won't be called (see fail check above) but keeping this comment to indicate we may support more then 2 views in the future...
}
@@ -3643,7 +3645,7 @@ void RendererSceneCull::render_empty_scene(const Ref<RenderSceneBuffers> &p_rend
RENDER_TIMESTAMP("Render Empty 3D Scene");
RendererSceneRender::CameraData camera_data;
- camera_data.set_camera(Transform3D(), Projection(), true, false);
+ camera_data.set_camera(Transform3D(), Projection(), true, false, false);
scene_render->render_scene(p_render_buffers, &camera_data, &camera_data, PagedArray<RenderGeometryInstance *>(), PagedArray<RID>(), PagedArray<RID>(), PagedArray<RID>(), PagedArray<RID>(), PagedArray<RID>(), PagedArray<RID>(), environment, RID(), compositor, p_shadow_atlas, RID(), scenario->reflection_atlas, RID(), 0, 0, nullptr, 0, nullptr, 0, nullptr);
#endif
@@ -3721,7 +3723,7 @@ bool RendererSceneCull::_render_reflection_probe_step(Instance *p_instance, int
RENDER_TIMESTAMP("Render ReflectionProbe, Step " + itos(p_step));
RendererSceneRender::CameraData camera_data;
- camera_data.set_camera(xform, cm, false, false);
+ camera_data.set_camera(xform, cm, false, false, false);
Ref<RenderSceneBuffers> render_buffers = RSG::light_storage->reflection_probe_atlas_get_render_buffers(scenario->reflection_atlas);
_render_scene(&camera_data, render_buffers, environment, RID(), RID(), RSG::light_storage->reflection_probe_get_cull_mask(p_instance->base), p_instance->scenario->self, RID(), shadow_atlas, reflection_probe->instance, p_step, mesh_lod_threshold, use_shadows);
diff --git a/servers/rendering/renderer_scene_cull.h b/servers/rendering/renderer_scene_cull.h
index 1be89b46cf..36cf5cb4c3 100644
--- a/servers/rendering/renderer_scene_cull.h
+++ b/servers/rendering/renderer_scene_cull.h
@@ -1258,6 +1258,7 @@ public:
PASS3(environment_set_bg_energy, RID, float, float)
PASS2(environment_set_canvas_max_layer, RID, int)
PASS6(environment_set_ambient_light, RID, const Color &, RS::EnvironmentAmbientSource, float, float, RS::EnvironmentReflectionSource)
+ PASS2(environment_set_camera_feed_id, RID, int)
PASS1RC(RS::EnvironmentBG, environment_get_background, RID)
PASS1RC(RID, environment_get_sky, RID)
diff --git a/servers/rendering/renderer_scene_render.cpp b/servers/rendering/renderer_scene_render.cpp
index f330236254..a68c86406a 100644
--- a/servers/rendering/renderer_scene_render.cpp
+++ b/servers/rendering/renderer_scene_render.cpp
@@ -35,9 +35,10 @@
/////////////////////////////////////////////////////////////////////////////
// CameraData
-void RendererSceneRender::CameraData::set_camera(const Transform3D p_transform, const Projection p_projection, bool p_is_orthogonal, bool p_vaspect, const Vector2 &p_taa_jitter, float p_taa_frame_count, const uint32_t p_visible_layers) {
+void RendererSceneRender::CameraData::set_camera(const Transform3D p_transform, const Projection p_projection, bool p_is_orthogonal, bool p_is_frustum, bool p_vaspect, const Vector2 &p_taa_jitter, float p_taa_frame_count, const uint32_t p_visible_layers) {
view_count = 1;
is_orthogonal = p_is_orthogonal;
+ is_frustum = p_is_frustum;
vaspect = p_vaspect;
main_transform = p_transform;
@@ -50,12 +51,13 @@ void RendererSceneRender::CameraData::set_camera(const Transform3D p_transform,
taa_frame_count = p_taa_frame_count;
}
-void RendererSceneRender::CameraData::set_multiview_camera(uint32_t p_view_count, const Transform3D *p_transforms, const Projection *p_projections, bool p_is_orthogonal, bool p_vaspect) {
+void RendererSceneRender::CameraData::set_multiview_camera(uint32_t p_view_count, const Transform3D *p_transforms, const Projection *p_projections, bool p_is_orthogonal, bool p_is_frustum, bool p_vaspect) {
ERR_FAIL_COND_MSG(p_view_count != 2, "Incorrect view count for stereoscopic view");
visible_layers = 0xFFFFFFFF;
view_count = p_view_count;
is_orthogonal = p_is_orthogonal;
+ is_frustum = p_is_frustum;
vaspect = p_vaspect;
Vector<Plane> planes[2];
@@ -351,6 +353,14 @@ RS::EnvironmentReflectionSource RendererSceneRender::environment_get_reflection_
return environment_storage.environment_get_reflection_source(p_env);
}
+void RendererSceneRender::environment_set_camera_feed_id(RID p_env, int p_camera_feed_id) {
+ environment_storage.environment_set_camera_feed_id(p_env, p_camera_feed_id);
+}
+
+int RendererSceneRender::environment_get_camera_feed_id(RID p_env) const {
+ return environment_storage.environment_get_camera_feed_id(p_env);
+}
+
// Tonemap
void RendererSceneRender::environment_set_tonemap(RID p_env, RS::EnvironmentToneMapper p_tone_mapper, float p_exposure, float p_white) {
diff --git a/servers/rendering/renderer_scene_render.h b/servers/rendering/renderer_scene_render.h
index 91ef871020..b5fede3f91 100644
--- a/servers/rendering/renderer_scene_render.h
+++ b/servers/rendering/renderer_scene_render.h
@@ -120,10 +120,7 @@ public:
void environment_set_bg_energy(RID p_env, float p_multiplier, float p_exposure_value);
void environment_set_canvas_max_layer(RID p_env, int p_max_layer);
void environment_set_ambient_light(RID p_env, const Color &p_color, RS::EnvironmentAmbientSource p_ambient = RS::ENV_AMBIENT_SOURCE_BG, float p_energy = 1.0, float p_sky_contribution = 0.0, RS::EnvironmentReflectionSource p_reflection_source = RS::ENV_REFLECTION_SOURCE_BG);
-// FIXME: Disabled during Vulkan refactoring, should be ported.
-#if 0
void environment_set_camera_feed_id(RID p_env, int p_camera_feed_id);
-#endif
RS::EnvironmentBG environment_get_background(RID p_env) const;
RID environment_get_sky(RID p_env) const;
@@ -138,6 +135,7 @@ public:
float environment_get_ambient_light_energy(RID p_env) const;
float environment_get_ambient_sky_contribution(RID p_env) const;
RS::EnvironmentReflectionSource environment_get_reflection_source(RID p_env) const;
+ int environment_get_camera_feed_id(RID p_env) const;
// Tonemap
void environment_set_tonemap(RID p_env, RS::EnvironmentToneMapper p_tone_mapper, float p_exposure, float p_white);
@@ -304,6 +302,7 @@ public:
// flags
uint32_t view_count;
bool is_orthogonal;
+ bool is_frustum;
uint32_t visible_layers;
bool vaspect;
@@ -316,8 +315,8 @@ public:
Vector2 taa_jitter;
float taa_frame_count = 0.0f;
- void set_camera(const Transform3D p_transform, const Projection p_projection, bool p_is_orthogonal, bool p_vaspect, const Vector2 &p_taa_jitter = Vector2(), float p_taa_frame_count = 0.0f, uint32_t p_visible_layers = 0xFFFFFFFF);
- void set_multiview_camera(uint32_t p_view_count, const Transform3D *p_transforms, const Projection *p_projections, bool p_is_orthogonal, bool p_vaspect);
+ void set_camera(const Transform3D p_transform, const Projection p_projection, bool p_is_orthogonal, bool p_is_frustum, bool p_vaspect, const Vector2 &p_taa_jitter = Vector2(), float p_taa_frame_count = 0.0f, uint32_t p_visible_layers = 0xFFFFFFFF);
+ void set_multiview_camera(uint32_t p_view_count, const Transform3D *p_transforms, const Projection *p_projections, bool p_is_orthogonal, bool p_is_frustum, bool p_vaspect);
};
virtual void render_scene(const Ref<RenderSceneBuffers> &p_render_buffers, const CameraData *p_camera_data, const CameraData *p_prev_camera_data, const PagedArray<RenderGeometryInstance *> &p_instances, const PagedArray<RID> &p_lights, const PagedArray<RID> &p_reflection_probes, const PagedArray<RID> &p_voxel_gi_instances, const PagedArray<RID> &p_decals, const PagedArray<RID> &p_lightmaps, const PagedArray<RID> &p_fog_volumes, RID p_environment, RID p_camera_attributes, RID p_compositor, RID p_shadow_atlas, RID p_occluder_debug_tex, RID p_reflection_atlas, RID p_reflection_probe, int p_reflection_probe_pass, float p_screen_mesh_lod_threshold, const RenderShadowData *p_render_shadows, int p_render_shadow_count, const RenderSDFGIData *p_render_sdfgi_regions, int p_render_sdfgi_region_count, const RenderSDFGIUpdateData *p_sdfgi_update_data = nullptr, RenderingMethod::RenderInfo *r_render_info = nullptr) = 0;
diff --git a/servers/rendering/rendering_device.cpp b/servers/rendering/rendering_device.cpp
index df94d320c3..8bf0e7f623 100644
--- a/servers/rendering/rendering_device.cpp
+++ b/servers/rendering/rendering_device.cpp
@@ -3759,6 +3759,15 @@ int RenderingDevice::screen_get_height(DisplayServer::WindowID p_screen) const {
return context->surface_get_height(surface);
}
+int RenderingDevice::screen_get_pre_rotation_degrees(DisplayServer::WindowID p_screen) const {
+ _THREAD_SAFE_METHOD_
+
+ HashMap<DisplayServer::WindowID, RDD::SwapChainID>::ConstIterator it = screen_swap_chains.find(p_screen);
+ ERR_FAIL_COND_V_MSG(it == screen_swap_chains.end(), ERR_CANT_CREATE, "A swap chain was not created for the screen.");
+
+ return driver->swap_chain_get_pre_rotation_degrees(it->value);
+}
+
RenderingDevice::FramebufferFormatID RenderingDevice::screen_get_framebuffer_format(DisplayServer::WindowID p_screen) const {
_THREAD_SAFE_METHOD_
diff --git a/servers/rendering/rendering_device.h b/servers/rendering/rendering_device.h
index 6831f3df2b..b39ff9b920 100644
--- a/servers/rendering/rendering_device.h
+++ b/servers/rendering/rendering_device.h
@@ -1085,6 +1085,7 @@ public:
Error screen_prepare_for_drawing(DisplayServer::WindowID p_screen = DisplayServer::MAIN_WINDOW_ID);
int screen_get_width(DisplayServer::WindowID p_screen = DisplayServer::MAIN_WINDOW_ID) const;
int screen_get_height(DisplayServer::WindowID p_screen = DisplayServer::MAIN_WINDOW_ID) const;
+ int screen_get_pre_rotation_degrees(DisplayServer::WindowID p_screen = DisplayServer::MAIN_WINDOW_ID) const;
FramebufferFormatID screen_get_framebuffer_format(DisplayServer::WindowID p_screen = DisplayServer::MAIN_WINDOW_ID) const;
Error screen_free(DisplayServer::WindowID p_screen = DisplayServer::MAIN_WINDOW_ID);
diff --git a/servers/rendering/rendering_device_driver.cpp b/servers/rendering/rendering_device_driver.cpp
index f9ed067eae..b5e5679ddf 100644
--- a/servers/rendering/rendering_device_driver.cpp
+++ b/servers/rendering/rendering_device_driver.cpp
@@ -378,6 +378,8 @@ uint64_t RenderingDeviceDriver::api_trait_get(ApiTrait p_trait) {
return true;
case API_TRAIT_USE_GENERAL_IN_COPY_QUEUES:
return false;
+ case API_TRAIT_BUFFERS_REQUIRE_TRANSITIONS:
+ return false;
default:
ERR_FAIL_V(0);
}
diff --git a/servers/rendering/rendering_device_driver.h b/servers/rendering/rendering_device_driver.h
index 258717091d..3046c8d1a3 100644
--- a/servers/rendering/rendering_device_driver.h
+++ b/servers/rendering/rendering_device_driver.h
@@ -456,6 +456,9 @@ public:
// Retrieve the render pass that can be used to draw on the swap chain's framebuffers.
virtual RenderPassID swap_chain_get_render_pass(SwapChainID p_swap_chain) = 0;
+ // Retrieve the rotation in degrees to apply as a pre-transform. Usually 0 on PC. May be 0, 90, 180 & 270 on Android.
+ virtual int swap_chain_get_pre_rotation_degrees(SwapChainID p_swap_chain) { return 0; }
+
// Retrieve the format used by the swap chain's framebuffers.
virtual DataFormat swap_chain_get_format(SwapChainID p_swap_chain) = 0;
@@ -758,6 +761,7 @@ public:
API_TRAIT_SECONDARY_VIEWPORT_SCISSOR,
API_TRAIT_CLEARS_WITH_COPY_ENGINE,
API_TRAIT_USE_GENERAL_IN_COPY_QUEUES,
+ API_TRAIT_BUFFERS_REQUIRE_TRANSITIONS,
};
enum ShaderChangeInvalidation {
diff --git a/servers/rendering/rendering_device_graph.cpp b/servers/rendering/rendering_device_graph.cpp
index 2e394df0bb..1cf3ec46f0 100644
--- a/servers/rendering/rendering_device_graph.cpp
+++ b/servers/rendering/rendering_device_graph.cpp
@@ -142,6 +142,25 @@ RDD::BarrierAccessBits RenderingDeviceGraph::_usage_to_access_bits(ResourceUsage
#endif
}
+bool RenderingDeviceGraph::_check_command_intersection(ResourceTracker *p_resource_tracker, int32_t p_previous_command_index, int32_t p_command_index) const {
+ if (p_resource_tracker->usage != RESOURCE_USAGE_ATTACHMENT_COLOR_READ_WRITE && p_resource_tracker->usage != RESOURCE_USAGE_ATTACHMENT_DEPTH_STENCIL_READ_WRITE) {
+ // We don't check possible intersections for usages that aren't consecutive color or depth writes.
+ return true;
+ }
+
+ const uint32_t previous_command_data_offset = command_data_offsets[p_previous_command_index];
+ const uint32_t current_command_data_offset = command_data_offsets[p_command_index];
+ const RecordedDrawListCommand &previous_draw_list_command = *reinterpret_cast<const RecordedDrawListCommand *>(&command_data[previous_command_data_offset]);
+ const RecordedDrawListCommand &current_draw_list_command = *reinterpret_cast<const RecordedDrawListCommand *>(&command_data[current_command_data_offset]);
+ if (previous_draw_list_command.type != RecordedCommand::TYPE_DRAW_LIST || current_draw_list_command.type != RecordedCommand::TYPE_DRAW_LIST) {
+ // We don't check possible intersections if both commands aren't draw lists.
+ return true;
+ }
+
+ // We check if the region used by both draw lists have an intersection.
+ return previous_draw_list_command.region.intersects(current_draw_list_command.region);
+}
+
int32_t RenderingDeviceGraph::_add_to_command_list(int32_t p_command_index, int32_t p_list_index) {
DEV_ASSERT(p_command_index < int32_t(command_count));
DEV_ASSERT(p_list_index < int32_t(command_list_nodes.size()));
@@ -427,11 +446,9 @@ void RenderingDeviceGraph::_add_command_to_graph(ResourceTracker **p_resource_tr
#if USE_BUFFER_BARRIERS
_add_buffer_barrier_to_command(resource_tracker->buffer_driver_id, resource_tracker->usage_access, new_usage_access, r_command->buffer_barrier_index, r_command->buffer_barrier_count);
#endif
- // FIXME: Memory barriers are currently pushed regardless of whether buffer barriers are being used or not. Refer to the comment on the
- // definition of USE_BUFFER_BARRIERS for the reason behind this. This can be fixed to be one case or the other once it's been confirmed
- // the buffer and memory barrier behavior discrepancy has been solved.
- r_command->memory_barrier.src_access = resource_tracker->usage_access;
- r_command->memory_barrier.dst_access = new_usage_access;
+ // Memory barriers are pushed regardless of buffer barriers being used or not.
+ r_command->memory_barrier.src_access = r_command->memory_barrier.src_access | resource_tracker->usage_access;
+ r_command->memory_barrier.dst_access = r_command->memory_barrier.dst_access | new_usage_access;
} else {
DEV_ASSERT(false && "Resource tracker does not contain a valid buffer or texture ID.");
}
@@ -451,10 +468,12 @@ void RenderingDeviceGraph::_add_command_to_graph(ResourceTracker **p_resource_tr
if (different_usage) {
// Even if the usage of the resource isn't a write usage explicitly, a different usage implies a transition and it should therefore be considered a write.
- write_usage = true;
+ // In the case of buffers however, this is not exactly necessary if the driver does not consider different buffer usages as different states.
+ write_usage = write_usage || bool(resource_tracker->texture_driver_id) || driver_buffers_require_transitions;
resource_tracker->usage = new_resource_usage;
}
+ bool command_intersection_failed = false;
if (search_tracker->write_command_or_list_index >= 0) {
if (search_tracker->write_command_list_enabled) {
// Make this command adjacent to any commands that wrote to this resource and intersect with the slice if it applies.
@@ -466,7 +485,7 @@ void RenderingDeviceGraph::_add_command_to_graph(ResourceTracker **p_resource_tr
if (!resource_has_parent || search_tracker_rect.intersects(write_list_node.subresources)) {
if (write_list_node.command_index == p_command_index) {
ERR_FAIL_COND_MSG(!resource_has_parent, "Command can't have itself as a dependency.");
- } else {
+ } else if (_check_command_intersection(resource_tracker, write_list_node.command_index, p_command_index)) {
// Command is dependent on this command. Add this command to the adjacency list of the write command.
_add_adjacent_command(write_list_node.command_index, p_command_index, r_command);
@@ -482,6 +501,8 @@ void RenderingDeviceGraph::_add_command_to_graph(ResourceTracker **p_resource_tr
write_list_index = write_list_node.next_list_index;
continue;
}
+ } else {
+ command_intersection_failed = true;
}
}
@@ -492,14 +513,16 @@ void RenderingDeviceGraph::_add_command_to_graph(ResourceTracker **p_resource_tr
// The index is just the latest command index that wrote to the resource.
if (search_tracker->write_command_or_list_index == p_command_index) {
ERR_FAIL_MSG("Command can't have itself as a dependency.");
- } else {
+ } else if (_check_command_intersection(resource_tracker, search_tracker->write_command_or_list_index, p_command_index)) {
_add_adjacent_command(search_tracker->write_command_or_list_index, p_command_index, r_command);
+ } else {
+ command_intersection_failed = true;
}
}
}
if (write_usage) {
- if (resource_has_parent) {
+ if (resource_has_parent || command_intersection_failed) {
if (!search_tracker->write_command_list_enabled && search_tracker->write_command_or_list_index >= 0) {
// Write command list was not being used but there was a write command recorded. Add a new node with the entire parent resource's subresources and the recorded command index to the list.
const RDD::TextureSubresourceRange &tracker_subresources = search_tracker->texture_subresources;
@@ -1320,6 +1343,7 @@ void RenderingDeviceGraph::initialize(RDD *p_driver, RenderingContextDriver::Dev
driver_honors_barriers = driver->api_trait_get(RDD::API_TRAIT_HONORS_PIPELINE_BARRIERS);
driver_clears_with_copy_engine = driver->api_trait_get(RDD::API_TRAIT_CLEARS_WITH_COPY_ENGINE);
+ driver_buffers_require_transitions = driver->api_trait_get(RDD::API_TRAIT_BUFFERS_REQUIRE_TRANSITIONS);
}
void RenderingDeviceGraph::finalize() {
diff --git a/servers/rendering/rendering_device_graph.h b/servers/rendering/rendering_device_graph.h
index 0148c6b78d..6c8aa23fe7 100644
--- a/servers/rendering/rendering_device_graph.h
+++ b/servers/rendering/rendering_device_graph.h
@@ -639,6 +639,7 @@ private:
BarrierGroup barrier_group;
bool driver_honors_barriers : 1;
bool driver_clears_with_copy_engine : 1;
+ bool driver_buffers_require_transitions : 1;
WorkaroundsState workarounds_state;
TightLocalVector<Frame> frames;
uint32_t frame = 0;
@@ -650,6 +651,7 @@ private:
static bool _is_write_usage(ResourceUsage p_usage);
static RDD::TextureLayout _usage_to_image_layout(ResourceUsage p_usage);
static RDD::BarrierAccessBits _usage_to_access_bits(ResourceUsage p_usage);
+ bool _check_command_intersection(ResourceTracker *p_resource_tracker, int32_t p_previous_command_index, int32_t p_command_index) const;
int32_t _add_to_command_list(int32_t p_command_index, int32_t p_list_index);
void _add_adjacent_command(int32_t p_previous_command_index, int32_t p_command_index, RecordedCommand *r_command);
int32_t _add_to_slice_read_list(int32_t p_command_index, Rect2i p_subresources, int32_t p_list_index);
diff --git a/servers/rendering/rendering_method.h b/servers/rendering/rendering_method.h
index ee864d79a8..630aa8a75a 100644
--- a/servers/rendering/rendering_method.h
+++ b/servers/rendering/rendering_method.h
@@ -170,6 +170,7 @@ public:
virtual void environment_set_bg_energy(RID p_env, float p_multiplier, float p_exposure_value) = 0;
virtual void environment_set_canvas_max_layer(RID p_env, int p_max_layer) = 0;
virtual void environment_set_ambient_light(RID p_env, const Color &p_color, RS::EnvironmentAmbientSource p_ambient = RS::ENV_AMBIENT_SOURCE_BG, float p_energy = 1.0, float p_sky_contribution = 0.0, RS::EnvironmentReflectionSource p_reflection_source = RS::ENV_REFLECTION_SOURCE_BG) = 0;
+ virtual void environment_set_camera_feed_id(RID p_env, int p_camera_feed_id) = 0;
virtual RS::EnvironmentBG environment_get_background(RID p_Env) const = 0;
virtual RID environment_get_sky(RID p_env) const = 0;
diff --git a/servers/rendering/rendering_server_default.h b/servers/rendering/rendering_server_default.h
index 739fc06ae6..8c9942ad76 100644
--- a/servers/rendering/rendering_server_default.h
+++ b/servers/rendering/rendering_server_default.h
@@ -788,10 +788,8 @@ public:
FUNC2(environment_set_canvas_max_layer, RID, int)
FUNC6(environment_set_ambient_light, RID, const Color &, EnvironmentAmbientSource, float, float, EnvironmentReflectionSource)
-// FIXME: Disabled during Vulkan refactoring, should be ported.
-#if 0
FUNC2(environment_set_camera_feed_id, RID, int)
-#endif
+
FUNC6(environment_set_ssr, RID, bool, int, float, float, float)
FUNC1(environment_set_ssr_roughness_quality, EnvironmentSSRRoughnessQuality)
diff --git a/servers/rendering/shader_language.h b/servers/rendering/shader_language.h
index 8936351eac..fe3f56a548 100644
--- a/servers/rendering/shader_language.h
+++ b/servers/rendering/shader_language.h
@@ -1215,7 +1215,7 @@ public:
struct ShaderCompileInfo {
HashMap<StringName, FunctionInfo> functions;
Vector<ModeInfo> render_modes;
- VaryingFunctionNames varying_function_names = VaryingFunctionNames();
+ VaryingFunctionNames varying_function_names;
HashSet<String> shader_types;
GlobalShaderUniformGetTypeFunc global_shader_uniform_type_func = nullptr;
bool is_include = false;
diff --git a/servers/rendering/storage/environment_storage.cpp b/servers/rendering/storage/environment_storage.cpp
index 84d18ecc70..393ef723bc 100644
--- a/servers/rendering/storage/environment_storage.cpp
+++ b/servers/rendering/storage/environment_storage.cpp
@@ -191,6 +191,18 @@ RS::EnvironmentReflectionSource RendererEnvironmentStorage::environment_get_refl
return env->reflection_source;
}
+void RendererEnvironmentStorage::environment_set_camera_feed_id(RID p_env, int p_camera_feed_id) {
+ Environment *env = environment_owner.get_or_null(p_env);
+ ERR_FAIL_NULL(env);
+ env->camera_feed_id = p_camera_feed_id;
+}
+
+int RendererEnvironmentStorage::environment_get_camera_feed_id(RID p_env) const {
+ Environment *env = environment_owner.get_or_null(p_env);
+ ERR_FAIL_NULL_V(env, -1);
+ return env->camera_feed_id;
+}
+
// Tonemap
void RendererEnvironmentStorage::environment_set_tonemap(RID p_env, RS::EnvironmentToneMapper p_tone_mapper, float p_exposure, float p_white) {
diff --git a/servers/rendering/storage/environment_storage.h b/servers/rendering/storage/environment_storage.h
index ced579d513..11d6c5f75c 100644
--- a/servers/rendering/storage/environment_storage.h
+++ b/servers/rendering/storage/environment_storage.h
@@ -59,6 +59,7 @@ private:
float ambient_light_energy = 1.0;
float ambient_sky_contribution = 1.0;
RS::EnvironmentReflectionSource reflection_source = RS::ENV_REFLECTION_SOURCE_BG;
+ int camera_feed_id = 0;
// Tonemap
RS::EnvironmentToneMapper tone_mapper;
@@ -183,10 +184,8 @@ public:
void environment_set_bg_energy(RID p_env, float p_multiplier, float p_exposure_value);
void environment_set_canvas_max_layer(RID p_env, int p_max_layer);
void environment_set_ambient_light(RID p_env, const Color &p_color, RS::EnvironmentAmbientSource p_ambient = RS::ENV_AMBIENT_SOURCE_BG, float p_energy = 1.0, float p_sky_contribution = 0.0, RS::EnvironmentReflectionSource p_reflection_source = RS::ENV_REFLECTION_SOURCE_BG);
-// FIXME: Disabled during Vulkan refactoring, should be ported.
-#if 0
void environment_set_camera_feed_id(RID p_env, int p_camera_feed_id);
-#endif
+ int environment_get_camera_feed_id(RID p_env) const;
RS::EnvironmentBG environment_get_background(RID p_env) const;
RID environment_get_sky(RID p_env) const;
diff --git a/servers/rendering_server.cpp b/servers/rendering_server.cpp
index 19bbd4d13d..4dfe48f0d6 100644
--- a/servers/rendering_server.cpp
+++ b/servers/rendering_server.cpp
@@ -311,7 +311,7 @@ RID RenderingServer::get_white_texture() {
w[i] = 255;
}
}
- Ref<Image> white = memnew(Image(4, 4, 0, Image::FORMAT_RGB8, wt));
+ Ref<Image> white = memnew(Image(4, 4, false, Image::FORMAT_RGB8, wt));
white_texture = texture_2d_create(white);
return white_texture;
}
@@ -2982,6 +2982,7 @@ void RenderingServer::_bind_methods() {
ClassDB::bind_method(D_METHOD("environment_create"), &RenderingServer::environment_create);
ClassDB::bind_method(D_METHOD("environment_set_background", "env", "bg"), &RenderingServer::environment_set_background);
+ ClassDB::bind_method(D_METHOD("environment_set_camera_id", "env", "id"), &RenderingServer::environment_set_camera_feed_id);
ClassDB::bind_method(D_METHOD("environment_set_sky", "env", "sky"), &RenderingServer::environment_set_sky);
ClassDB::bind_method(D_METHOD("environment_set_sky_custom_fov", "env", "scale"), &RenderingServer::environment_set_sky_custom_fov);
ClassDB::bind_method(D_METHOD("environment_set_sky_orientation", "env", "orientation"), &RenderingServer::environment_set_sky_orientation);
diff --git a/servers/rendering_server.h b/servers/rendering_server.h
index f4f6592c6d..d3aff46832 100644
--- a/servers/rendering_server.h
+++ b/servers/rendering_server.h
@@ -1182,6 +1182,7 @@ public:
virtual void environment_set_bg_energy(RID p_env, float p_multiplier, float p_exposure_value) = 0;
virtual void environment_set_canvas_max_layer(RID p_env, int p_max_layer) = 0;
virtual void environment_set_ambient_light(RID p_env, const Color &p_color, EnvironmentAmbientSource p_ambient = ENV_AMBIENT_SOURCE_BG, float p_energy = 1.0, float p_sky_contribution = 0.0, EnvironmentReflectionSource p_reflection_source = ENV_REFLECTION_SOURCE_BG) = 0;
+ virtual void environment_set_camera_feed_id(RID p_env, int p_camera_feed_id) = 0;
enum EnvironmentGlowBlendMode {
ENV_GLOW_BLEND_MODE_ADDITIVE,
diff --git a/tests/scene/test_primitives.h b/tests/scene/test_primitives.h
index cab37388fc..ad1a8e8440 100644
--- a/tests/scene/test_primitives.h
+++ b/tests/scene/test_primitives.h
@@ -106,8 +106,9 @@ TEST_CASE("[SceneTree][Primitive][Capsule] Capsule Primitive") {
float dist_to_yaxis = 0.f;
for (Vector3 point : points) {
float new_dist_to_y = point.x * point.x + point.z * point.z;
- if (new_dist_to_y > dist_to_yaxis)
+ if (new_dist_to_y > dist_to_yaxis) {
dist_to_yaxis = new_dist_to_y;
+ }
}
CHECK(dist_to_yaxis <= radius * radius);
@@ -116,10 +117,12 @@ TEST_CASE("[SceneTree][Primitive][Capsule] Capsule Primitive") {
float max_y{ 0.f };
float min_y{ 0.f };
for (Vector3 point : points) {
- if (point.y > max_y)
+ if (point.y > max_y) {
max_y = point.y;
- if (point.y < min_y)
+ }
+ if (point.y < min_y) {
min_y = point.y;
+ }
}
CHECK(max_y - min_y <= height);
@@ -198,12 +201,14 @@ TEST_CASE("[SceneTree][Primitive][Box] Box Primitive") {
for (const Vector3 &normal : normals) {
bool add_normal{ true };
for (const Vector3 &vec : distinct_normals) {
- if (vec.is_equal_approx(normal))
+ if (vec.is_equal_approx(normal)) {
add_normal = false;
+ }
}
- if (add_normal)
+ if (add_normal) {
distinct_normals.push_back(normal);
+ }
}
CHECK_MESSAGE(distinct_normals.size() == 6,
@@ -220,8 +225,9 @@ TEST_CASE("[SceneTree][Primitive][Box] Box Primitive") {
break;
}
}
- if (!normal_correct_direction)
+ if (!normal_correct_direction) {
break;
+ }
}
CHECK_MESSAGE(normal_correct_direction,
diff --git a/tests/servers/test_navigation_server_3d.h b/tests/servers/test_navigation_server_3d.h
index f2863248eb..f622aac6ad 100644
--- a/tests/servers/test_navigation_server_3d.h
+++ b/tests/servers/test_navigation_server_3d.h
@@ -51,7 +51,7 @@ public:
}
unsigned function1_calls{ 0 };
- Variant function1_latest_arg0{};
+ Variant function1_latest_arg0;
};
static inline Array build_array() {
diff --git a/tests/test_macros.h b/tests/test_macros.h
index a736030293..3cfe8dac37 100644
--- a/tests/test_macros.h
+++ b/tests/test_macros.h
@@ -476,6 +476,6 @@ public:
for (int i = 0; i < string_list.size(); ++i) { \
CHECK(string_list[i] == m_slices[i]); \
} \
- } while (0)
+ } while (false)
#endif // TEST_MACROS_H
diff --git a/thirdparty/README.md b/thirdparty/README.md
index 87f8782e7f..b473d9e693 100644
--- a/thirdparty/README.md
+++ b/thirdparty/README.md
@@ -99,9 +99,14 @@ Files extracted from upstream source:
## certs
- Upstream: Mozilla, via https://github.com/bagder/ca-bundle
-- Version: git (c5a419971b1bec220368c619aaafd0b818aa119f, 2024)
+- Version: git (4d3fe6683f651d96be1bbef316b201e9b33b274d, 2024),
+ generated from mozilla-release changeset b8ea2342548b8571e58f9176d9555ccdb5ec199f
- License: MPL 2.0
+Files extracted from upstream source:
+
+- `ca-bundle.crt` renamed to `ca-certificates.crt`
+
## clipper2
@@ -709,7 +714,7 @@ Collection of single-file libraries used in Godot components.
* Modifications: use `const char*` instead of `char*` for input string
- `smolv.{cpp,h}`
* Upstream: https://github.com/aras-p/smol-v
- * Version: git (4b52c165c13763051a18e80ffbc2ee436314ceb2, 2020)
+ * Version: git (9dd54c379ac29fa148cb1b829bb939ba7381d8f4, 2024)
* License: Public Domain or MIT
- `stb_rect_pack.h`
* Upstream: https://github.com/nothings/stb
diff --git a/thirdparty/certs/ca-certificates.crt b/thirdparty/certs/ca-certificates.crt
index f437587091..c559ab795b 100644
--- a/thirdparty/certs/ca-certificates.crt
+++ b/thirdparty/certs/ca-certificates.crt
@@ -1,7 +1,9 @@
##
## Bundle of CA Root Certificates
##
-## Certificate data from Mozilla as of: Mon Mar 11 15:15:21 2024 GMT
+## Certificate data from Mozilla as of: Sat Oct 19 21:26:09 2024 GMT
+##
+## Find updated versions here: https://curl.se/docs/caextract.html
##
## This is a bundle of X.509 certificates of public Certificate Authorities
## (CA). These were automatically extracted from Mozilla's root certificates
@@ -14,7 +16,7 @@
## Just configure this file as the SSLCACertificateFile.
##
## Conversion done with mk-ca-bundle.pl version 1.29.
-## SHA256: 4d96bd539f4719e9ace493757afbe4a23ee8579de1c97fbebc50bba3c12e8c1e
+## SHA256: 36105b01631f9fc03b1eca779b44a30a1a5890b9bf8dc07ccb001a07301e01cf
##
@@ -2600,36 +2602,6 @@ vLtoURMMA/cVi4RguYv/Uo7njLwcAjA8+RHUjE7AwWHCFUyqqx0LMV87HOIAl0Qx5v5zli/altP+
CAezNIm8BZ/3Hobui3A=
-----END CERTIFICATE-----
-GLOBALTRUST 2020
-================
------BEGIN CERTIFICATE-----
-MIIFgjCCA2qgAwIBAgILWku9WvtPilv6ZeUwDQYJKoZIhvcNAQELBQAwTTELMAkGA1UEBhMCQVQx
-IzAhBgNVBAoTGmUtY29tbWVyY2UgbW9uaXRvcmluZyBHbWJIMRkwFwYDVQQDExBHTE9CQUxUUlVT
-VCAyMDIwMB4XDTIwMDIxMDAwMDAwMFoXDTQwMDYxMDAwMDAwMFowTTELMAkGA1UEBhMCQVQxIzAh
-BgNVBAoTGmUtY29tbWVyY2UgbW9uaXRvcmluZyBHbWJIMRkwFwYDVQQDExBHTE9CQUxUUlVTVCAy
-MDIwMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAri5WrRsc7/aVj6B3GyvTY4+ETUWi
-D59bRatZe1E0+eyLinjF3WuvvcTfk0Uev5E4C64OFudBc/jbu9G4UeDLgztzOG53ig9ZYybNpyrO
-VPu44sB8R85gfD+yc/LAGbaKkoc1DZAoouQVBGM+uq/ufF7MpotQsjj3QWPKzv9pj2gOlTblzLmM
-CcpL3TGQlsjMH/1WljTbjhzqLL6FLmPdqqmV0/0plRPwyJiT2S0WR5ARg6I6IqIoV6Lr/sCMKKCm
-fecqQjuCgGOlYx8ZzHyyZqjC0203b+J+BlHZRYQfEs4kUmSFC0iAToexIiIwquuuvuAC4EDosEKA
-A1GqtH6qRNdDYfOiaxaJSaSjpCuKAsR49GiKweR6NrFvG5Ybd0mN1MkGco/PU+PcF4UgStyYJ9OR
-JitHHmkHr96i5OTUawuzXnzUJIBHKWk7buis/UDr2O1xcSvy6Fgd60GXIsUf1DnQJ4+H4xj04KlG
-DfV0OoIu0G4skaMxXDtG6nsEEFZegB31pWXogvziB4xiRfUg3kZwhqG8k9MedKZssCz3AwyIDMvU
-clOGvGBG85hqwvG/Q/lwIHfKN0F5VVJjjVsSn8VoxIidrPIwq7ejMZdnrY8XD2zHc+0klGvIg5rQ
-mjdJBKuxFshsSUktq6HQjJLyQUp5ISXbY9e2nKd+Qmn7OmMCAwEAAaNjMGEwDwYDVR0TAQH/BAUw
-AwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFNwuH9FhN3nkq9XVsxJxaD1qaJwiMB8GA1Ud
-IwQYMBaAFNwuH9FhN3nkq9XVsxJxaD1qaJwiMA0GCSqGSIb3DQEBCwUAA4ICAQCR8EICaEDuw2jA
-VC/f7GLDw56KoDEoqoOOpFaWEhCGVrqXctJUMHytGdUdaG/7FELYjQ7ztdGl4wJCXtzoRlgHNQIw
-4Lx0SsFDKv/bGtCwr2zD/cuz9X9tAy5ZVp0tLTWMstZDFyySCstd6IwPS3BD0IL/qMy/pJTAvoe9
-iuOTe8aPmxadJ2W8esVCgmxcB9CpwYhgROmYhRZf+I/KARDOJcP5YBugxZfD0yyIMaK9MOzQ0MAS
-8cE54+X1+NZK3TTN+2/BT+MAi1bikvcoskJ3ciNnxz8RFbLEAwW+uxF7Cr+obuf/WEPPm2eggAe2
-HcqtbepBEX4tdJP7wry+UUTF72glJ4DjyKDUEuzZpTcdN3y0kcra1LGWge9oXHYQSa9+pTeAsRxS
-vTOBTI/53WXZFM2KJVj04sWDpQmQ1GwUY7VA3+vA/MRYfg0UFodUJ25W5HCEuGwyEn6CMUO+1918
-oa2u1qsgEu8KwxCMSZY13At1XrFP1U80DhEgB3VDRemjEdqso5nCtnkn4rnvyOL2NSl6dPrFf4IF
-YqYK6miyeUcGbvJXqBUzxvd4Sj1Ce2t+/vdG6tHrju+IaFvowdlxfv1k7/9nR4hYJS8+hge9+6jl
-gqispdNpQ80xiEmEU5LAsTkbOYMBMMTyqfrQA71yN2BWHzZ8vTmR9W0Nv3vXkg==
------END CERTIFICATE-----
-
ANF Secure Server Root CA
=========================
-----BEGIN CERTIFICATE-----
@@ -3579,3 +3551,116 @@ wPfc5+pbrrLMtTWGS9DiP7bY+A4A7l3j941Y/8+LN+ljX273CXE2whJdV/LItM3z7gLfEdxquVeE
HVlNjM7IDiPCtyaaEBRx/pOyiriA8A4QntOoUAw3gi/q4Iqd4Sw5/7W0cwDk90imc6y/st53BIe0
o82bNSQ3+pCTE4FCxpgmdTdmQRCsu/WU48IxK63nI1bMNSWSs1A=
-----END CERTIFICATE-----
+
+FIRMAPROFESIONAL CA ROOT-A WEB
+==============================
+-----BEGIN CERTIFICATE-----
+MIICejCCAgCgAwIBAgIQMZch7a+JQn81QYehZ1ZMbTAKBggqhkjOPQQDAzBuMQswCQYDVQQGEwJF
+UzEcMBoGA1UECgwTRmlybWFwcm9mZXNpb25hbCBTQTEYMBYGA1UEYQwPVkFURVMtQTYyNjM0MDY4
+MScwJQYDVQQDDB5GSVJNQVBST0ZFU0lPTkFMIENBIFJPT1QtQSBXRUIwHhcNMjIwNDA2MDkwMTM2
+WhcNNDcwMzMxMDkwMTM2WjBuMQswCQYDVQQGEwJFUzEcMBoGA1UECgwTRmlybWFwcm9mZXNpb25h
+bCBTQTEYMBYGA1UEYQwPVkFURVMtQTYyNjM0MDY4MScwJQYDVQQDDB5GSVJNQVBST0ZFU0lPTkFM
+IENBIFJPT1QtQSBXRUIwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAARHU+osEaR3xyrq89Zfe9MEkVz6
+iMYiuYMQYneEMy3pA4jU4DP37XcsSmDq5G+tbbT4TIqk5B/K6k84Si6CcyvHZpsKjECcfIr28jlg
+st7L7Ljkb+qbXbdTkBgyVcUgt5SjYzBhMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAUk+FD
+Y1w8ndYn81LsF7Kpryz3dvgwHQYDVR0OBBYEFJPhQ2NcPJ3WJ/NS7Beyqa8s93b4MA4GA1UdDwEB
+/wQEAwIBBjAKBggqhkjOPQQDAwNoADBlAjAdfKR7w4l1M+E7qUW/Runpod3JIha3RxEL2Jq68cgL
+cFBTApFwhVmpHqTm6iMxoAACMQD94vizrxa5HnPEluPBMBnYfubDl94cT7iJLzPrSA8Z94dGXSaQ
+pYXFuXqUPoeovQA=
+-----END CERTIFICATE-----
+
+TWCA CYBER Root CA
+==================
+-----BEGIN CERTIFICATE-----
+MIIFjTCCA3WgAwIBAgIQQAE0jMIAAAAAAAAAATzyxjANBgkqhkiG9w0BAQwFADBQMQswCQYDVQQG
+EwJUVzESMBAGA1UEChMJVEFJV0FOLUNBMRAwDgYDVQQLEwdSb290IENBMRswGQYDVQQDExJUV0NB
+IENZQkVSIFJvb3QgQ0EwHhcNMjIxMTIyMDY1NDI5WhcNNDcxMTIyMTU1OTU5WjBQMQswCQYDVQQG
+EwJUVzESMBAGA1UEChMJVEFJV0FOLUNBMRAwDgYDVQQLEwdSb290IENBMRswGQYDVQQDExJUV0NB
+IENZQkVSIFJvb3QgQ0EwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDG+Moe2Qkgfh1s
+Ts6P40czRJzHyWmqOlt47nDSkvgEs1JSHWdyKKHfi12VCv7qze33Kc7wb3+szT3vsxxFavcokPFh
+V8UMxKNQXd7UtcsZyoC5dc4pztKFIuwCY8xEMCDa6pFbVuYdHNWdZsc/34bKS1PE2Y2yHer43CdT
+o0fhYcx9tbD47nORxc5zb87uEB8aBs/pJ2DFTxnk684iJkXXYJndzk834H/nY62wuFm40AZoNWDT
+Nq5xQwTxaWV4fPMf88oon1oglWa0zbfuj3ikRRjpJi+NmykosaS3Om251Bw4ckVYsV7r8Cibt4LK
+/c/WMw+f+5eesRycnupfXtuq3VTpMCEobY5583WSjCb+3MX2w7DfRFlDo7YDKPYIMKoNM+HvnKkH
+IuNZW0CP2oi3aQiotyMuRAlZN1vH4xfyIutuOVLF3lSnmMlLIJXcRolftBL5hSmO68gnFSDAS9TM
+fAxsNAwmmyYxpjyn9tnQS6Jk/zuZQXLB4HCX8SS7K8R0IrGsayIyJNN4KsDAoS/xUgXJP+92ZuJF
+2A09rZXIx4kmyA+upwMu+8Ff+iDhcK2wZSA3M2Cw1a/XDBzCkHDXShi8fgGwsOsVHkQGzaRP6AzR
+wyAQ4VRlnrZR0Bp2a0JaWHY06rc3Ga4udfmW5cFZ95RXKSWNOkyrTZpB0F8mAwIDAQABo2MwYTAO
+BgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBSdhWEUfMFib5do5E83
+QOGt4A1WNzAdBgNVHQ4EFgQUnYVhFHzBYm+XaORPN0DhreANVjcwDQYJKoZIhvcNAQEMBQADggIB
+AGSPesRiDrWIzLjHhg6hShbNcAu3p4ULs3a2D6f/CIsLJc+o1IN1KriWiLb73y0ttGlTITVX1olN
+c79pj3CjYcya2x6a4CD4bLubIp1dhDGaLIrdaqHXKGnK/nZVekZn68xDiBaiA9a5F/gZbG0jAn/x
+X9AKKSM70aoK7akXJlQKTcKlTfjF/biBzysseKNnTKkHmvPfXvt89YnNdJdhEGoHK4Fa0o635yDR
+IG4kqIQnoVesqlVYL9zZyvpoBJ7tRCT5dEA7IzOrg1oYJkK2bVS1FmAwbLGg+LhBoF1JSdJlBTrq
+/p1hvIbZv97Tujqxf36SNI7JAG7cmL3c7IAFrQI932XtCwP39xaEBDG6k5TY8hL4iuO/Qq+n1M0R
+FxbIQh0UqEL20kCGoE8jypZFVmAGzbdVAaYBlGX+bgUJurSkquLvWL69J1bY73NxW0Qz8ppy6rBe
+Pm6pUlvscG21h483XjyMnM7k8M4MZ0HMzvaAq07MTFb1wWFZk7Q+ptq4NxKfKjLji7gh7MMrZQzv
+It6IKTtM1/r+t+FHvpw+PoP7UV31aPcuIYXcv/Fa4nzXxeSDwWrruoBa3lwtcHb4yOWHh8qgnaHl
+IhInD0Q9HWzq1MKLL295q39QpsQZp6F6t5b5wR9iWqJDB0BeJsas7a5wFsWqynKKTbDPAYsDP27X
+-----END CERTIFICATE-----
+
+SecureSign Root CA12
+====================
+-----BEGIN CERTIFICATE-----
+MIIDcjCCAlqgAwIBAgIUZvnHwa/swlG07VOX5uaCwysckBYwDQYJKoZIhvcNAQELBQAwUTELMAkG
+A1UEBhMCSlAxIzAhBgNVBAoTGkN5YmVydHJ1c3QgSmFwYW4gQ28uLCBMdGQuMR0wGwYDVQQDExRT
+ZWN1cmVTaWduIFJvb3QgQ0ExMjAeFw0yMDA0MDgwNTM2NDZaFw00MDA0MDgwNTM2NDZaMFExCzAJ
+BgNVBAYTAkpQMSMwIQYDVQQKExpDeWJlcnRydXN0IEphcGFuIENvLiwgTHRkLjEdMBsGA1UEAxMU
+U2VjdXJlU2lnbiBSb290IENBMTIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC6OcE3
+emhFKxS06+QT61d1I02PJC0W6K6OyX2kVzsqdiUzg2zqMoqUm048luT9Ub+ZyZN+v/mtp7JIKwcc
+J/VMvHASd6SFVLX9kHrko+RRWAPNEHl57muTH2SOa2SroxPjcf59q5zdJ1M3s6oYwlkm7Fsf0uZl
+fO+TvdhYXAvA42VvPMfKWeP+bl+sg779XSVOKik71gurFzJ4pOE+lEa+Ym6b3kaosRbnhW70CEBF
+EaCeVESE99g2zvVQR9wsMJvuwPWW0v4JhscGWa5Pro4RmHvzC1KqYiaqId+OJTN5lxZJjfU+1Uef
+NzFJM3IFTQy2VYzxV4+Kh9GtxRESOaCtAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0P
+AQH/BAQDAgEGMB0GA1UdDgQWBBRXNPN0zwRL1SXm8UC2LEzZLemgrTANBgkqhkiG9w0BAQsFAAOC
+AQEAPrvbFxbS8hQBICw4g0utvsqFepq2m2um4fylOqyttCg6r9cBg0krY6LdmmQOmFxv3Y67ilQi
+LUoT865AQ9tPkbeGGuwAtEGBpE/6aouIs3YIcipJQMPTw4WJmBClnW8Zt7vPemVV2zfrPIpyMpce
+mik+rY3moxtt9XUa5rBouVui7mlHJzWhhpmA8zNL4WukJsPvdFlseqJkth5Ew1DgDzk9qTPxpfPS
+vWKErI4cqc1avTc7bgoitPQV55FYxTpE05Uo2cBl6XLK0A+9H7MV2anjpEcJnuDLN/v9vZfVvhga
+aaI5gdka9at/yOPiZwud9AzqVN/Ssq+xIvEg37xEHA==
+-----END CERTIFICATE-----
+
+SecureSign Root CA14
+====================
+-----BEGIN CERTIFICATE-----
+MIIFcjCCA1qgAwIBAgIUZNtaDCBO6Ncpd8hQJ6JaJ90t8sswDQYJKoZIhvcNAQEMBQAwUTELMAkG
+A1UEBhMCSlAxIzAhBgNVBAoTGkN5YmVydHJ1c3QgSmFwYW4gQ28uLCBMdGQuMR0wGwYDVQQDExRT
+ZWN1cmVTaWduIFJvb3QgQ0ExNDAeFw0yMDA0MDgwNzA2MTlaFw00NTA0MDgwNzA2MTlaMFExCzAJ
+BgNVBAYTAkpQMSMwIQYDVQQKExpDeWJlcnRydXN0IEphcGFuIENvLiwgTHRkLjEdMBsGA1UEAxMU
+U2VjdXJlU2lnbiBSb290IENBMTQwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDF0nqh
+1oq/FjHQmNE6lPxauG4iwWL3pwon71D2LrGeaBLwbCRjOfHw3xDG3rdSINVSW0KZnvOgvlIfX8xn
+bacuUKLBl422+JX1sLrcneC+y9/3OPJH9aaakpUqYllQC6KxNedlsmGy6pJxaeQp8E+BgQQ8sqVb
+1MWoWWd7VRxJq3qdwudzTe/NCcLEVxLbAQ4jeQkHO6Lo/IrPj8BGJJw4J+CDnRugv3gVEOuGTgpa
+/d/aLIJ+7sr2KeH6caH3iGicnPCNvg9JkdjqOvn90Ghx2+m1K06Ckm9mH+Dw3EzsytHqunQG+bOE
+kJTRX45zGRBdAuVwpcAQ0BB8b8VYSbSwbprafZX1zNoCr7gsfXmPvkPx+SgojQlD+Ajda8iLLCSx
+jVIHvXiby8posqTdDEx5YMaZ0ZPxMBoH064iwurO8YQJzOAUbn8/ftKChazcqRZOhaBgy/ac18iz
+ju3Gm5h1DVXoX+WViwKkrkMpKBGk5hIwAUt1ax5mnXkvpXYvHUC0bcl9eQjs0Wq2XSqypWa9a4X0
+dFbD9ed1Uigspf9mR6XU/v6eVL9lfgHWMI+lNpyiUBzuOIABSMbHdPTGrMNASRZhdCyvjG817XsY
+AFs2PJxQDcqSMxDxJklt33UkN4Ii1+iW/RVLApY+B3KVfqs9TC7XyvDf4Fg/LS8EmjijAQIDAQAB
+o0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUBpOjCl4oaTeq
+YR3r6/wtbyPk86AwDQYJKoZIhvcNAQEMBQADggIBAJaAcgkGfpzMkwQWu6A6jZJOtxEaCnFxEM0E
+rX+lRVAQZk5KQaID2RFPeje5S+LGjzJmdSX7684/AykmjbgWHfYfM25I5uj4V7Ibed87hwriZLoA
+ymzvftAj63iP/2SbNDefNWWipAA9EiOWWF3KY4fGoweITedpdopTzfFP7ELyk+OZpDc8h7hi2/Ds
+Hzc/N19DzFGdtfCXwreFamgLRB7lUe6TzktuhsHSDCRZNhqfLJGP4xjblJUK7ZGqDpncllPjYYPG
+FrojutzdfhrGe0K22VoF3Jpf1d+42kd92jjbrDnVHmtsKheMYc2xbXIBw8MgAGJoFjHVdqqGuw6q
+nsb58Nn4DSEC5MUoFlkRudlpcyqSeLiSV5sI8jrlL5WwWLdrIBRtFO8KvH7YVdiI2i/6GaX7i+B/
+OfVyK4XELKzvGUWSTLNhB9xNH27SgRNcmvMSZ4PPmz+Ln52kuaiWA3rF7iDeM9ovnhp6dB7h7sxa
+OgTdsxoEqBRjrLdHEoOabPXm6RUVkRqEGQ6UROcSjiVbgGcZ3GOTEAtlLor6CZpO2oYofaphNdgO
+pygau1LgePhsumywbrmHXumZNTfxPWQrqaA0k89jL9WB365jJ6UeTo3cKXhZ+PmhIIynJkBugnLN
+eLLIjzwec+fBH7/PzqUqm9tEZDKgu39cJRNItX+S
+-----END CERTIFICATE-----
+
+SecureSign Root CA15
+====================
+-----BEGIN CERTIFICATE-----
+MIICIzCCAamgAwIBAgIUFhXHw9hJp75pDIqI7fBw+d23PocwCgYIKoZIzj0EAwMwUTELMAkGA1UE
+BhMCSlAxIzAhBgNVBAoTGkN5YmVydHJ1c3QgSmFwYW4gQ28uLCBMdGQuMR0wGwYDVQQDExRTZWN1
+cmVTaWduIFJvb3QgQ0ExNTAeFw0yMDA0MDgwODMyNTZaFw00NTA0MDgwODMyNTZaMFExCzAJBgNV
+BAYTAkpQMSMwIQYDVQQKExpDeWJlcnRydXN0IEphcGFuIENvLiwgTHRkLjEdMBsGA1UEAxMUU2Vj
+dXJlU2lnbiBSb290IENBMTUwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQLUHSNZDKZmbPSYAi4Io5G
+dCx4wCtELW1fHcmuS1Iggz24FG1Th2CeX2yF2wYUleDHKP+dX+Sq8bOLbe1PL0vJSpSRZHX+AezB
+2Ot6lHhWGENfa4HL9rzatAy2KZMIaY+jQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQD
+AgEGMB0GA1UdDgQWBBTrQciu/NWeUUj1vYv0hyCTQSvT9DAKBggqhkjOPQQDAwNoADBlAjEA2S6J
+fl5OpBEHvVnCB96rMjhTKkZEBhd6zlHp4P9mLQlO4E/0BdGF9jVg3PVys0Z9AjBEmEYagoUeYWmJ
+SwdLZrWeqrqgHkHZAXQ6bkU6iYAZezKYVWOr62Nuk22rGwlgMU4=
+-----END CERTIFICATE-----
diff --git a/thirdparty/misc/smolv.cpp b/thirdparty/misc/smolv.cpp
index 26ed7294f9..67b914606c 100644
--- a/thirdparty/misc/smolv.cpp
+++ b/thirdparty/misc/smolv.cpp
@@ -1,5 +1,5 @@
// smol-v - public domain - https://github.com/aras-p/smol-v
-// authored 2016-2020 by Aras Pranckevicius
+// authored 2016-2024 by Aras Pranckevicius
// no warranty implied; use at your own risk
// See end of file for license information.
@@ -1197,8 +1197,8 @@ static bool smolv_CheckGenericHeader(const uint32_t* words, size_t wordCount, ui
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
+ if (headerVersion < 0x00010000 || headerVersion > 0x00010600)
+ return false; // only support 1.0 through 1.6
return true;
}
@@ -2071,7 +2071,7 @@ void smolv::StatsPrint(const Stats* stats)
// This software is available under 2 licenses -- choose whichever you prefer.
// ------------------------------------------------------------------------------
// ALTERNATIVE A - MIT License
-// Copyright (c) 2016-2020 Aras Pranckevicius
+// Copyright (c) 2016-2024 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
diff --git a/thirdparty/misc/smolv.h b/thirdparty/misc/smolv.h
index 798ee4126f..231dfc6f30 100644
--- a/thirdparty/misc/smolv.h
+++ b/thirdparty/misc/smolv.h
@@ -1,5 +1,5 @@
// smol-v - public domain - https://github.com/aras-p/smol-v
-// authored 2016-2020 by Aras Pranckevicius
+// authored 2016-2024 by Aras Pranckevicius
// no warranty implied; use at your own risk
// See end of file for license information.
//
@@ -132,7 +132,7 @@ namespace smolv
// This software is available under 2 licenses -- choose whichever you prefer.
// ------------------------------------------------------------------------------
// ALTERNATIVE A - MIT License
-// Copyright (c) 2016-2020 Aras Pranckevicius
+// Copyright (c) 2016-2024 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
diff --git a/thirdparty/openxr/patches/use-egl-from-glad.diff b/thirdparty/openxr/patches/use-egl-from-glad.diff
new file mode 100644
index 0000000000..29c746d5c7
--- /dev/null
+++ b/thirdparty/openxr/patches/use-egl-from-glad.diff
@@ -0,0 +1,27 @@
+diff -Nur openxr-orig/src/common/xr_dependencies.h openxr/src/common/xr_dependencies.h
+--- openxr-orig/src/common/xr_dependencies.h 2024-11-04 10:38:11.940682727 -0600
++++ openxr/src/common/xr_dependencies.h 2024-11-04 10:38:46.351415476 -0600
+@@ -65,7 +65,11 @@
+ #endif // XR_USE_GRAPHICS_API_OPENGL
+
+ #ifdef XR_USE_GRAPHICS_API_OPENGL_ES
++#ifdef GLAD_ENABLED
++#include "thirdparty/glad/glad/egl.h"
++#else
+ #include <EGL/egl.h>
++#endif
+ #endif // XR_USE_GRAPHICS_API_OPENGL_ES
+
+ #ifdef XR_USE_GRAPHICS_API_VULKAN
+@@ -77,7 +81,11 @@
+ #endif // XR_USE_PLATFORM_WAYLAND
+
+ #ifdef XR_USE_PLATFORM_EGL
++#ifdef GLAD_ENABLED
++#include "thirdparty/glad/glad/egl.h"
++#else
+ #include <EGL/egl.h>
++#endif
+ #endif // XR_USE_PLATFORM_EGL
+
+ #if defined(XR_USE_PLATFORM_XLIB) || defined(XR_USE_PLATFORM_XCB)
diff --git a/thirdparty/openxr/src/common/xr_dependencies.h b/thirdparty/openxr/src/common/xr_dependencies.h
index 55d93bfbac..a192a2ec6d 100644
--- a/thirdparty/openxr/src/common/xr_dependencies.h
+++ b/thirdparty/openxr/src/common/xr_dependencies.h
@@ -65,7 +65,11 @@
#endif // XR_USE_GRAPHICS_API_OPENGL
#ifdef XR_USE_GRAPHICS_API_OPENGL_ES
+#ifdef GLAD_ENABLED
+#include "thirdparty/glad/glad/egl.h"
+#else
#include <EGL/egl.h>
+#endif
#endif // XR_USE_GRAPHICS_API_OPENGL_ES
#ifdef XR_USE_GRAPHICS_API_VULKAN
@@ -77,7 +81,11 @@
#endif // XR_USE_PLATFORM_WAYLAND
#ifdef XR_USE_PLATFORM_EGL
+#ifdef GLAD_ENABLED
+#include "thirdparty/glad/glad/egl.h"
+#else
#include <EGL/egl.h>
+#endif
#endif // XR_USE_PLATFORM_EGL
#if defined(XR_USE_PLATFORM_XLIB) || defined(XR_USE_PLATFORM_XCB)