summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--core/config/project_settings.cpp14
-rw-r--r--core/core_bind.cpp10
-rw-r--r--core/core_bind.h2
-rw-r--r--core/input/godotcontrollerdb.txt2
-rw-r--r--core/input/input.cpp33
-rw-r--r--core/input/input.h4
-rw-r--r--core/io/image.cpp32
-rw-r--r--core/io/image.h1
-rw-r--r--core/io/resource_loader.cpp24
-rw-r--r--core/io/resource_loader.h2
-rw-r--r--core/object/object.cpp8
-rw-r--r--core/object/script_language_extension.cpp1
-rw-r--r--core/register_core_types.cpp1
-rw-r--r--core/string/string_name.cpp9
-rw-r--r--core/string/string_name.h8
-rw-r--r--core/string/translation.compat.inc5
-rw-r--r--core/string/translation.cpp917
-rw-r--r--core/string/translation.h128
-rw-r--r--core/string/translation_server.compat.inc38
-rw-r--r--core/string/translation_server.cpp947
-rw-r--r--core/string/translation_server.h164
-rw-r--r--core/string/ustring.cpp234
-rw-r--r--doc/classes/AnimationNodeBlendSpace2D.xml4
-rw-r--r--doc/classes/ClassDB.xml16
-rw-r--r--doc/classes/Compositor.xml1
-rw-r--r--doc/classes/CompositorEffect.xml1
-rw-r--r--doc/classes/EditorSettings.xml9
-rw-r--r--doc/classes/Geometry2D.xml30
-rw-r--r--doc/classes/Image.xml8
-rw-r--r--doc/classes/NavigationLink2D.xml13
-rw-r--r--doc/classes/NavigationLink3D.xml13
-rw-r--r--doc/classes/NavigationMesh.xml2
-rw-r--r--doc/classes/NavigationMeshSourceGeometryData2D.xml6
-rw-r--r--doc/classes/NavigationMeshSourceGeometryData3D.xml6
-rw-r--r--doc/classes/NavigationPolygon.xml12
-rw-r--r--doc/classes/NavigationServer3D.xml3
-rw-r--r--doc/classes/Performance.xml5
-rw-r--r--doc/classes/Polygon2D.xml3
-rw-r--r--doc/classes/ProjectSettings.xml18
-rw-r--r--doc/classes/RenderingServer.xml10
-rw-r--r--doc/classes/ResourceImporterDynamicFont.xml3
-rw-r--r--doc/classes/ResourceImporterImageFont.xml2
-rw-r--r--doc/classes/ScriptLanguageExtension.xml7
-rw-r--r--doc/classes/ShapeCast2D.xml18
-rw-r--r--doc/classes/ShapeCast3D.xml18
-rw-r--r--doc/classes/TextServer.xml7
-rw-r--r--doc/classes/TextServerExtension.xml8
-rw-r--r--doc/classes/TileMap.xml27
-rw-r--r--doc/classes/TileMapLayer.xml21
-rw-r--r--doc/classes/Timer.xml2
-rw-r--r--doc/classes/Viewport.xml1
-rw-r--r--doc/classes/ViewportTexture.xml5
-rw-r--r--drivers/gles3/rasterizer_canvas_gles3.cpp33
-rw-r--r--drivers/gles3/rasterizer_canvas_gles3.h2
-rw-r--r--drivers/gles3/rasterizer_scene_gles3.cpp26
-rw-r--r--drivers/gles3/rasterizer_scene_gles3.h3
-rw-r--r--drivers/gles3/shaders/scene.glsl107
-rw-r--r--drivers/gles3/shaders/sky.glsl37
-rw-r--r--drivers/gles3/storage/light_storage.cpp3
-rw-r--r--drivers/gles3/storage/light_storage.h1
-rw-r--r--drivers/gles3/storage/material_storage.cpp10
-rw-r--r--drivers/gles3/storage/texture_storage.cpp8
-rw-r--r--drivers/gles3/storage/texture_storage.h1
-rw-r--r--editor/animation_track_editor_plugins.cpp6
-rw-r--r--editor/animation_track_editor_plugins.h6
-rw-r--r--editor/dependency_editor.cpp42
-rw-r--r--editor/dependency_editor.h5
-rw-r--r--editor/doc_tools.cpp2
-rw-r--r--editor/editor_asset_installer.cpp3
-rw-r--r--editor/editor_asset_installer.h1
-rw-r--r--editor/editor_autoload_settings.cpp4
-rw-r--r--editor/editor_file_system.cpp7
-rw-r--r--editor/editor_folding.cpp2
-rw-r--r--editor/editor_locale_dialog.cpp1
-rw-r--r--editor/editor_locale_dialog.h1
-rw-r--r--editor/editor_node.cpp9
-rw-r--r--editor/editor_properties.cpp75
-rw-r--r--editor/editor_properties.h29
-rw-r--r--editor/editor_properties_array_dict.cpp9
-rw-r--r--editor/editor_properties_array_dict.h3
-rw-r--r--editor/editor_property_name_processor.cpp2
-rw-r--r--editor/editor_resource_picker.cpp4
-rw-r--r--editor/editor_settings.cpp13
-rw-r--r--editor/editor_settings.h3
-rw-r--r--editor/editor_translation.cpp1
-rw-r--r--editor/export/export_template_manager.cpp3
-rw-r--r--editor/export/export_template_manager.h1
-rw-r--r--editor/filesystem_dock.cpp22
-rw-r--r--editor/filesystem_dock.h1
-rw-r--r--editor/gui/editor_object_selector.cpp3
-rw-r--r--editor/gui/editor_object_selector.h1
-rw-r--r--editor/gui/editor_spin_slider.cpp6
-rw-r--r--editor/gui/scene_tree_editor.cpp19
-rw-r--r--editor/icons/AudioStreamPlayer.svg2
-rw-r--r--editor/icons/AudioStreamPlayer2D.svg2
-rw-r--r--editor/icons/AudioStreamPlayer3D.svg2
-rw-r--r--editor/icons/Panels1.svg2
-rw-r--r--editor/icons/Panels2.svg2
-rw-r--r--editor/icons/Panels2Alt.svg2
-rw-r--r--editor/icons/Panels3.svg2
-rw-r--r--editor/icons/Panels3Alt.svg2
-rw-r--r--editor/icons/Panels4.svg2
-rw-r--r--editor/import/3d/resource_importer_scene.cpp2
-rw-r--r--editor/import/3d/scene_import_settings.cpp9
-rw-r--r--editor/import/dynamic_font_import_settings.cpp1
-rw-r--r--editor/import/resource_importer_csv_translation.cpp2
-rw-r--r--editor/import/resource_importer_dynamic_font.cpp37
-rw-r--r--editor/import/resource_importer_imagefont.cpp6
-rw-r--r--editor/localization_editor.cpp2
-rw-r--r--editor/node_dock.cpp3
-rw-r--r--editor/node_dock.h1
-rw-r--r--editor/plugins/abstract_polygon_2d_editor.cpp3
-rw-r--r--editor/plugins/abstract_polygon_2d_editor.h1
-rw-r--r--editor/plugins/animation_tree_editor_plugin.cpp3
-rw-r--r--editor/plugins/animation_tree_editor_plugin.h1
-rw-r--r--editor/plugins/bone_map_editor_plugin.cpp3
-rw-r--r--editor/plugins/bone_map_editor_plugin.h2
-rw-r--r--editor/plugins/camera_3d_editor_plugin.cpp3
-rw-r--r--editor/plugins/camera_3d_editor_plugin.h1
-rw-r--r--editor/plugins/cpu_particles_2d_editor_plugin.cpp3
-rw-r--r--editor/plugins/cpu_particles_2d_editor_plugin.h1
-rw-r--r--editor/plugins/cpu_particles_3d_editor_plugin.cpp3
-rw-r--r--editor/plugins/cpu_particles_3d_editor_plugin.h1
-rw-r--r--editor/plugins/editor_plugin_settings.cpp3
-rw-r--r--editor/plugins/editor_plugin_settings.h2
-rw-r--r--editor/plugins/font_config_plugin.cpp4
-rw-r--r--editor/plugins/font_config_plugin.h1
-rw-r--r--editor/plugins/gizmos/camera_3d_gizmo_plugin.cpp31
-rw-r--r--editor/plugins/gpu_particles_2d_editor_plugin.cpp3
-rw-r--r--editor/plugins/gpu_particles_2d_editor_plugin.h1
-rw-r--r--editor/plugins/gpu_particles_3d_editor_plugin.cpp6
-rw-r--r--editor/plugins/gpu_particles_3d_editor_plugin.h3
-rw-r--r--editor/plugins/gpu_particles_collision_sdf_editor_plugin.cpp3
-rw-r--r--editor/plugins/gpu_particles_collision_sdf_editor_plugin.h1
-rw-r--r--editor/plugins/input_event_editor_plugin.cpp3
-rw-r--r--editor/plugins/input_event_editor_plugin.h1
-rw-r--r--editor/plugins/mesh_library_editor_plugin.cpp3
-rw-r--r--editor/plugins/mesh_library_editor_plugin.h3
-rw-r--r--editor/plugins/multimesh_editor_plugin.cpp3
-rw-r--r--editor/plugins/multimesh_editor_plugin.h1
-rw-r--r--editor/plugins/node_3d_editor_gizmos.cpp37
-rw-r--r--editor/plugins/node_3d_editor_gizmos.h5
-rw-r--r--editor/plugins/node_3d_editor_plugin.cpp103
-rw-r--r--editor/plugins/node_3d_editor_plugin.h9
-rw-r--r--editor/plugins/packed_scene_translation_parser_plugin.cpp5
-rw-r--r--editor/plugins/physical_bone_3d_editor_plugin.cpp3
-rw-r--r--editor/plugins/physical_bone_3d_editor_plugin.h3
-rw-r--r--editor/plugins/polygon_2d_editor_plugin.cpp59
-rw-r--r--editor/plugins/root_motion_editor_plugin.cpp3
-rw-r--r--editor/plugins/root_motion_editor_plugin.h1
-rw-r--r--editor/plugins/script_editor_plugin.cpp5
-rw-r--r--editor/plugins/shader/shader_editor.h50
-rw-r--r--editor/plugins/shader_editor_plugin.cpp54
-rw-r--r--editor/plugins/shader_editor_plugin.h7
-rw-r--r--editor/plugins/shader_file_editor_plugin.cpp3
-rw-r--r--editor/plugins/shader_file_editor_plugin.h1
-rw-r--r--editor/plugins/skeleton_2d_editor_plugin.cpp3
-rw-r--r--editor/plugins/skeleton_2d_editor_plugin.h1
-rw-r--r--editor/plugins/skeleton_ik_3d_editor_plugin.cpp3
-rw-r--r--editor/plugins/skeleton_ik_3d_editor_plugin.h3
-rw-r--r--editor/plugins/text_shader_editor.cpp5
-rw-r--r--editor/plugins/text_shader_editor.h19
-rw-r--r--editor/plugins/tiles/tile_atlas_view.cpp2
-rw-r--r--editor/plugins/tiles/tile_data_editors.cpp39
-rw-r--r--editor/plugins/tiles/tile_map_layer_editor.cpp2
-rw-r--r--editor/plugins/tiles/tile_set_atlas_source_editor.cpp6
-rw-r--r--editor/plugins/tiles/tile_set_editor.cpp2
-rw-r--r--editor/plugins/tiles/tile_set_scenes_collection_source_editor.cpp3
-rw-r--r--editor/plugins/visual_shader_editor_plugin.cpp33
-rw-r--r--editor/plugins/visual_shader_editor_plugin.h18
-rw-r--r--editor/plugins/voxel_gi_editor_plugin.cpp3
-rw-r--r--editor/plugins/voxel_gi_editor_plugin.h1
-rw-r--r--editor/project_manager/quick_settings_dialog.cpp2
-rw-r--r--editor/property_selector.cpp179
-rw-r--r--editor/property_selector.h4
-rw-r--r--editor/scene_tree_dock.cpp15
-rw-r--r--editor/scene_tree_dock.h2
-rw-r--r--editor/themes/editor_fonts.cpp11
-rw-r--r--editor/themes/editor_theme_manager.cpp2
-rw-r--r--main/main.cpp45
-rw-r--r--main/performance.cpp5
-rw-r--r--main/performance.h1
-rw-r--r--modules/csg/csg_shape.cpp64
-rw-r--r--modules/csg/csg_shape.h5
-rw-r--r--modules/csg/doc_classes/CSGShape3D.xml19
-rw-r--r--modules/csg/editor/csg_gizmos.cpp146
-rw-r--r--modules/csg/editor/csg_gizmos.h36
-rw-r--r--modules/dds/texture_loader_dds.cpp188
-rw-r--r--modules/etcpak/image_compress_etcpak.cpp211
-rw-r--r--modules/etcpak/image_compress_etcpak.h2
-rw-r--r--modules/fbx/fbx_document.cpp3
-rw-r--r--modules/fbx/fbx_document.h3
-rw-r--r--modules/gdscript/doc_classes/@GDScript.xml10
-rw-r--r--modules/gdscript/gdscript.cpp3
-rw-r--r--modules/gdscript/gdscript.h2
-rw-r--r--modules/gltf/README.md12
-rw-r--r--modules/gltf/doc_classes/GLTFAccessor.xml12
-rw-r--r--modules/gltf/doc_classes/GLTFAnimation.xml4
-rw-r--r--modules/gltf/doc_classes/GLTFBufferView.xml4
-rw-r--r--modules/gltf/doc_classes/GLTFCamera.xml18
-rw-r--r--modules/gltf/doc_classes/GLTFDocument.xml10
-rw-r--r--modules/gltf/doc_classes/GLTFDocumentExtension.xml20
-rw-r--r--modules/gltf/doc_classes/GLTFLight.xml8
-rw-r--r--modules/gltf/doc_classes/GLTFMesh.xml8
-rw-r--r--modules/gltf/doc_classes/GLTFNode.xml32
-rw-r--r--modules/gltf/doc_classes/GLTFPhysicsBody.xml10
-rw-r--r--modules/gltf/doc_classes/GLTFPhysicsShape.xml10
-rw-r--r--modules/gltf/doc_classes/GLTFSkeleton.xml4
-rw-r--r--modules/gltf/doc_classes/GLTFSpecGloss.xml6
-rw-r--r--modules/gltf/doc_classes/GLTFState.xml48
-rw-r--r--modules/gltf/doc_classes/GLTFTexture.xml2
-rw-r--r--modules/gltf/doc_classes/GLTFTextureSampler.xml4
-rw-r--r--modules/gltf/editor/editor_scene_importer_blend.cpp24
-rw-r--r--modules/gltf/editor/editor_scene_importer_blend.h1
-rw-r--r--modules/gltf/extensions/gltf_document_extension_convert_importer_mesh.cpp3
-rw-r--r--modules/gltf/extensions/gltf_document_extension_convert_importer_mesh.h1
-rw-r--r--modules/gltf/extensions/gltf_light.cpp8
-rw-r--r--modules/gltf/extensions/physics/gltf_document_extension_physics.cpp20
-rw-r--r--modules/gltf/extensions/physics/gltf_physics_body.cpp16
-rw-r--r--modules/gltf/extensions/physics/gltf_physics_shape.cpp2
-rw-r--r--modules/gltf/gltf_document.cpp35
-rw-r--r--modules/gltf/structures/gltf_camera.cpp12
-rw-r--r--modules/gltf/structures/gltf_camera.h4
-rw-r--r--modules/gridmap/editor/grid_map_editor_plugin.cpp1
-rw-r--r--modules/mono/csharp_script.cpp15
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/GeneratedSources/ExportDiagnostics_GD0107_OK_ScriptPropertyDefVal.generated.cs18
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/Sources/ExportDiagnostics_GD0107.cs49
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertyDefValGenerator.cs51
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs25
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Export/XcodeHelper.cs93
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Internals/Internal.cs9
-rw-r--r--modules/mono/editor/editor_internal_calls.cpp9
-rw-r--r--modules/mono/editor/script_templates/VisualShaderNodeCustom/basic.cs16
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/GodotObjectExtensions.cs3
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs10
-rw-r--r--modules/multiplayer/doc_classes/MultiplayerSpawner.xml4
-rw-r--r--modules/navigation/2d/nav_mesh_generator_2d.cpp30
-rw-r--r--modules/navigation/3d/godot_navigation_server_3d.cpp27
-rw-r--r--modules/navigation/3d/godot_navigation_server_3d.h1
-rw-r--r--modules/navigation/editor/navigation_mesh_editor_plugin.cpp3
-rw-r--r--modules/navigation/editor/navigation_mesh_editor_plugin.h1
-rw-r--r--modules/navigation/nav_map.cpp41
-rw-r--r--modules/navigation/nav_map.h8
-rw-r--r--modules/navigation/nav_region.cpp21
-rw-r--r--modules/navigation/nav_region.h8
-rw-r--r--modules/openxr/editor/openxr_select_runtime.cpp3
-rw-r--r--modules/openxr/editor/openxr_select_runtime.h1
-rw-r--r--modules/openxr/extensions/openxr_mxink_extension.cpp83
-rw-r--r--modules/openxr/extensions/openxr_mxink_extension.h48
-rw-r--r--modules/openxr/openxr_api.cpp2
-rw-r--r--modules/openxr/openxr_interface.cpp3
-rw-r--r--modules/openxr/register_types.cpp2
-rw-r--r--modules/text_server_adv/SCsub10
-rw-r--r--modules/text_server_adv/gdextension_build/SConstruct2
-rw-r--r--modules/text_server_adv/text_server_adv.cpp33
-rw-r--r--modules/text_server_adv/text_server_adv.h1
-rw-r--r--modules/text_server_fb/text_server_fb.cpp33
-rw-r--r--modules/text_server_fb/text_server_fb.h1
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/Godot.kt149
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/GodotGLRenderView.java14
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/GodotRenderView.java4
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/GodotVulkanRenderView.java14
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/input/GodotInputHandler.java184
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/input/InputEventRunnable.java353
-rw-r--r--platform/android/java/lib/src/org/godotengine/godot/plugin/GodotPluginRegistry.java4
-rw-r--r--platform/android/java_godot_lib_jni.cpp14
-rw-r--r--platform/android/java_godot_wrapper.cpp21
-rw-r--r--platform/ios/godot_app_delegate.h2
-rw-r--r--platform/ios/godot_app_delegate.m4
-rw-r--r--platform/ios/main.m2
-rw-r--r--platform/linuxbsd/export/export_plugin.cpp100
-rw-r--r--platform/linuxbsd/export/export_plugin.h2
-rw-r--r--platform/macos/export/export_plugin.cpp8
-rw-r--r--platform/web/audio_driver_web.cpp5
-rw-r--r--platform/web/audio_driver_web.h1
-rw-r--r--platform/web/display_server_web.cpp6
-rw-r--r--platform/web/emscripten_helpers.py3
-rw-r--r--platform/web/export/export_plugin.cpp2
-rw-r--r--platform/web/godot_audio.h1
-rw-r--r--platform/web/js/engine/config.js2
-rw-r--r--platform/web/js/libs/audio.position.worklet.js50
-rw-r--r--platform/web/js/libs/library_godot_audio.js109
-rwxr-xr-xplatform/web/serve.py20
-rw-r--r--platform/windows/display_server_windows.cpp371
-rw-r--r--platform/windows/display_server_windows.h40
-rw-r--r--platform/windows/export/export_plugin.cpp64
-rw-r--r--platform/windows/export/export_plugin.h2
-rw-r--r--scene/2d/audio_stream_player_2d.cpp4
-rw-r--r--scene/2d/navigation_link_2d.cpp133
-rw-r--r--scene/2d/navigation_link_2d.h13
-rw-r--r--scene/2d/parallax_layer.cpp8
-rw-r--r--scene/2d/polygon_2d.cpp2
-rw-r--r--scene/2d/skeleton_2d.cpp40
-rw-r--r--scene/2d/tile_map.cpp16
-rw-r--r--scene/2d/tile_map.h4
-rw-r--r--scene/2d/tile_map_layer.cpp16
-rw-r--r--scene/2d/tile_map_layer.h4
-rw-r--r--scene/3d/audio_stream_player_3d.cpp4
-rw-r--r--scene/3d/navigation_link_3d.cpp106
-rw-r--r--scene/3d/navigation_link_3d.h9
-rw-r--r--scene/3d/navigation_region_3d.cpp17
-rw-r--r--scene/3d/occluder_instance_3d.cpp3
-rw-r--r--scene/3d/occluder_instance_3d.h1
-rw-r--r--scene/3d/skeleton_3d.cpp12
-rw-r--r--scene/3d/soft_body_3d.cpp8
-rw-r--r--scene/animation/animation_blend_tree.cpp21
-rw-r--r--scene/animation/animation_blend_tree.h21
-rw-r--r--scene/animation/tween.cpp17
-rw-r--r--scene/animation/tween.h1
-rw-r--r--scene/audio/audio_stream_player.cpp4
-rw-r--r--scene/audio/audio_stream_player_internal.cpp13
-rw-r--r--scene/audio/audio_stream_player_internal.h5
-rw-r--r--scene/gui/code_edit.cpp2
-rw-r--r--scene/gui/control.cpp2
-rw-r--r--scene/gui/menu_bar.cpp5
-rw-r--r--scene/gui/rich_text_label.cpp10
-rw-r--r--scene/gui/text_edit.cpp19
-rw-r--r--scene/main/node.cpp6
-rw-r--r--scene/main/viewport.cpp1
-rw-r--r--scene/main/window.cpp4
-rw-r--r--scene/resources/2d/navigation_mesh_source_geometry_data_2d.cpp70
-rw-r--r--scene/resources/2d/navigation_mesh_source_geometry_data_2d.h5
-rw-r--r--scene/resources/2d/navigation_polygon.cpp84
-rw-r--r--scene/resources/2d/navigation_polygon.h21
-rw-r--r--scene/resources/2d/tile_set.cpp34
-rw-r--r--scene/resources/2d/tile_set.h1
-rw-r--r--scene/resources/3d/importer_mesh.cpp54
-rw-r--r--scene/resources/3d/importer_mesh.h2
-rw-r--r--scene/resources/3d/navigation_mesh_source_geometry_data_3d.cpp52
-rw-r--r--scene/resources/3d/navigation_mesh_source_geometry_data_3d.h5
-rw-r--r--scene/resources/font.cpp9
-rw-r--r--scene/resources/navigation_mesh.cpp41
-rw-r--r--scene/resources/navigation_mesh.h9
-rw-r--r--scene/resources/visual_shader.cpp1
-rw-r--r--servers/audio/audio_stream.cpp6
-rw-r--r--servers/audio/audio_stream.h5
-rw-r--r--servers/audio_server.cpp11
-rw-r--r--servers/audio_server.h2
-rw-r--r--servers/movie_writer/movie_writer.cpp4
-rw-r--r--servers/navigation_server_3d.cpp1
-rw-r--r--servers/navigation_server_3d.h1
-rw-r--r--servers/rendering/dummy/rasterizer_scene_dummy.h1
-rw-r--r--servers/rendering/dummy/storage/material_storage.cpp6
-rw-r--r--servers/rendering/renderer_canvas_cull.cpp54
-rw-r--r--servers/rendering/renderer_canvas_cull.h3
-rw-r--r--servers/rendering/renderer_canvas_render.h1
-rw-r--r--servers/rendering/renderer_rd/effects/fsr2.cpp3
-rw-r--r--servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp19
-rw-r--r--servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.h4
-rw-r--r--servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp13
-rw-r--r--servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.h5
-rw-r--r--servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp33
-rw-r--r--servers/rendering/renderer_rd/renderer_canvas_render_rd.h2
-rw-r--r--servers/rendering/renderer_rd/renderer_scene_render_rd.cpp9
-rw-r--r--servers/rendering/renderer_rd/renderer_scene_render_rd.h5
-rw-r--r--servers/rendering/renderer_rd/shaders/environment/sky.glsl8
-rw-r--r--servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered.glsl114
-rw-r--r--servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered_inc.glsl3
-rw-r--r--servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile.glsl119
-rw-r--r--servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile_inc.glsl3
-rw-r--r--servers/rendering/renderer_rd/storage_rd/light_storage.cpp1
-rw-r--r--servers/rendering/renderer_rd/storage_rd/light_storage.h5
-rw-r--r--servers/rendering/renderer_rd/storage_rd/material_storage.cpp10
-rw-r--r--servers/rendering/renderer_rd/storage_rd/mesh_storage.cpp5
-rw-r--r--servers/rendering/renderer_rd/storage_rd/render_data_rd.cpp3
-rw-r--r--servers/rendering/renderer_rd/storage_rd/render_data_rd.h5
-rw-r--r--servers/rendering/renderer_rd/storage_rd/render_scene_data_rd.cpp3
-rw-r--r--servers/rendering/renderer_rd/storage_rd/render_scene_data_rd.h2
-rw-r--r--servers/rendering/renderer_scene_cull.h1
-rw-r--r--servers/rendering/renderer_scene_render.h1
-rw-r--r--servers/rendering/renderer_viewport.cpp7
-rw-r--r--servers/rendering/renderer_viewport.h2
-rw-r--r--servers/rendering/rendering_method.h1
-rw-r--r--servers/rendering/rendering_server_default.h2
-rw-r--r--servers/rendering/shader_compiler.cpp4
-rw-r--r--servers/rendering/shader_language.cpp26
-rw-r--r--servers/rendering/shader_language.h7
-rw-r--r--servers/rendering_server.cpp2
-rw-r--r--servers/rendering_server.h3
-rw-r--r--servers/text/text_server_dummy.h1
-rw-r--r--servers/text/text_server_extension.cpp7
-rw-r--r--servers/text/text_server_extension.h2
-rw-r--r--servers/text_server.cpp7
-rw-r--r--servers/text_server.h1
-rw-r--r--tests/core/config/test_project_settings.h7
-rw-r--r--tests/core/string/test_string.h124
-rw-r--r--tests/core/string/test_translation.h1
-rw-r--r--tests/core/string/test_translation_server.h2
-rw-r--r--tests/scene/test_code_edit.h20
-rw-r--r--tests/scene/test_text_edit.h66
-rw-r--r--thirdparty/README.md2
-rw-r--r--thirdparty/openxr/include/openxr/openxr.h524
-rw-r--r--thirdparty/openxr/include/openxr/openxr_loader_negotiation.h1
-rw-r--r--thirdparty/openxr/include/openxr/openxr_platform.h24
-rw-r--r--thirdparty/openxr/include/openxr/openxr_reflection.h258
-rw-r--r--thirdparty/openxr/include/openxr/openxr_reflection_parent_structs.h14
-rw-r--r--thirdparty/openxr/include/openxr/openxr_reflection_structs.h15
-rw-r--r--thirdparty/openxr/src/common/platform_utils.hpp70
398 files changed, 7034 insertions, 3115 deletions
diff --git a/core/config/project_settings.cpp b/core/config/project_settings.cpp
index 37a2608c10..5b04986020 100644
--- a/core/config/project_settings.cpp
+++ b/core/config/project_settings.cpp
@@ -1489,15 +1489,6 @@ ProjectSettings::ProjectSettings() {
GLOBAL_DEF(PropertyInfo(Variant::INT, "audio/general/ios/session_category", PROPERTY_HINT_ENUM, "Ambient,Multi Route,Play and Record,Playback,Record,Solo Ambient"), 0);
GLOBAL_DEF("audio/general/ios/mix_with_others", false);
- PackedStringArray extensions;
- extensions.push_back("gd");
- if (ClassDB::class_exists("CSharpScript")) {
- extensions.push_back("cs");
- }
- extensions.push_back("gdshader");
-
- GLOBAL_DEF(PropertyInfo(Variant::PACKED_STRING_ARRAY, "editor/script/search_in_file_extensions"), extensions);
-
_add_builtin_input_map();
// Keep the enum values in sync with the `DisplayServer::ScreenOrientation` enum.
@@ -1570,6 +1561,11 @@ ProjectSettings::ProjectSettings() {
GLOBAL_DEF("collada/use_ambient", false);
+ // Input settings
+ GLOBAL_DEF_BASIC("input_devices/pointing/android/enable_long_press_as_right_click", false);
+ GLOBAL_DEF_BASIC("input_devices/pointing/android/enable_pan_and_scale_gestures", false);
+ GLOBAL_DEF_BASIC(PropertyInfo(Variant::INT, "input_devices/pointing/android/rotary_input_scroll_axis", PROPERTY_HINT_ENUM, "Horizontal,Vertical"), 1);
+
// These properties will not show up in the dialog. If you want to exclude whole groups, use add_hidden_prefix().
GLOBAL_DEF_INTERNAL("application/config/features", PackedStringArray());
GLOBAL_DEF_INTERNAL("internationalization/locale/translation_remaps", PackedStringArray());
diff --git a/core/core_bind.cpp b/core/core_bind.cpp
index a1b7b81111..a15085bcde 100644
--- a/core/core_bind.cpp
+++ b/core/core_bind.cpp
@@ -1440,6 +1440,14 @@ TypedArray<Dictionary> ClassDB::class_get_property_list(const StringName &p_clas
return ret;
}
+StringName ClassDB::class_get_property_getter(const StringName &p_class, const StringName &p_property) {
+ return ::ClassDB::get_property_getter(p_class, p_property);
+}
+
+StringName ClassDB::class_get_property_setter(const StringName &p_class, const StringName &p_property) {
+ return ::ClassDB::get_property_setter(p_class, p_property);
+}
+
Variant ClassDB::class_get_property(Object *p_object, const StringName &p_property) const {
Variant ret;
::ClassDB::get_property(p_object, p_property, ret);
@@ -1601,6 +1609,8 @@ void ClassDB::_bind_methods() {
::ClassDB::bind_method(D_METHOD("class_get_signal_list", "class", "no_inheritance"), &ClassDB::class_get_signal_list, DEFVAL(false));
::ClassDB::bind_method(D_METHOD("class_get_property_list", "class", "no_inheritance"), &ClassDB::class_get_property_list, DEFVAL(false));
+ ::ClassDB::bind_method(D_METHOD("class_get_property_getter", "class", "property"), &ClassDB::class_get_property_getter);
+ ::ClassDB::bind_method(D_METHOD("class_get_property_setter", "class", "property"), &ClassDB::class_get_property_setter);
::ClassDB::bind_method(D_METHOD("class_get_property", "object", "property"), &ClassDB::class_get_property);
::ClassDB::bind_method(D_METHOD("class_set_property", "object", "property", "value"), &ClassDB::class_set_property);
diff --git a/core/core_bind.h b/core/core_bind.h
index b142a2fbbd..69a359e4ed 100644
--- a/core/core_bind.h
+++ b/core/core_bind.h
@@ -447,6 +447,8 @@ public:
TypedArray<Dictionary> class_get_signal_list(const StringName &p_class, bool p_no_inheritance = false) const;
TypedArray<Dictionary> class_get_property_list(const StringName &p_class, bool p_no_inheritance = false) const;
+ StringName class_get_property_getter(const StringName &p_class, const StringName &p_property);
+ StringName class_get_property_setter(const StringName &p_class, const StringName &p_property);
Variant class_get_property(Object *p_object, const StringName &p_property) const;
Error class_set_property(Object *p_object, const StringName &p_property, const Variant &p_value) const;
diff --git a/core/input/godotcontrollerdb.txt b/core/input/godotcontrollerdb.txt
index f5158bfabb..8e8ec4c718 100644
--- a/core/input/godotcontrollerdb.txt
+++ b/core/input/godotcontrollerdb.txt
@@ -8,7 +8,7 @@ __XINPUT_DEVICE__,XInput Gamepad,a:b12,b:b13,x:b14,y:b15,start:b4,guide:b10,back
Default Android Gamepad,Default Controller,leftx:a0,lefty:a1,dpdown:h0.4,rightstick:b8,rightshoulder:b10,rightx:a2,start:b6,righty:a3,dpleft:h0.8,lefttrigger:a4,x:b2,dpup:h0.1,back:b4,leftstick:b7,leftshoulder:b9,y:b3,a:b0,dpright:h0.2,righttrigger:a5,b:b1,platform:Android,
# Web
-standard,Standard Gamepad Mapping,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7,a:b0,b:b1,x:b2,y:b3,leftshoulder:b4,rightshoulder:b5,back:b8,start:b9,leftstick:b10,rightstick:b11,dpup:b12,dpdown:b13,dpleft:b14,dpright:b15,guide:b16,leftstick:b10,rightstick:b11,platform:Web,
+standard,Standard Gamepad Mapping,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:+a4,righttrigger:+a5,a:b0,b:b1,x:b2,y:b3,leftshoulder:b4,rightshoulder:b5,back:b8,start:b9,leftstick:b10,rightstick:b11,dpup:b12,dpdown:b13,dpleft:b14,dpright:b15,guide:b16,leftstick:b10,rightstick:b11,platform:Web,
Linux24c6581a,PowerA Xbox One Cabled,a:b0,b:b1,y:b3,x:b2,start:b7,guide:b8,back:b6,leftstick:b9,rightstick:b10,leftshoulder:b4,rightshoulder:b5,dpup:-a7,dpleft:-a6,dpdown:+a7,dpright:+a6,leftx:a0,lefty:a1,rightx:a3,righty:a4,lefttrigger:a2,righttrigger:a5,platform:Web,
Linux0e6f0301,Logic 3 Controller (xbox compatible),a:b0,b:b1,y:b3,x:b2,start:b7,guide:b8,back:b6,leftstick:b9,rightstick:b10,leftshoulder:b4,rightshoulder:b5,dpup:-a7,dpleft:-a6,dpdown:+a7,dpright:+a6,leftx:a0,lefty:a1,rightx:a3,righty:a4,lefttrigger:a2,righttrigger:a5,platform:Web,
Linux045e028e,Microsoft X-Box 360 pad,a:b0,b:b1,y:b3,x:b2,start:b7,guide:b8,back:b6,leftstick:b9,rightstick:b10,leftshoulder:b4,rightshoulder:b5,dpup:-a7,dpleft:-a6,dpdown:+a7,dpright:+a6,leftx:a0,lefty:a1,rightx:a3,righty:a4,lefttrigger:a2,righttrigger:a5,platform:Web,
diff --git a/core/input/input.cpp b/core/input/input.cpp
index 91378591b0..eba7ded267 100644
--- a/core/input/input.cpp
+++ b/core/input/input.cpp
@@ -513,21 +513,49 @@ void Input::joy_connection_changed(int p_idx, bool p_connected, const String &p_
Vector3 Input::get_gravity() const {
_THREAD_SAFE_METHOD_
+
+#ifdef DEBUG_ENABLED
+ if (!gravity_enabled) {
+ WARN_PRINT_ONCE("`input_devices/sensors/enable_gravity` is not enabled in project settings.");
+ }
+#endif
+
return gravity;
}
Vector3 Input::get_accelerometer() const {
_THREAD_SAFE_METHOD_
+
+#ifdef DEBUG_ENABLED
+ if (!accelerometer_enabled) {
+ WARN_PRINT_ONCE("`input_devices/sensors/enable_accelerometer` is not enabled in project settings.");
+ }
+#endif
+
return accelerometer;
}
Vector3 Input::get_magnetometer() const {
_THREAD_SAFE_METHOD_
+
+#ifdef DEBUG_ENABLED
+ if (!magnetometer_enabled) {
+ WARN_PRINT_ONCE("`input_devices/sensors/enable_magnetometer` is not enabled in project settings.");
+ }
+#endif
+
return magnetometer;
}
Vector3 Input::get_gyroscope() const {
_THREAD_SAFE_METHOD_
+
+#ifdef DEBUG_ENABLED
+ if (!gyroscope_enabled) {
+ WARN_PRINT_ONCE("`input_devices/sensors/enable_gyroscope` is not enabled in project settings.");
+ }
+#endif
+
return gyroscope;
}
@@ -1683,6 +1711,11 @@ Input::Input() {
// Always use standard behavior in the editor.
legacy_just_pressed_behavior = false;
}
+
+ accelerometer_enabled = GLOBAL_DEF_RST_BASIC("input_devices/sensors/enable_accelerometer", false);
+ gravity_enabled = GLOBAL_DEF_RST_BASIC("input_devices/sensors/enable_gravity", false);
+ gyroscope_enabled = GLOBAL_DEF_RST_BASIC("input_devices/sensors/enable_gyroscope", false);
+ magnetometer_enabled = GLOBAL_DEF_RST_BASIC("input_devices/sensors/enable_magnetometer", false);
}
Input::~Input() {
diff --git a/core/input/input.h b/core/input/input.h
index 89e48f53d7..95dd623cc0 100644
--- a/core/input/input.h
+++ b/core/input/input.h
@@ -92,9 +92,13 @@ private:
RBSet<JoyButton> joy_buttons_pressed;
RBMap<JoyAxis, float> _joy_axis;
//RBMap<StringName,int> custom_action_press;
+ bool gravity_enabled = false;
Vector3 gravity;
+ bool accelerometer_enabled = false;
Vector3 accelerometer;
+ bool magnetometer_enabled = false;
Vector3 magnetometer;
+ bool gyroscope_enabled = false;
Vector3 gyroscope;
Vector2 mouse_pos;
int64_t mouse_window = 0;
diff --git a/core/io/image.cpp b/core/io/image.cpp
index b35d405662..499364826d 100644
--- a/core/io/image.cpp
+++ b/core/io/image.cpp
@@ -3564,6 +3564,7 @@ void Image::_bind_methods() {
ClassDB::bind_method(D_METHOD("fix_alpha_edges"), &Image::fix_alpha_edges);
ClassDB::bind_method(D_METHOD("premultiply_alpha"), &Image::premultiply_alpha);
ClassDB::bind_method(D_METHOD("srgb_to_linear"), &Image::srgb_to_linear);
+ ClassDB::bind_method(D_METHOD("linear_to_srgb"), &Image::linear_to_srgb);
ClassDB::bind_method(D_METHOD("normal_map_to_xy"), &Image::normal_map_to_xy);
ClassDB::bind_method(D_METHOD("rgbe_to_srgb"), &Image::rgbe_to_srgb);
ClassDB::bind_method(D_METHOD("bump_map_to_normal_map", "bump_scale"), &Image::bump_map_to_normal_map, DEFVAL(1.0));
@@ -3831,6 +3832,37 @@ void Image::srgb_to_linear() {
}
}
+void Image::linear_to_srgb() {
+ if (data.size() == 0) {
+ return;
+ }
+
+ static const uint8_t lin2srgb[256] = { 0, 12, 21, 28, 33, 38, 42, 46, 49, 52, 55, 58, 61, 63, 66, 68, 70, 73, 75, 77, 79, 81, 82, 84, 86, 88, 89, 91, 93, 94, 96, 97, 99, 100, 102, 103, 104, 106, 107, 109, 110, 111, 112, 114, 115, 116, 117, 118, 120, 121, 122, 123, 124, 125, 126, 127, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 151, 152, 153, 154, 155, 156, 157, 157, 158, 159, 160, 161, 161, 162, 163, 164, 165, 165, 166, 167, 168, 168, 169, 170, 171, 171, 172, 173, 174, 174, 175, 176, 176, 177, 178, 179, 179, 180, 181, 181, 182, 183, 183, 184, 185, 185, 186, 187, 187, 188, 189, 189, 190, 191, 191, 192, 193, 193, 194, 194, 195, 196, 196, 197, 197, 198, 199, 199, 200, 201, 201, 202, 202, 203, 204, 204, 205, 205, 206, 206, 207, 208, 208, 209, 209, 210, 210, 211, 212, 212, 213, 213, 214, 214, 215, 215, 216, 217, 217, 218, 218, 219, 219, 220, 220, 221, 221, 222, 222, 223, 223, 224, 224, 225, 226, 226, 227, 227, 228, 228, 229, 229, 230, 230, 231, 231, 232, 232, 233, 233, 234, 234, 235, 235, 236, 236, 237, 237, 237, 238, 238, 239, 239, 240, 240, 241, 241, 242, 242, 243, 243, 244, 244, 245, 245, 245, 246, 246, 247, 247, 248, 248, 249, 249, 250, 250, 251, 251, 251, 252, 252, 253, 253, 254, 254, 255 };
+
+ ERR_FAIL_COND(format != FORMAT_RGB8 && format != FORMAT_RGBA8);
+
+ if (format == FORMAT_RGBA8) {
+ int len = data.size() / 4;
+ uint8_t *data_ptr = data.ptrw();
+
+ for (int i = 0; i < len; i++) {
+ data_ptr[(i << 2) + 0] = lin2srgb[data_ptr[(i << 2) + 0]];
+ data_ptr[(i << 2) + 1] = lin2srgb[data_ptr[(i << 2) + 1]];
+ data_ptr[(i << 2) + 2] = lin2srgb[data_ptr[(i << 2) + 2]];
+ }
+
+ } else if (format == FORMAT_RGB8) {
+ int len = data.size() / 3;
+ uint8_t *data_ptr = data.ptrw();
+
+ for (int i = 0; i < len; i++) {
+ data_ptr[(i * 3) + 0] = lin2srgb[data_ptr[(i * 3) + 0]];
+ data_ptr[(i * 3) + 1] = lin2srgb[data_ptr[(i * 3) + 1]];
+ data_ptr[(i * 3) + 2] = lin2srgb[data_ptr[(i * 3) + 2]];
+ }
+ }
+}
+
void Image::premultiply_alpha() {
if (data.size() == 0) {
return;
diff --git a/core/io/image.h b/core/io/image.h
index d55cc39dbb..cc285dabe3 100644
--- a/core/io/image.h
+++ b/core/io/image.h
@@ -383,6 +383,7 @@ public:
void fix_alpha_edges();
void premultiply_alpha();
void srgb_to_linear();
+ void linear_to_srgb();
void normal_map_to_xy();
Ref<Image> rgbe_to_srgb();
Ref<Image> get_image_from_mipmap(int p_mipmap) const;
diff --git a/core/io/resource_loader.cpp b/core/io/resource_loader.cpp
index e727dfa56d..0577ca6bca 100644
--- a/core/io/resource_loader.cpp
+++ b/core/io/resource_loader.cpp
@@ -38,7 +38,7 @@
#include "core/os/os.h"
#include "core/os/safe_binary_mutex.h"
#include "core/string/print_string.h"
-#include "core/string/translation.h"
+#include "core/string/translation_server.h"
#include "core/variant/variant_parser.h"
#include "servers/rendering_server.h"
@@ -245,9 +245,9 @@ ResourceLoader::LoadToken::~LoadToken() {
Ref<Resource> ResourceLoader::_load(const String &p_path, const String &p_original_path, const String &p_type_hint, ResourceFormatLoader::CacheMode p_cache_mode, Error *r_error, bool p_use_sub_threads, float *r_progress) {
const String &original_path = p_original_path.is_empty() ? p_path : p_original_path;
load_nesting++;
- if (load_paths_stack->size()) {
+ if (load_paths_stack.size()) {
thread_load_mutex.lock();
- const String &parent_task_path = load_paths_stack->get(load_paths_stack->size() - 1);
+ const String &parent_task_path = load_paths_stack.get(load_paths_stack.size() - 1);
HashMap<String, ThreadLoadTask>::Iterator E = thread_load_tasks.find(parent_task_path);
// Avoid double-tracking, for progress reporting, resources that boil down to a remapped path containing the real payload (e.g., imported resources).
bool is_remapped_load = original_path == parent_task_path;
@@ -256,7 +256,7 @@ Ref<Resource> ResourceLoader::_load(const String &p_path, const String &p_origin
}
thread_load_mutex.unlock();
}
- load_paths_stack->push_back(original_path);
+ load_paths_stack.push_back(original_path);
// Try all loaders and pick the first match for the type hint
bool found = false;
@@ -272,7 +272,7 @@ Ref<Resource> ResourceLoader::_load(const String &p_path, const String &p_origin
}
}
- load_paths_stack->resize(load_paths_stack->size() - 1);
+ load_paths_stack.resize(load_paths_stack.size() - 1);
res_ref_overrides.erase(load_nesting);
load_nesting--;
@@ -280,6 +280,10 @@ Ref<Resource> ResourceLoader::_load(const String &p_path, const String &p_origin
return res;
}
+ if (r_error) {
+ *r_error = ERR_FILE_UNRECOGNIZED;
+ }
+
ERR_FAIL_COND_V_MSG(found, Ref<Resource>(),
vformat("Failed loading resource: %s. Make sure resources have been imported by opening the project in the editor at least once.", p_path));
@@ -306,8 +310,7 @@ void ResourceLoader::_thread_load_function(void *p_userdata) {
// Thread-safe either if it's the current thread or a brand new one.
CallQueue *own_mq_override = nullptr;
if (load_nesting == 0) {
- load_paths_stack = memnew(Vector<String>);
-
+ DEV_ASSERT(load_paths_stack.is_empty());
if (!Thread::is_main_thread()) {
// Let the caller thread use its own, for added flexibility. Provide one otherwise.
if (MessageQueue::get_singleton() == MessageQueue::get_main_singleton()) {
@@ -408,10 +411,7 @@ void ResourceLoader::_thread_load_function(void *p_userdata) {
MessageQueue::set_thread_singleton_override(nullptr);
memdelete(own_mq_override);
}
- if (load_paths_stack) {
- memdelete(load_paths_stack);
- load_paths_stack = nullptr;
- }
+ DEV_ASSERT(load_paths_stack.is_empty());
}
}
@@ -1299,7 +1299,7 @@ bool ResourceLoader::timestamp_on_load = false;
thread_local int ResourceLoader::load_nesting = 0;
thread_local WorkerThreadPool::TaskID ResourceLoader::caller_task_id = 0;
-thread_local Vector<String> *ResourceLoader::load_paths_stack = nullptr;
+thread_local Vector<String> ResourceLoader::load_paths_stack;
thread_local HashMap<int, HashMap<String, Ref<Resource>>> ResourceLoader::res_ref_overrides;
template <>
diff --git a/core/io/resource_loader.h b/core/io/resource_loader.h
index 5f1831f0d9..9d07964105 100644
--- a/core/io/resource_loader.h
+++ b/core/io/resource_loader.h
@@ -188,7 +188,7 @@ private:
static thread_local int load_nesting;
static thread_local WorkerThreadPool::TaskID caller_task_id;
static thread_local HashMap<int, HashMap<String, Ref<Resource>>> res_ref_overrides; // Outermost key is nesting level.
- static thread_local Vector<String> *load_paths_stack; // A pointer to avoid broken TLS implementations from double-running the destructor.
+ static thread_local Vector<String> load_paths_stack;
static SafeBinaryMutex<BINARY_MUTEX_TAG> thread_load_mutex;
static HashMap<String, ThreadLoadTask> thread_load_tasks;
static bool cleaning_tasks;
diff --git a/core/object/object.cpp b/core/object/object.cpp
index e4d1a8fc9a..a2926a478d 100644
--- a/core/object/object.cpp
+++ b/core/object/object.cpp
@@ -38,7 +38,7 @@
#include "core/object/script_language.h"
#include "core/os/os.h"
#include "core/string/print_string.h"
-#include "core/string/translation.h"
+#include "core/string/translation_server.h"
#include "core/templates/local_vector.h"
#include "core/variant/typed_array.h"
@@ -2097,7 +2097,11 @@ Object::~Object() {
// Disconnect signals that connect to this object.
while (connections.size()) {
Connection c = connections.front()->get();
- bool disconnected = c.signal.get_object()->_disconnect(c.signal.get_name(), c.callable, true);
+ Object *obj = c.callable.get_object();
+ bool disconnected = false;
+ if (likely(obj)) {
+ disconnected = c.signal.get_object()->_disconnect(c.signal.get_name(), c.callable, true);
+ }
if (unlikely(!disconnected)) {
// If the disconnect has failed, abandon the connection to avoid getting trapped in an infinite loop here.
connections.pop_front();
diff --git a/core/object/script_language_extension.cpp b/core/object/script_language_extension.cpp
index 7b643e4637..73f7ec5a54 100644
--- a/core/object/script_language_extension.cpp
+++ b/core/object/script_language_extension.cpp
@@ -142,6 +142,7 @@ void ScriptLanguageExtension::_bind_methods() {
GDVIRTUAL_BIND(_debug_get_current_stack_info);
GDVIRTUAL_BIND(_reload_all_scripts);
+ GDVIRTUAL_BIND(_reload_scripts, "scripts", "soft_reload");
GDVIRTUAL_BIND(_reload_tool_script, "script", "soft_reload");
GDVIRTUAL_BIND(_get_recognized_extensions);
diff --git a/core/register_core_types.cpp b/core/register_core_types.cpp
index c0a86e9fb7..c866ff0415 100644
--- a/core/register_core_types.cpp
+++ b/core/register_core_types.cpp
@@ -79,6 +79,7 @@
#include "core/os/time.h"
#include "core/string/optimized_translation.h"
#include "core/string/translation.h"
+#include "core/string/translation_server.h"
static Ref<ResourceFormatSaverBinary> resource_saver_binary;
static Ref<ResourceFormatLoaderBinary> resource_loader_binary;
diff --git a/core/string/string_name.cpp b/core/string/string_name.cpp
index 658297d805..5d59d65f92 100644
--- a/core/string/string_name.cpp
+++ b/core/string/string_name.cpp
@@ -39,19 +39,10 @@ StaticCString StaticCString::create(const char *p_ptr) {
return scs;
}
-StringName::_Data *StringName::_table[STRING_TABLE_LEN];
-
StringName _scs_create(const char *p_chr, bool p_static) {
return (p_chr[0] ? StringName(StaticCString::create(p_chr), p_static) : StringName());
}
-bool StringName::configured = false;
-Mutex StringName::mutex;
-
-#ifdef DEBUG_ENABLED
-bool StringName::debug_stringname = false;
-#endif
-
void StringName::setup() {
ERR_FAIL_COND(configured);
for (int i = 0; i < STRING_TABLE_LEN; i++) {
diff --git a/core/string/string_name.h b/core/string/string_name.h
index 89b4c07e0e..0eb98cf64b 100644
--- a/core/string/string_name.h
+++ b/core/string/string_name.h
@@ -67,7 +67,7 @@ class StringName {
_Data() {}
};
- static _Data *_table[STRING_TABLE_LEN];
+ static inline _Data *_table[STRING_TABLE_LEN];
_Data *_data = nullptr;
@@ -75,10 +75,10 @@ class StringName {
friend void register_core_types();
friend void unregister_core_types();
friend class Main;
- static Mutex mutex;
+ static inline Mutex mutex;
static void setup();
static void cleanup();
- static bool configured;
+ static inline bool configured = false;
#ifdef DEBUG_ENABLED
struct DebugSortReferences {
bool operator()(const _Data *p_left, const _Data *p_right) const {
@@ -86,7 +86,7 @@ class StringName {
}
};
- static bool debug_stringname;
+ static inline bool debug_stringname = false;
#endif
StringName(_Data *p_data) { _data = p_data; }
diff --git a/core/string/translation.compat.inc b/core/string/translation.compat.inc
index d792d4a6fc..68bd1831e4 100644
--- a/core/string/translation.compat.inc
+++ b/core/string/translation.compat.inc
@@ -38,9 +38,4 @@ void Translation::_bind_compatibility_methods() {
ClassDB::bind_compatibility_method(D_METHOD("erase_message", "src_message", "context"), &Translation::erase_message, DEFVAL(""));
}
-void TranslationServer::_bind_compatibility_methods() {
- ClassDB::bind_compatibility_method(D_METHOD("translate", "message", "context"), &TranslationServer::translate, DEFVAL(""));
- ClassDB::bind_compatibility_method(D_METHOD("translate_plural", "message", "plural_message", "n", "context"), &TranslationServer::translate_plural, DEFVAL(""));
-}
-
#endif
diff --git a/core/string/translation.cpp b/core/string/translation.cpp
index 432016284a..33d4a1bcde 100644
--- a/core/string/translation.cpp
+++ b/core/string/translation.cpp
@@ -31,14 +31,9 @@
#include "translation.h"
#include "translation.compat.inc"
-#include "core/config/project_settings.h"
-#include "core/io/resource_loader.h"
#include "core/os/os.h"
-#include "core/string/locales.h"
-
-#ifdef TOOLS_ENABLED
-#include "main/main.h"
-#endif
+#include "core/os/thread.h"
+#include "core/string/translation_server.h"
Dictionary Translation::_get_messages() const {
Dictionary d;
@@ -173,911 +168,3 @@ void Translation::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "messages", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_messages", "_get_messages");
ADD_PROPERTY(PropertyInfo(Variant::STRING, "locale"), "set_locale", "get_locale");
}
-
-///////////////////////////////////////////////
-
-struct _character_accent_pair {
- const char32_t character;
- const char32_t *accented_character;
-};
-
-static _character_accent_pair _character_to_accented[] = {
- { 'A', U"Å" },
- { 'B', U"ß" },
- { 'C', U"Ç" },
- { 'D', U"Ð" },
- { 'E', U"É" },
- { 'F', U"F́" },
- { 'G', U"Ĝ" },
- { 'H', U"Ĥ" },
- { 'I', U"Ĩ" },
- { 'J', U"Ĵ" },
- { 'K', U"ĸ" },
- { 'L', U"Ł" },
- { 'M', U"Ḿ" },
- { 'N', U"й" },
- { 'O', U"Ö" },
- { 'P', U"Ṕ" },
- { 'Q', U"Q́" },
- { 'R', U"Ř" },
- { 'S', U"Ŝ" },
- { 'T', U"Ŧ" },
- { 'U', U"Ũ" },
- { 'V', U"Ṽ" },
- { 'W', U"Ŵ" },
- { 'X', U"X́" },
- { 'Y', U"Ÿ" },
- { 'Z', U"Ž" },
- { 'a', U"á" },
- { 'b', U"ḅ" },
- { 'c', U"ć" },
- { 'd', U"d́" },
- { 'e', U"é" },
- { 'f', U"f́" },
- { 'g', U"ǵ" },
- { 'h', U"h̀" },
- { 'i', U"í" },
- { 'j', U"ǰ" },
- { 'k', U"ḱ" },
- { 'l', U"ł" },
- { 'm', U"m̀" },
- { 'n', U"ή" },
- { 'o', U"ô" },
- { 'p', U"ṕ" },
- { 'q', U"q́" },
- { 'r', U"ŕ" },
- { 's', U"š" },
- { 't', U"ŧ" },
- { 'u', U"ü" },
- { 'v', U"ṽ" },
- { 'w', U"ŵ" },
- { 'x', U"x́" },
- { 'y', U"ý" },
- { 'z', U"ź" },
-};
-
-Vector<TranslationServer::LocaleScriptInfo> TranslationServer::locale_script_info;
-
-HashMap<String, String> TranslationServer::language_map;
-HashMap<String, String> TranslationServer::script_map;
-HashMap<String, String> TranslationServer::locale_rename_map;
-HashMap<String, String> TranslationServer::country_name_map;
-HashMap<String, String> TranslationServer::variant_map;
-HashMap<String, String> TranslationServer::country_rename_map;
-
-void TranslationServer::init_locale_info() {
- // Init locale info.
- language_map.clear();
- int idx = 0;
- while (language_list[idx][0] != nullptr) {
- language_map[language_list[idx][0]] = String::utf8(language_list[idx][1]);
- idx++;
- }
-
- // Init locale-script map.
- locale_script_info.clear();
- idx = 0;
- while (locale_scripts[idx][0] != nullptr) {
- LocaleScriptInfo info;
- info.name = locale_scripts[idx][0];
- info.script = locale_scripts[idx][1];
- info.default_country = locale_scripts[idx][2];
- Vector<String> supported_countries = String(locale_scripts[idx][3]).split(",", false);
- for (int i = 0; i < supported_countries.size(); i++) {
- info.supported_countries.insert(supported_countries[i]);
- }
- locale_script_info.push_back(info);
- idx++;
- }
-
- // Init supported script list.
- script_map.clear();
- idx = 0;
- while (script_list[idx][0] != nullptr) {
- script_map[script_list[idx][1]] = String::utf8(script_list[idx][0]);
- idx++;
- }
-
- // Init regional variant map.
- variant_map.clear();
- idx = 0;
- while (locale_variants[idx][0] != nullptr) {
- variant_map[locale_variants[idx][0]] = locale_variants[idx][1];
- idx++;
- }
-
- // Init locale renames.
- locale_rename_map.clear();
- idx = 0;
- while (locale_renames[idx][0] != nullptr) {
- if (!String(locale_renames[idx][1]).is_empty()) {
- locale_rename_map[locale_renames[idx][0]] = locale_renames[idx][1];
- }
- idx++;
- }
-
- // Init country names.
- country_name_map.clear();
- idx = 0;
- while (country_names[idx][0] != nullptr) {
- country_name_map[String(country_names[idx][0])] = String::utf8(country_names[idx][1]);
- idx++;
- }
-
- // Init country renames.
- country_rename_map.clear();
- idx = 0;
- while (country_renames[idx][0] != nullptr) {
- if (!String(country_renames[idx][1]).is_empty()) {
- country_rename_map[country_renames[idx][0]] = country_renames[idx][1];
- }
- idx++;
- }
-}
-
-String TranslationServer::standardize_locale(const String &p_locale) const {
- return _standardize_locale(p_locale, false);
-}
-
-String TranslationServer::_standardize_locale(const String &p_locale, bool p_add_defaults) const {
- // Replaces '-' with '_' for macOS style locales.
- String univ_locale = p_locale.replace("-", "_");
-
- // Extract locale elements.
- String lang_name, script_name, country_name, variant_name;
- Vector<String> locale_elements = univ_locale.get_slice("@", 0).split("_");
- lang_name = locale_elements[0];
- if (locale_elements.size() >= 2) {
- if (locale_elements[1].length() == 4 && is_ascii_upper_case(locale_elements[1][0]) && is_ascii_lower_case(locale_elements[1][1]) && is_ascii_lower_case(locale_elements[1][2]) && is_ascii_lower_case(locale_elements[1][3])) {
- script_name = locale_elements[1];
- }
- if (locale_elements[1].length() == 2 && is_ascii_upper_case(locale_elements[1][0]) && is_ascii_upper_case(locale_elements[1][1])) {
- country_name = locale_elements[1];
- }
- }
- if (locale_elements.size() >= 3) {
- if (locale_elements[2].length() == 2 && is_ascii_upper_case(locale_elements[2][0]) && is_ascii_upper_case(locale_elements[2][1])) {
- country_name = locale_elements[2];
- } else if (variant_map.has(locale_elements[2].to_lower()) && variant_map[locale_elements[2].to_lower()] == lang_name) {
- variant_name = locale_elements[2].to_lower();
- }
- }
- if (locale_elements.size() >= 4) {
- if (variant_map.has(locale_elements[3].to_lower()) && variant_map[locale_elements[3].to_lower()] == lang_name) {
- variant_name = locale_elements[3].to_lower();
- }
- }
-
- // Try extract script and variant from the extra part.
- Vector<String> script_extra = univ_locale.get_slice("@", 1).split(";");
- for (int i = 0; i < script_extra.size(); i++) {
- if (script_extra[i].to_lower() == "cyrillic") {
- script_name = "Cyrl";
- break;
- } else if (script_extra[i].to_lower() == "latin") {
- script_name = "Latn";
- break;
- } else if (script_extra[i].to_lower() == "devanagari") {
- script_name = "Deva";
- break;
- } else if (variant_map.has(script_extra[i].to_lower()) && variant_map[script_extra[i].to_lower()] == lang_name) {
- variant_name = script_extra[i].to_lower();
- }
- }
-
- // Handles known non-ISO language names used e.g. on Windows.
- if (locale_rename_map.has(lang_name)) {
- lang_name = locale_rename_map[lang_name];
- }
-
- // Handle country renames.
- if (country_rename_map.has(country_name)) {
- country_name = country_rename_map[country_name];
- }
-
- // Remove unsupported script codes.
- if (!script_map.has(script_name)) {
- script_name = "";
- }
-
- // Add script code base on language and country codes for some ambiguous cases.
- if (p_add_defaults) {
- if (script_name.is_empty()) {
- for (int i = 0; i < locale_script_info.size(); i++) {
- const LocaleScriptInfo &info = locale_script_info[i];
- if (info.name == lang_name) {
- if (country_name.is_empty() || info.supported_countries.has(country_name)) {
- script_name = info.script;
- break;
- }
- }
- }
- }
- if (!script_name.is_empty() && country_name.is_empty()) {
- // Add conntry code based on script for some ambiguous cases.
- for (int i = 0; i < locale_script_info.size(); i++) {
- const LocaleScriptInfo &info = locale_script_info[i];
- if (info.name == lang_name && info.script == script_name) {
- country_name = info.default_country;
- break;
- }
- }
- }
- }
-
- // Combine results.
- String out = lang_name;
- if (!script_name.is_empty()) {
- out = out + "_" + script_name;
- }
- if (!country_name.is_empty()) {
- out = out + "_" + country_name;
- }
- if (!variant_name.is_empty()) {
- out = out + "_" + variant_name;
- }
- return out;
-}
-
-int TranslationServer::compare_locales(const String &p_locale_a, const String &p_locale_b) const {
- String locale_a = _standardize_locale(p_locale_a, true);
- String locale_b = _standardize_locale(p_locale_b, true);
-
- if (locale_a == locale_b) {
- // Exact match.
- return 10;
- }
-
- Vector<String> locale_a_elements = locale_a.split("_");
- Vector<String> locale_b_elements = locale_b.split("_");
- if (locale_a_elements[0] == locale_b_elements[0]) {
- // Matching language, both locales have extra parts.
- // Return number of matching elements.
- int matching_elements = 1;
- for (int i = 1; i < locale_a_elements.size(); i++) {
- for (int j = 1; j < locale_b_elements.size(); j++) {
- if (locale_a_elements[i] == locale_b_elements[j]) {
- matching_elements++;
- }
- }
- }
- return matching_elements;
- } else {
- // No match.
- return 0;
- }
-}
-
-String TranslationServer::get_locale_name(const String &p_locale) const {
- String lang_name, script_name, country_name;
- Vector<String> locale_elements = standardize_locale(p_locale).split("_");
- lang_name = locale_elements[0];
- if (locale_elements.size() >= 2) {
- if (locale_elements[1].length() == 4 && is_ascii_upper_case(locale_elements[1][0]) && is_ascii_lower_case(locale_elements[1][1]) && is_ascii_lower_case(locale_elements[1][2]) && is_ascii_lower_case(locale_elements[1][3])) {
- script_name = locale_elements[1];
- }
- if (locale_elements[1].length() == 2 && is_ascii_upper_case(locale_elements[1][0]) && is_ascii_upper_case(locale_elements[1][1])) {
- country_name = locale_elements[1];
- }
- }
- if (locale_elements.size() >= 3) {
- if (locale_elements[2].length() == 2 && is_ascii_upper_case(locale_elements[2][0]) && is_ascii_upper_case(locale_elements[2][1])) {
- country_name = locale_elements[2];
- }
- }
-
- String name = language_map[lang_name];
- if (!script_name.is_empty()) {
- name = name + " (" + script_map[script_name] + ")";
- }
- if (!country_name.is_empty()) {
- name = name + ", " + country_name_map[country_name];
- }
- return name;
-}
-
-Vector<String> TranslationServer::get_all_languages() const {
- Vector<String> languages;
-
- for (const KeyValue<String, String> &E : language_map) {
- languages.push_back(E.key);
- }
-
- return languages;
-}
-
-String TranslationServer::get_language_name(const String &p_language) const {
- return language_map[p_language];
-}
-
-Vector<String> TranslationServer::get_all_scripts() const {
- Vector<String> scripts;
-
- for (const KeyValue<String, String> &E : script_map) {
- scripts.push_back(E.key);
- }
-
- return scripts;
-}
-
-String TranslationServer::get_script_name(const String &p_script) const {
- return script_map[p_script];
-}
-
-Vector<String> TranslationServer::get_all_countries() const {
- Vector<String> countries;
-
- for (const KeyValue<String, String> &E : country_name_map) {
- countries.push_back(E.key);
- }
-
- return countries;
-}
-
-String TranslationServer::get_country_name(const String &p_country) const {
- return country_name_map[p_country];
-}
-
-void TranslationServer::set_locale(const String &p_locale) {
- String new_locale = standardize_locale(p_locale);
- if (locale == new_locale) {
- return;
- }
-
- locale = new_locale;
- ResourceLoader::reload_translation_remaps();
-
- if (OS::get_singleton()->get_main_loop()) {
- OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_TRANSLATION_CHANGED);
- }
-}
-
-String TranslationServer::get_locale() const {
- return locale;
-}
-
-PackedStringArray TranslationServer::get_loaded_locales() const {
- PackedStringArray locales;
- for (const Ref<Translation> &E : translations) {
- const Ref<Translation> &t = E;
- ERR_FAIL_COND_V(t.is_null(), PackedStringArray());
- String l = t->get_locale();
-
- locales.push_back(l);
- }
-
- return locales;
-}
-
-void TranslationServer::add_translation(const Ref<Translation> &p_translation) {
- translations.insert(p_translation);
-}
-
-void TranslationServer::remove_translation(const Ref<Translation> &p_translation) {
- translations.erase(p_translation);
-}
-
-Ref<Translation> TranslationServer::get_translation_object(const String &p_locale) {
- Ref<Translation> res;
- int best_score = 0;
-
- for (const Ref<Translation> &E : translations) {
- const Ref<Translation> &t = E;
- ERR_FAIL_COND_V(t.is_null(), nullptr);
- String l = t->get_locale();
-
- int score = compare_locales(p_locale, l);
- if (score > 0 && score >= best_score) {
- res = t;
- best_score = score;
- if (score == 10) {
- break; // Exact match, skip the rest.
- }
- }
- }
- return res;
-}
-
-void TranslationServer::clear() {
- translations.clear();
-}
-
-StringName TranslationServer::translate(const StringName &p_message, const StringName &p_context) const {
- // Match given message against the translation catalog for the project locale.
-
- if (!enabled) {
- return p_message;
- }
-
- StringName res = _get_message_from_translations(p_message, p_context, locale, false);
-
- if (!res && fallback.length() >= 2) {
- res = _get_message_from_translations(p_message, p_context, fallback, false);
- }
-
- if (!res) {
- return pseudolocalization_enabled ? pseudolocalize(p_message) : p_message;
- }
-
- return pseudolocalization_enabled ? pseudolocalize(res) : res;
-}
-
-StringName TranslationServer::translate_plural(const StringName &p_message, const StringName &p_message_plural, int p_n, const StringName &p_context) const {
- if (!enabled) {
- if (p_n == 1) {
- return p_message;
- }
- return p_message_plural;
- }
-
- StringName res = _get_message_from_translations(p_message, p_context, locale, true, p_message_plural, p_n);
-
- if (!res && fallback.length() >= 2) {
- res = _get_message_from_translations(p_message, p_context, fallback, true, p_message_plural, p_n);
- }
-
- if (!res) {
- if (p_n == 1) {
- return p_message;
- }
- return p_message_plural;
- }
-
- return res;
-}
-
-StringName TranslationServer::_get_message_from_translations(const StringName &p_message, const StringName &p_context, const String &p_locale, bool plural, const String &p_message_plural, int p_n) const {
- StringName res;
- int best_score = 0;
-
- for (const Ref<Translation> &E : translations) {
- const Ref<Translation> &t = E;
- ERR_FAIL_COND_V(t.is_null(), p_message);
- String l = t->get_locale();
-
- int score = compare_locales(p_locale, l);
- if (score > 0 && score >= best_score) {
- StringName r;
- if (!plural) {
- r = t->get_message(p_message, p_context);
- } else {
- r = t->get_plural_message(p_message, p_message_plural, p_n, p_context);
- }
- if (!r) {
- continue;
- }
- res = r;
- best_score = score;
- if (score == 10) {
- break; // Exact match, skip the rest.
- }
- }
- }
-
- return res;
-}
-
-TranslationServer *TranslationServer::singleton = nullptr;
-
-bool TranslationServer::_load_translations(const String &p_from) {
- if (ProjectSettings::get_singleton()->has_setting(p_from)) {
- const Vector<String> &translation_names = GLOBAL_GET(p_from);
-
- int tcount = translation_names.size();
-
- if (tcount) {
- const String *r = translation_names.ptr();
-
- for (int i = 0; i < tcount; i++) {
- Ref<Translation> tr = ResourceLoader::load(r[i]);
- if (tr.is_valid()) {
- add_translation(tr);
- }
- }
- }
- return true;
- }
-
- return false;
-}
-
-void TranslationServer::setup() {
- String test = GLOBAL_DEF("internationalization/locale/test", "");
- test = test.strip_edges();
- if (!test.is_empty()) {
- set_locale(test);
- } else {
- set_locale(OS::get_singleton()->get_locale());
- }
-
- fallback = GLOBAL_DEF("internationalization/locale/fallback", "en");
- pseudolocalization_enabled = GLOBAL_DEF("internationalization/pseudolocalization/use_pseudolocalization", false);
- pseudolocalization_accents_enabled = GLOBAL_DEF("internationalization/pseudolocalization/replace_with_accents", true);
- pseudolocalization_double_vowels_enabled = GLOBAL_DEF("internationalization/pseudolocalization/double_vowels", false);
- pseudolocalization_fake_bidi_enabled = GLOBAL_DEF("internationalization/pseudolocalization/fake_bidi", false);
- pseudolocalization_override_enabled = GLOBAL_DEF("internationalization/pseudolocalization/override", false);
- expansion_ratio = GLOBAL_DEF("internationalization/pseudolocalization/expansion_ratio", 0.0);
- pseudolocalization_prefix = GLOBAL_DEF("internationalization/pseudolocalization/prefix", "[");
- pseudolocalization_suffix = GLOBAL_DEF("internationalization/pseudolocalization/suffix", "]");
- pseudolocalization_skip_placeholders_enabled = GLOBAL_DEF("internationalization/pseudolocalization/skip_placeholders", true);
-
-#ifdef TOOLS_ENABLED
- ProjectSettings::get_singleton()->set_custom_property_info(PropertyInfo(Variant::STRING, "internationalization/locale/fallback", PROPERTY_HINT_LOCALE_ID, ""));
-#endif
-}
-
-void TranslationServer::set_tool_translation(const Ref<Translation> &p_translation) {
- tool_translation = p_translation;
-}
-
-Ref<Translation> TranslationServer::get_tool_translation() const {
- return tool_translation;
-}
-
-String TranslationServer::get_tool_locale() {
-#ifdef TOOLS_ENABLED
- if (Engine::get_singleton()->is_editor_hint() || Engine::get_singleton()->is_project_manager_hint()) {
- if (TranslationServer::get_singleton()->get_tool_translation().is_valid()) {
- return tool_translation->get_locale();
- } else {
- return "en";
- }
- } else {
-#else
- {
-#endif
- // Look for best matching loaded translation.
- String best_locale = "en";
- int best_score = 0;
-
- for (const Ref<Translation> &E : translations) {
- const Ref<Translation> &t = E;
- ERR_FAIL_COND_V(t.is_null(), best_locale);
- String l = t->get_locale();
-
- int score = compare_locales(locale, l);
- if (score > 0 && score >= best_score) {
- best_locale = l;
- best_score = score;
- if (score == 10) {
- break; // Exact match, skip the rest.
- }
- }
- }
- return best_locale;
- }
-}
-
-StringName TranslationServer::tool_translate(const StringName &p_message, const StringName &p_context) const {
- if (tool_translation.is_valid()) {
- StringName r = tool_translation->get_message(p_message, p_context);
- if (r) {
- return r;
- }
- }
- return p_message;
-}
-
-StringName TranslationServer::tool_translate_plural(const StringName &p_message, const StringName &p_message_plural, int p_n, const StringName &p_context) const {
- if (tool_translation.is_valid()) {
- StringName r = tool_translation->get_plural_message(p_message, p_message_plural, p_n, p_context);
- if (r) {
- return r;
- }
- }
-
- if (p_n == 1) {
- return p_message;
- }
- return p_message_plural;
-}
-
-void TranslationServer::set_property_translation(const Ref<Translation> &p_translation) {
- property_translation = p_translation;
-}
-
-StringName TranslationServer::property_translate(const StringName &p_message, const StringName &p_context) const {
- if (property_translation.is_valid()) {
- StringName r = property_translation->get_message(p_message, p_context);
- if (r) {
- return r;
- }
- }
- return p_message;
-}
-
-void TranslationServer::set_doc_translation(const Ref<Translation> &p_translation) {
- doc_translation = p_translation;
-}
-
-StringName TranslationServer::doc_translate(const StringName &p_message, const StringName &p_context) const {
- if (doc_translation.is_valid()) {
- StringName r = doc_translation->get_message(p_message, p_context);
- if (r) {
- return r;
- }
- }
- return p_message;
-}
-
-StringName TranslationServer::doc_translate_plural(const StringName &p_message, const StringName &p_message_plural, int p_n, const StringName &p_context) const {
- if (doc_translation.is_valid()) {
- StringName r = doc_translation->get_plural_message(p_message, p_message_plural, p_n, p_context);
- if (r) {
- return r;
- }
- }
-
- if (p_n == 1) {
- return p_message;
- }
- return p_message_plural;
-}
-
-void TranslationServer::set_extractable_translation(const Ref<Translation> &p_translation) {
- extractable_translation = p_translation;
-}
-
-StringName TranslationServer::extractable_translate(const StringName &p_message, const StringName &p_context) const {
- if (extractable_translation.is_valid()) {
- StringName r = extractable_translation->get_message(p_message, p_context);
- if (r) {
- return r;
- }
- }
- return p_message;
-}
-
-StringName TranslationServer::extractable_translate_plural(const StringName &p_message, const StringName &p_message_plural, int p_n, const StringName &p_context) const {
- if (extractable_translation.is_valid()) {
- StringName r = extractable_translation->get_plural_message(p_message, p_message_plural, p_n, p_context);
- if (r) {
- return r;
- }
- }
-
- if (p_n == 1) {
- return p_message;
- }
- return p_message_plural;
-}
-
-bool TranslationServer::is_pseudolocalization_enabled() const {
- return pseudolocalization_enabled;
-}
-
-void TranslationServer::set_pseudolocalization_enabled(bool p_enabled) {
- pseudolocalization_enabled = p_enabled;
-
- ResourceLoader::reload_translation_remaps();
-
- if (OS::get_singleton()->get_main_loop()) {
- OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_TRANSLATION_CHANGED);
- }
-}
-
-void TranslationServer::reload_pseudolocalization() {
- pseudolocalization_accents_enabled = GLOBAL_GET("internationalization/pseudolocalization/replace_with_accents");
- pseudolocalization_double_vowels_enabled = GLOBAL_GET("internationalization/pseudolocalization/double_vowels");
- pseudolocalization_fake_bidi_enabled = GLOBAL_GET("internationalization/pseudolocalization/fake_bidi");
- pseudolocalization_override_enabled = GLOBAL_GET("internationalization/pseudolocalization/override");
- expansion_ratio = GLOBAL_GET("internationalization/pseudolocalization/expansion_ratio");
- pseudolocalization_prefix = GLOBAL_GET("internationalization/pseudolocalization/prefix");
- pseudolocalization_suffix = GLOBAL_GET("internationalization/pseudolocalization/suffix");
- pseudolocalization_skip_placeholders_enabled = GLOBAL_GET("internationalization/pseudolocalization/skip_placeholders");
-
- ResourceLoader::reload_translation_remaps();
-
- if (OS::get_singleton()->get_main_loop()) {
- OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_TRANSLATION_CHANGED);
- }
-}
-
-StringName TranslationServer::pseudolocalize(const StringName &p_message) const {
- String message = p_message;
- int length = message.length();
- if (pseudolocalization_override_enabled) {
- message = get_override_string(message);
- }
-
- if (pseudolocalization_double_vowels_enabled) {
- message = double_vowels(message);
- }
-
- if (pseudolocalization_accents_enabled) {
- message = replace_with_accented_string(message);
- }
-
- if (pseudolocalization_fake_bidi_enabled) {
- message = wrap_with_fakebidi_characters(message);
- }
-
- StringName res = add_padding(message, length);
- return res;
-}
-
-StringName TranslationServer::tool_pseudolocalize(const StringName &p_message) const {
- String message = p_message;
- message = double_vowels(message);
- message = replace_with_accented_string(message);
- StringName res = "[!!! " + message + " !!!]";
- return res;
-}
-
-String TranslationServer::get_override_string(String &p_message) const {
- String res;
- for (int i = 0; i < p_message.length(); i++) {
- if (pseudolocalization_skip_placeholders_enabled && is_placeholder(p_message, i)) {
- res += p_message[i];
- res += p_message[i + 1];
- i++;
- continue;
- }
- res += '*';
- }
- return res;
-}
-
-String TranslationServer::double_vowels(String &p_message) const {
- String res;
- for (int i = 0; i < p_message.length(); i++) {
- if (pseudolocalization_skip_placeholders_enabled && is_placeholder(p_message, i)) {
- res += p_message[i];
- res += p_message[i + 1];
- i++;
- continue;
- }
- res += p_message[i];
- if (p_message[i] == 'a' || p_message[i] == 'e' || p_message[i] == 'i' || p_message[i] == 'o' || p_message[i] == 'u' ||
- p_message[i] == 'A' || p_message[i] == 'E' || p_message[i] == 'I' || p_message[i] == 'O' || p_message[i] == 'U') {
- res += p_message[i];
- }
- }
- return res;
-};
-
-String TranslationServer::replace_with_accented_string(String &p_message) const {
- String res;
- for (int i = 0; i < p_message.length(); i++) {
- if (pseudolocalization_skip_placeholders_enabled && is_placeholder(p_message, i)) {
- res += p_message[i];
- res += p_message[i + 1];
- i++;
- continue;
- }
- const char32_t *accented = get_accented_version(p_message[i]);
- if (accented) {
- res += accented;
- } else {
- res += p_message[i];
- }
- }
- return res;
-}
-
-String TranslationServer::wrap_with_fakebidi_characters(String &p_message) const {
- String res;
- char32_t fakebidiprefix = U'\u202e';
- char32_t fakebidisuffix = U'\u202c';
- res += fakebidiprefix;
- // The fake bidi unicode gets popped at every newline so pushing it back at every newline.
- for (int i = 0; i < p_message.length(); i++) {
- if (p_message[i] == '\n') {
- res += fakebidisuffix;
- res += p_message[i];
- res += fakebidiprefix;
- } else if (pseudolocalization_skip_placeholders_enabled && is_placeholder(p_message, i)) {
- res += fakebidisuffix;
- res += p_message[i];
- res += p_message[i + 1];
- res += fakebidiprefix;
- i++;
- } else {
- res += p_message[i];
- }
- }
- res += fakebidisuffix;
- return res;
-}
-
-String TranslationServer::add_padding(const String &p_message, int p_length) const {
- String underscores = String("_").repeat(p_length * expansion_ratio / 2);
- String prefix = pseudolocalization_prefix + underscores;
- String suffix = underscores + pseudolocalization_suffix;
-
- return prefix + p_message + suffix;
-}
-
-const char32_t *TranslationServer::get_accented_version(char32_t p_character) const {
- if (!is_ascii_alphabet_char(p_character)) {
- return nullptr;
- }
-
- for (unsigned int i = 0; i < sizeof(_character_to_accented) / sizeof(_character_to_accented[0]); i++) {
- if (_character_to_accented[i].character == p_character) {
- return _character_to_accented[i].accented_character;
- }
- }
-
- return nullptr;
-}
-
-bool TranslationServer::is_placeholder(String &p_message, int p_index) const {
- return p_index < p_message.length() - 1 && p_message[p_index] == '%' &&
- (p_message[p_index + 1] == 's' || p_message[p_index + 1] == 'c' || p_message[p_index + 1] == 'd' ||
- p_message[p_index + 1] == 'o' || p_message[p_index + 1] == 'x' || p_message[p_index + 1] == 'X' || p_message[p_index + 1] == 'f');
-}
-
-#ifdef TOOLS_ENABLED
-void TranslationServer::get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const {
- const String pf = p_function;
- if (p_idx == 0) {
- HashMap<String, String> *target_hash_map = nullptr;
- if (pf == "get_language_name") {
- target_hash_map = &language_map;
- } else if (pf == "get_script_name") {
- target_hash_map = &script_map;
- } else if (pf == "get_country_name") {
- target_hash_map = &country_name_map;
- }
-
- if (target_hash_map) {
- for (const KeyValue<String, String> &E : *target_hash_map) {
- r_options->push_back(E.key.quote());
- }
- }
- }
- Object::get_argument_options(p_function, p_idx, r_options);
-}
-#endif // TOOLS_ENABLED
-
-void TranslationServer::_bind_methods() {
- ClassDB::bind_method(D_METHOD("set_locale", "locale"), &TranslationServer::set_locale);
- ClassDB::bind_method(D_METHOD("get_locale"), &TranslationServer::get_locale);
- ClassDB::bind_method(D_METHOD("get_tool_locale"), &TranslationServer::get_tool_locale);
-
- ClassDB::bind_method(D_METHOD("compare_locales", "locale_a", "locale_b"), &TranslationServer::compare_locales);
- ClassDB::bind_method(D_METHOD("standardize_locale", "locale"), &TranslationServer::standardize_locale);
-
- ClassDB::bind_method(D_METHOD("get_all_languages"), &TranslationServer::get_all_languages);
- ClassDB::bind_method(D_METHOD("get_language_name", "language"), &TranslationServer::get_language_name);
-
- ClassDB::bind_method(D_METHOD("get_all_scripts"), &TranslationServer::get_all_scripts);
- ClassDB::bind_method(D_METHOD("get_script_name", "script"), &TranslationServer::get_script_name);
-
- ClassDB::bind_method(D_METHOD("get_all_countries"), &TranslationServer::get_all_countries);
- ClassDB::bind_method(D_METHOD("get_country_name", "country"), &TranslationServer::get_country_name);
-
- ClassDB::bind_method(D_METHOD("get_locale_name", "locale"), &TranslationServer::get_locale_name);
-
- ClassDB::bind_method(D_METHOD("translate", "message", "context"), &TranslationServer::translate, DEFVAL(StringName()));
- ClassDB::bind_method(D_METHOD("translate_plural", "message", "plural_message", "n", "context"), &TranslationServer::translate_plural, DEFVAL(StringName()));
-
- ClassDB::bind_method(D_METHOD("add_translation", "translation"), &TranslationServer::add_translation);
- ClassDB::bind_method(D_METHOD("remove_translation", "translation"), &TranslationServer::remove_translation);
- ClassDB::bind_method(D_METHOD("get_translation_object", "locale"), &TranslationServer::get_translation_object);
-
- ClassDB::bind_method(D_METHOD("clear"), &TranslationServer::clear);
-
- ClassDB::bind_method(D_METHOD("get_loaded_locales"), &TranslationServer::get_loaded_locales);
-
- ClassDB::bind_method(D_METHOD("is_pseudolocalization_enabled"), &TranslationServer::is_pseudolocalization_enabled);
- ClassDB::bind_method(D_METHOD("set_pseudolocalization_enabled", "enabled"), &TranslationServer::set_pseudolocalization_enabled);
- ClassDB::bind_method(D_METHOD("reload_pseudolocalization"), &TranslationServer::reload_pseudolocalization);
- ClassDB::bind_method(D_METHOD("pseudolocalize", "message"), &TranslationServer::pseudolocalize);
- ADD_PROPERTY(PropertyInfo(Variant::Type::BOOL, "pseudolocalization_enabled"), "set_pseudolocalization_enabled", "is_pseudolocalization_enabled");
-}
-
-void TranslationServer::load_translations() {
- _load_translations("internationalization/locale/translations"); //all
- _load_translations("internationalization/locale/translations_" + locale.substr(0, 2));
-
- if (locale.substr(0, 2) != locale) {
- _load_translations("internationalization/locale/translations_" + locale);
- }
-}
-
-TranslationServer::TranslationServer() {
- singleton = this;
- init_locale_info();
-}
diff --git a/core/string/translation.h b/core/string/translation.h
index 0a7eacc45f..2c5baae8b7 100644
--- a/core/string/translation.h
+++ b/core/string/translation.h
@@ -74,132 +74,4 @@ public:
Translation() {}
};
-class TranslationServer : public Object {
- GDCLASS(TranslationServer, Object);
-
- String locale = "en";
- String fallback;
-
- HashSet<Ref<Translation>> translations;
- Ref<Translation> tool_translation;
- Ref<Translation> property_translation;
- Ref<Translation> doc_translation;
- Ref<Translation> extractable_translation;
-
- bool enabled = true;
-
- bool pseudolocalization_enabled = false;
- bool pseudolocalization_accents_enabled = false;
- bool pseudolocalization_double_vowels_enabled = false;
- bool pseudolocalization_fake_bidi_enabled = false;
- bool pseudolocalization_override_enabled = false;
- bool pseudolocalization_skip_placeholders_enabled = false;
- float expansion_ratio = 0.0;
- String pseudolocalization_prefix;
- String pseudolocalization_suffix;
-
- StringName tool_pseudolocalize(const StringName &p_message) const;
- String get_override_string(String &p_message) const;
- String double_vowels(String &p_message) const;
- String replace_with_accented_string(String &p_message) const;
- String wrap_with_fakebidi_characters(String &p_message) const;
- String add_padding(const String &p_message, int p_length) const;
- const char32_t *get_accented_version(char32_t p_character) const;
- bool is_placeholder(String &p_message, int p_index) const;
-
- static TranslationServer *singleton;
- bool _load_translations(const String &p_from);
- String _standardize_locale(const String &p_locale, bool p_add_defaults) const;
-
- StringName _get_message_from_translations(const StringName &p_message, const StringName &p_context, const String &p_locale, bool plural, const String &p_message_plural = "", int p_n = 0) const;
-
- static void _bind_methods();
-
-#ifndef DISABLE_DEPRECATED
- static void _bind_compatibility_methods();
-#endif
-
- struct LocaleScriptInfo {
- String name;
- String script;
- String default_country;
- HashSet<String> supported_countries;
- };
- static Vector<LocaleScriptInfo> locale_script_info;
-
- static HashMap<String, String> language_map;
- static HashMap<String, String> script_map;
- static HashMap<String, String> locale_rename_map;
- static HashMap<String, String> country_name_map;
- static HashMap<String, String> country_rename_map;
- static HashMap<String, String> variant_map;
-
- void init_locale_info();
-
-public:
- _FORCE_INLINE_ static TranslationServer *get_singleton() { return singleton; }
-
- void set_enabled(bool p_enabled) { enabled = p_enabled; }
- _FORCE_INLINE_ bool is_enabled() const { return enabled; }
-
- void set_locale(const String &p_locale);
- String get_locale() const;
- Ref<Translation> get_translation_object(const String &p_locale);
-
- Vector<String> get_all_languages() const;
- String get_language_name(const String &p_language) const;
-
- Vector<String> get_all_scripts() const;
- String get_script_name(const String &p_script) const;
-
- Vector<String> get_all_countries() const;
- String get_country_name(const String &p_country) const;
-
- String get_locale_name(const String &p_locale) const;
-
- PackedStringArray get_loaded_locales() const;
-
- void add_translation(const Ref<Translation> &p_translation);
- void remove_translation(const Ref<Translation> &p_translation);
-
- StringName translate(const StringName &p_message, const StringName &p_context = "") const;
- StringName translate_plural(const StringName &p_message, const StringName &p_message_plural, int p_n, const StringName &p_context = "") const;
-
- StringName pseudolocalize(const StringName &p_message) const;
-
- bool is_pseudolocalization_enabled() const;
- void set_pseudolocalization_enabled(bool p_enabled);
- void reload_pseudolocalization();
-
- String standardize_locale(const String &p_locale) const;
-
- int compare_locales(const String &p_locale_a, const String &p_locale_b) const;
-
- String get_tool_locale();
- void set_tool_translation(const Ref<Translation> &p_translation);
- Ref<Translation> get_tool_translation() const;
- StringName tool_translate(const StringName &p_message, const StringName &p_context = "") const;
- StringName tool_translate_plural(const StringName &p_message, const StringName &p_message_plural, int p_n, const StringName &p_context = "") const;
- void set_property_translation(const Ref<Translation> &p_translation);
- StringName property_translate(const StringName &p_message, const StringName &p_context = "") const;
- void set_doc_translation(const Ref<Translation> &p_translation);
- StringName doc_translate(const StringName &p_message, const StringName &p_context = "") const;
- StringName doc_translate_plural(const StringName &p_message, const StringName &p_message_plural, int p_n, const StringName &p_context = "") const;
- void set_extractable_translation(const Ref<Translation> &p_translation);
- StringName extractable_translate(const StringName &p_message, const StringName &p_context = "") const;
- StringName extractable_translate_plural(const StringName &p_message, const StringName &p_message_plural, int p_n, const StringName &p_context = "") const;
-
- void setup();
-
- void clear();
-
- void load_translations();
-
-#ifdef TOOLS_ENABLED
- virtual void get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const override;
-#endif // TOOLS_ENABLED
-
- TranslationServer();
-};
-
#endif // TRANSLATION_H
diff --git a/core/string/translation_server.compat.inc b/core/string/translation_server.compat.inc
new file mode 100644
index 0000000000..9d1ee8b9df
--- /dev/null
+++ b/core/string/translation_server.compat.inc
@@ -0,0 +1,38 @@
+/**************************************************************************/
+/* translation_server.compat.inc */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* 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 DISABLE_DEPRECATED
+
+void TranslationServer::_bind_compatibility_methods() {
+ ClassDB::bind_compatibility_method(D_METHOD("translate", "message", "context"), &TranslationServer::translate, DEFVAL(""));
+ ClassDB::bind_compatibility_method(D_METHOD("translate_plural", "message", "plural_message", "n", "context"), &TranslationServer::translate_plural, DEFVAL(""));
+}
+
+#endif
diff --git a/core/string/translation_server.cpp b/core/string/translation_server.cpp
new file mode 100644
index 0000000000..6e784881d0
--- /dev/null
+++ b/core/string/translation_server.cpp
@@ -0,0 +1,947 @@
+/**************************************************************************/
+/* translation_server.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* 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 "translation_server.h"
+#include "translation_server.compat.inc"
+
+#include "core/config/project_settings.h"
+#include "core/io/resource_loader.h"
+#include "core/os/os.h"
+#include "core/string/locales.h"
+
+#ifdef TOOLS_ENABLED
+#include "main/main.h"
+#endif
+
+struct _character_accent_pair {
+ const char32_t character;
+ const char32_t *accented_character;
+};
+
+static _character_accent_pair _character_to_accented[] = {
+ { 'A', U"Å" },
+ { 'B', U"ß" },
+ { 'C', U"Ç" },
+ { 'D', U"Ð" },
+ { 'E', U"É" },
+ { 'F', U"F́" },
+ { 'G', U"Ĝ" },
+ { 'H', U"Ĥ" },
+ { 'I', U"Ĩ" },
+ { 'J', U"Ĵ" },
+ { 'K', U"ĸ" },
+ { 'L', U"Ł" },
+ { 'M', U"Ḿ" },
+ { 'N', U"й" },
+ { 'O', U"Ö" },
+ { 'P', U"Ṕ" },
+ { 'Q', U"Q́" },
+ { 'R', U"Ř" },
+ { 'S', U"Ŝ" },
+ { 'T', U"Ŧ" },
+ { 'U', U"Ũ" },
+ { 'V', U"Ṽ" },
+ { 'W', U"Ŵ" },
+ { 'X', U"X́" },
+ { 'Y', U"Ÿ" },
+ { 'Z', U"Ž" },
+ { 'a', U"á" },
+ { 'b', U"ḅ" },
+ { 'c', U"ć" },
+ { 'd', U"d́" },
+ { 'e', U"é" },
+ { 'f', U"f́" },
+ { 'g', U"ǵ" },
+ { 'h', U"h̀" },
+ { 'i', U"í" },
+ { 'j', U"ǰ" },
+ { 'k', U"ḱ" },
+ { 'l', U"ł" },
+ { 'm', U"m̀" },
+ { 'n', U"ή" },
+ { 'o', U"ô" },
+ { 'p', U"ṕ" },
+ { 'q', U"q́" },
+ { 'r', U"ŕ" },
+ { 's', U"š" },
+ { 't', U"ŧ" },
+ { 'u', U"ü" },
+ { 'v', U"ṽ" },
+ { 'w', U"ŵ" },
+ { 'x', U"x́" },
+ { 'y', U"ý" },
+ { 'z', U"ź" },
+};
+
+Vector<TranslationServer::LocaleScriptInfo> TranslationServer::locale_script_info;
+
+HashMap<String, String> TranslationServer::language_map;
+HashMap<String, String> TranslationServer::script_map;
+HashMap<String, String> TranslationServer::locale_rename_map;
+HashMap<String, String> TranslationServer::country_name_map;
+HashMap<String, String> TranslationServer::variant_map;
+HashMap<String, String> TranslationServer::country_rename_map;
+
+void TranslationServer::init_locale_info() {
+ // Init locale info.
+ language_map.clear();
+ int idx = 0;
+ while (language_list[idx][0] != nullptr) {
+ language_map[language_list[idx][0]] = String::utf8(language_list[idx][1]);
+ idx++;
+ }
+
+ // Init locale-script map.
+ locale_script_info.clear();
+ idx = 0;
+ while (locale_scripts[idx][0] != nullptr) {
+ LocaleScriptInfo info;
+ info.name = locale_scripts[idx][0];
+ info.script = locale_scripts[idx][1];
+ info.default_country = locale_scripts[idx][2];
+ Vector<String> supported_countries = String(locale_scripts[idx][3]).split(",", false);
+ for (int i = 0; i < supported_countries.size(); i++) {
+ info.supported_countries.insert(supported_countries[i]);
+ }
+ locale_script_info.push_back(info);
+ idx++;
+ }
+
+ // Init supported script list.
+ script_map.clear();
+ idx = 0;
+ while (script_list[idx][0] != nullptr) {
+ script_map[script_list[idx][1]] = String::utf8(script_list[idx][0]);
+ idx++;
+ }
+
+ // Init regional variant map.
+ variant_map.clear();
+ idx = 0;
+ while (locale_variants[idx][0] != nullptr) {
+ variant_map[locale_variants[idx][0]] = locale_variants[idx][1];
+ idx++;
+ }
+
+ // Init locale renames.
+ locale_rename_map.clear();
+ idx = 0;
+ while (locale_renames[idx][0] != nullptr) {
+ if (!String(locale_renames[idx][1]).is_empty()) {
+ locale_rename_map[locale_renames[idx][0]] = locale_renames[idx][1];
+ }
+ idx++;
+ }
+
+ // Init country names.
+ country_name_map.clear();
+ idx = 0;
+ while (country_names[idx][0] != nullptr) {
+ country_name_map[String(country_names[idx][0])] = String::utf8(country_names[idx][1]);
+ idx++;
+ }
+
+ // Init country renames.
+ country_rename_map.clear();
+ idx = 0;
+ while (country_renames[idx][0] != nullptr) {
+ if (!String(country_renames[idx][1]).is_empty()) {
+ country_rename_map[country_renames[idx][0]] = country_renames[idx][1];
+ }
+ idx++;
+ }
+}
+
+String TranslationServer::standardize_locale(const String &p_locale) const {
+ return _standardize_locale(p_locale, false);
+}
+
+String TranslationServer::_standardize_locale(const String &p_locale, bool p_add_defaults) const {
+ // Replaces '-' with '_' for macOS style locales.
+ String univ_locale = p_locale.replace("-", "_");
+
+ // Extract locale elements.
+ String lang_name, script_name, country_name, variant_name;
+ Vector<String> locale_elements = univ_locale.get_slice("@", 0).split("_");
+ lang_name = locale_elements[0];
+ if (locale_elements.size() >= 2) {
+ if (locale_elements[1].length() == 4 && is_ascii_upper_case(locale_elements[1][0]) && is_ascii_lower_case(locale_elements[1][1]) && is_ascii_lower_case(locale_elements[1][2]) && is_ascii_lower_case(locale_elements[1][3])) {
+ script_name = locale_elements[1];
+ }
+ if (locale_elements[1].length() == 2 && is_ascii_upper_case(locale_elements[1][0]) && is_ascii_upper_case(locale_elements[1][1])) {
+ country_name = locale_elements[1];
+ }
+ }
+ if (locale_elements.size() >= 3) {
+ if (locale_elements[2].length() == 2 && is_ascii_upper_case(locale_elements[2][0]) && is_ascii_upper_case(locale_elements[2][1])) {
+ country_name = locale_elements[2];
+ } else if (variant_map.has(locale_elements[2].to_lower()) && variant_map[locale_elements[2].to_lower()] == lang_name) {
+ variant_name = locale_elements[2].to_lower();
+ }
+ }
+ if (locale_elements.size() >= 4) {
+ if (variant_map.has(locale_elements[3].to_lower()) && variant_map[locale_elements[3].to_lower()] == lang_name) {
+ variant_name = locale_elements[3].to_lower();
+ }
+ }
+
+ // Try extract script and variant from the extra part.
+ Vector<String> script_extra = univ_locale.get_slice("@", 1).split(";");
+ for (int i = 0; i < script_extra.size(); i++) {
+ if (script_extra[i].to_lower() == "cyrillic") {
+ script_name = "Cyrl";
+ break;
+ } else if (script_extra[i].to_lower() == "latin") {
+ script_name = "Latn";
+ break;
+ } else if (script_extra[i].to_lower() == "devanagari") {
+ script_name = "Deva";
+ break;
+ } else if (variant_map.has(script_extra[i].to_lower()) && variant_map[script_extra[i].to_lower()] == lang_name) {
+ variant_name = script_extra[i].to_lower();
+ }
+ }
+
+ // Handles known non-ISO language names used e.g. on Windows.
+ if (locale_rename_map.has(lang_name)) {
+ lang_name = locale_rename_map[lang_name];
+ }
+
+ // Handle country renames.
+ if (country_rename_map.has(country_name)) {
+ country_name = country_rename_map[country_name];
+ }
+
+ // Remove unsupported script codes.
+ if (!script_map.has(script_name)) {
+ script_name = "";
+ }
+
+ // Add script code base on language and country codes for some ambiguous cases.
+ if (p_add_defaults) {
+ if (script_name.is_empty()) {
+ for (int i = 0; i < locale_script_info.size(); i++) {
+ const LocaleScriptInfo &info = locale_script_info[i];
+ if (info.name == lang_name) {
+ if (country_name.is_empty() || info.supported_countries.has(country_name)) {
+ script_name = info.script;
+ break;
+ }
+ }
+ }
+ }
+ if (!script_name.is_empty() && country_name.is_empty()) {
+ // Add conntry code based on script for some ambiguous cases.
+ for (int i = 0; i < locale_script_info.size(); i++) {
+ const LocaleScriptInfo &info = locale_script_info[i];
+ if (info.name == lang_name && info.script == script_name) {
+ country_name = info.default_country;
+ break;
+ }
+ }
+ }
+ }
+
+ // Combine results.
+ String out = lang_name;
+ if (!script_name.is_empty()) {
+ out = out + "_" + script_name;
+ }
+ if (!country_name.is_empty()) {
+ out = out + "_" + country_name;
+ }
+ if (!variant_name.is_empty()) {
+ out = out + "_" + variant_name;
+ }
+ return out;
+}
+
+int TranslationServer::compare_locales(const String &p_locale_a, const String &p_locale_b) const {
+ String locale_a = _standardize_locale(p_locale_a, true);
+ String locale_b = _standardize_locale(p_locale_b, true);
+
+ if (locale_a == locale_b) {
+ // Exact match.
+ return 10;
+ }
+
+ Vector<String> locale_a_elements = locale_a.split("_");
+ Vector<String> locale_b_elements = locale_b.split("_");
+ if (locale_a_elements[0] == locale_b_elements[0]) {
+ // Matching language, both locales have extra parts.
+ // Return number of matching elements.
+ int matching_elements = 1;
+ for (int i = 1; i < locale_a_elements.size(); i++) {
+ for (int j = 1; j < locale_b_elements.size(); j++) {
+ if (locale_a_elements[i] == locale_b_elements[j]) {
+ matching_elements++;
+ }
+ }
+ }
+ return matching_elements;
+ } else {
+ // No match.
+ return 0;
+ }
+}
+
+String TranslationServer::get_locale_name(const String &p_locale) const {
+ String lang_name, script_name, country_name;
+ Vector<String> locale_elements = standardize_locale(p_locale).split("_");
+ lang_name = locale_elements[0];
+ if (locale_elements.size() >= 2) {
+ if (locale_elements[1].length() == 4 && is_ascii_upper_case(locale_elements[1][0]) && is_ascii_lower_case(locale_elements[1][1]) && is_ascii_lower_case(locale_elements[1][2]) && is_ascii_lower_case(locale_elements[1][3])) {
+ script_name = locale_elements[1];
+ }
+ if (locale_elements[1].length() == 2 && is_ascii_upper_case(locale_elements[1][0]) && is_ascii_upper_case(locale_elements[1][1])) {
+ country_name = locale_elements[1];
+ }
+ }
+ if (locale_elements.size() >= 3) {
+ if (locale_elements[2].length() == 2 && is_ascii_upper_case(locale_elements[2][0]) && is_ascii_upper_case(locale_elements[2][1])) {
+ country_name = locale_elements[2];
+ }
+ }
+
+ String name = language_map[lang_name];
+ if (!script_name.is_empty()) {
+ name = name + " (" + script_map[script_name] + ")";
+ }
+ if (!country_name.is_empty()) {
+ name = name + ", " + country_name_map[country_name];
+ }
+ return name;
+}
+
+Vector<String> TranslationServer::get_all_languages() const {
+ Vector<String> languages;
+
+ for (const KeyValue<String, String> &E : language_map) {
+ languages.push_back(E.key);
+ }
+
+ return languages;
+}
+
+String TranslationServer::get_language_name(const String &p_language) const {
+ return language_map[p_language];
+}
+
+Vector<String> TranslationServer::get_all_scripts() const {
+ Vector<String> scripts;
+
+ for (const KeyValue<String, String> &E : script_map) {
+ scripts.push_back(E.key);
+ }
+
+ return scripts;
+}
+
+String TranslationServer::get_script_name(const String &p_script) const {
+ return script_map[p_script];
+}
+
+Vector<String> TranslationServer::get_all_countries() const {
+ Vector<String> countries;
+
+ for (const KeyValue<String, String> &E : country_name_map) {
+ countries.push_back(E.key);
+ }
+
+ return countries;
+}
+
+String TranslationServer::get_country_name(const String &p_country) const {
+ return country_name_map[p_country];
+}
+
+void TranslationServer::set_locale(const String &p_locale) {
+ String new_locale = standardize_locale(p_locale);
+ if (locale == new_locale) {
+ return;
+ }
+
+ locale = new_locale;
+ ResourceLoader::reload_translation_remaps();
+
+ if (OS::get_singleton()->get_main_loop()) {
+ OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_TRANSLATION_CHANGED);
+ }
+}
+
+String TranslationServer::get_locale() const {
+ return locale;
+}
+
+PackedStringArray TranslationServer::get_loaded_locales() const {
+ PackedStringArray locales;
+ for (const Ref<Translation> &E : translations) {
+ const Ref<Translation> &t = E;
+ ERR_FAIL_COND_V(t.is_null(), PackedStringArray());
+ String l = t->get_locale();
+
+ locales.push_back(l);
+ }
+
+ return locales;
+}
+
+void TranslationServer::add_translation(const Ref<Translation> &p_translation) {
+ translations.insert(p_translation);
+}
+
+void TranslationServer::remove_translation(const Ref<Translation> &p_translation) {
+ translations.erase(p_translation);
+}
+
+Ref<Translation> TranslationServer::get_translation_object(const String &p_locale) {
+ Ref<Translation> res;
+ int best_score = 0;
+
+ for (const Ref<Translation> &E : translations) {
+ const Ref<Translation> &t = E;
+ ERR_FAIL_COND_V(t.is_null(), nullptr);
+ String l = t->get_locale();
+
+ int score = compare_locales(p_locale, l);
+ if (score > 0 && score >= best_score) {
+ res = t;
+ best_score = score;
+ if (score == 10) {
+ break; // Exact match, skip the rest.
+ }
+ }
+ }
+ return res;
+}
+
+void TranslationServer::clear() {
+ translations.clear();
+}
+
+StringName TranslationServer::translate(const StringName &p_message, const StringName &p_context) const {
+ // Match given message against the translation catalog for the project locale.
+
+ if (!enabled) {
+ return p_message;
+ }
+
+ StringName res = _get_message_from_translations(p_message, p_context, locale, false);
+
+ if (!res && fallback.length() >= 2) {
+ res = _get_message_from_translations(p_message, p_context, fallback, false);
+ }
+
+ if (!res) {
+ return pseudolocalization_enabled ? pseudolocalize(p_message) : p_message;
+ }
+
+ return pseudolocalization_enabled ? pseudolocalize(res) : res;
+}
+
+StringName TranslationServer::translate_plural(const StringName &p_message, const StringName &p_message_plural, int p_n, const StringName &p_context) const {
+ if (!enabled) {
+ if (p_n == 1) {
+ return p_message;
+ }
+ return p_message_plural;
+ }
+
+ StringName res = _get_message_from_translations(p_message, p_context, locale, true, p_message_plural, p_n);
+
+ if (!res && fallback.length() >= 2) {
+ res = _get_message_from_translations(p_message, p_context, fallback, true, p_message_plural, p_n);
+ }
+
+ if (!res) {
+ if (p_n == 1) {
+ return p_message;
+ }
+ return p_message_plural;
+ }
+
+ return res;
+}
+
+StringName TranslationServer::_get_message_from_translations(const StringName &p_message, const StringName &p_context, const String &p_locale, bool plural, const String &p_message_plural, int p_n) const {
+ StringName res;
+ int best_score = 0;
+
+ for (const Ref<Translation> &E : translations) {
+ const Ref<Translation> &t = E;
+ ERR_FAIL_COND_V(t.is_null(), p_message);
+ String l = t->get_locale();
+
+ int score = compare_locales(p_locale, l);
+ if (score > 0 && score >= best_score) {
+ StringName r;
+ if (!plural) {
+ r = t->get_message(p_message, p_context);
+ } else {
+ r = t->get_plural_message(p_message, p_message_plural, p_n, p_context);
+ }
+ if (!r) {
+ continue;
+ }
+ res = r;
+ best_score = score;
+ if (score == 10) {
+ break; // Exact match, skip the rest.
+ }
+ }
+ }
+
+ return res;
+}
+
+TranslationServer *TranslationServer::singleton = nullptr;
+
+bool TranslationServer::_load_translations(const String &p_from) {
+ if (ProjectSettings::get_singleton()->has_setting(p_from)) {
+ const Vector<String> &translation_names = GLOBAL_GET(p_from);
+
+ int tcount = translation_names.size();
+
+ if (tcount) {
+ const String *r = translation_names.ptr();
+
+ for (int i = 0; i < tcount; i++) {
+ Ref<Translation> tr = ResourceLoader::load(r[i]);
+ if (tr.is_valid()) {
+ add_translation(tr);
+ }
+ }
+ }
+ return true;
+ }
+
+ return false;
+}
+
+void TranslationServer::setup() {
+ String test = GLOBAL_DEF("internationalization/locale/test", "");
+ test = test.strip_edges();
+ if (!test.is_empty()) {
+ set_locale(test);
+ } else {
+ set_locale(OS::get_singleton()->get_locale());
+ }
+
+ fallback = GLOBAL_DEF("internationalization/locale/fallback", "en");
+ pseudolocalization_enabled = GLOBAL_DEF("internationalization/pseudolocalization/use_pseudolocalization", false);
+ pseudolocalization_accents_enabled = GLOBAL_DEF("internationalization/pseudolocalization/replace_with_accents", true);
+ pseudolocalization_double_vowels_enabled = GLOBAL_DEF("internationalization/pseudolocalization/double_vowels", false);
+ pseudolocalization_fake_bidi_enabled = GLOBAL_DEF("internationalization/pseudolocalization/fake_bidi", false);
+ pseudolocalization_override_enabled = GLOBAL_DEF("internationalization/pseudolocalization/override", false);
+ expansion_ratio = GLOBAL_DEF("internationalization/pseudolocalization/expansion_ratio", 0.0);
+ pseudolocalization_prefix = GLOBAL_DEF("internationalization/pseudolocalization/prefix", "[");
+ pseudolocalization_suffix = GLOBAL_DEF("internationalization/pseudolocalization/suffix", "]");
+ pseudolocalization_skip_placeholders_enabled = GLOBAL_DEF("internationalization/pseudolocalization/skip_placeholders", true);
+
+#ifdef TOOLS_ENABLED
+ ProjectSettings::get_singleton()->set_custom_property_info(PropertyInfo(Variant::STRING, "internationalization/locale/fallback", PROPERTY_HINT_LOCALE_ID, ""));
+#endif
+}
+
+void TranslationServer::set_tool_translation(const Ref<Translation> &p_translation) {
+ tool_translation = p_translation;
+}
+
+Ref<Translation> TranslationServer::get_tool_translation() const {
+ return tool_translation;
+}
+
+String TranslationServer::get_tool_locale() {
+#ifdef TOOLS_ENABLED
+ if (Engine::get_singleton()->is_editor_hint() || Engine::get_singleton()->is_project_manager_hint()) {
+ if (TranslationServer::get_singleton()->get_tool_translation().is_valid()) {
+ return tool_translation->get_locale();
+ } else {
+ return "en";
+ }
+ } else {
+#else
+ {
+#endif
+ // Look for best matching loaded translation.
+ String best_locale = "en";
+ int best_score = 0;
+
+ for (const Ref<Translation> &E : translations) {
+ const Ref<Translation> &t = E;
+ ERR_FAIL_COND_V(t.is_null(), best_locale);
+ String l = t->get_locale();
+
+ int score = compare_locales(locale, l);
+ if (score > 0 && score >= best_score) {
+ best_locale = l;
+ best_score = score;
+ if (score == 10) {
+ break; // Exact match, skip the rest.
+ }
+ }
+ }
+ return best_locale;
+ }
+}
+
+StringName TranslationServer::tool_translate(const StringName &p_message, const StringName &p_context) const {
+ if (tool_translation.is_valid()) {
+ StringName r = tool_translation->get_message(p_message, p_context);
+ if (r) {
+ return r;
+ }
+ }
+ return p_message;
+}
+
+StringName TranslationServer::tool_translate_plural(const StringName &p_message, const StringName &p_message_plural, int p_n, const StringName &p_context) const {
+ if (tool_translation.is_valid()) {
+ StringName r = tool_translation->get_plural_message(p_message, p_message_plural, p_n, p_context);
+ if (r) {
+ return r;
+ }
+ }
+
+ if (p_n == 1) {
+ return p_message;
+ }
+ return p_message_plural;
+}
+
+void TranslationServer::set_property_translation(const Ref<Translation> &p_translation) {
+ property_translation = p_translation;
+}
+
+StringName TranslationServer::property_translate(const StringName &p_message, const StringName &p_context) const {
+ if (property_translation.is_valid()) {
+ StringName r = property_translation->get_message(p_message, p_context);
+ if (r) {
+ return r;
+ }
+ }
+ return p_message;
+}
+
+void TranslationServer::set_doc_translation(const Ref<Translation> &p_translation) {
+ doc_translation = p_translation;
+}
+
+StringName TranslationServer::doc_translate(const StringName &p_message, const StringName &p_context) const {
+ if (doc_translation.is_valid()) {
+ StringName r = doc_translation->get_message(p_message, p_context);
+ if (r) {
+ return r;
+ }
+ }
+ return p_message;
+}
+
+StringName TranslationServer::doc_translate_plural(const StringName &p_message, const StringName &p_message_plural, int p_n, const StringName &p_context) const {
+ if (doc_translation.is_valid()) {
+ StringName r = doc_translation->get_plural_message(p_message, p_message_plural, p_n, p_context);
+ if (r) {
+ return r;
+ }
+ }
+
+ if (p_n == 1) {
+ return p_message;
+ }
+ return p_message_plural;
+}
+
+void TranslationServer::set_extractable_translation(const Ref<Translation> &p_translation) {
+ extractable_translation = p_translation;
+}
+
+StringName TranslationServer::extractable_translate(const StringName &p_message, const StringName &p_context) const {
+ if (extractable_translation.is_valid()) {
+ StringName r = extractable_translation->get_message(p_message, p_context);
+ if (r) {
+ return r;
+ }
+ }
+ return p_message;
+}
+
+StringName TranslationServer::extractable_translate_plural(const StringName &p_message, const StringName &p_message_plural, int p_n, const StringName &p_context) const {
+ if (extractable_translation.is_valid()) {
+ StringName r = extractable_translation->get_plural_message(p_message, p_message_plural, p_n, p_context);
+ if (r) {
+ return r;
+ }
+ }
+
+ if (p_n == 1) {
+ return p_message;
+ }
+ return p_message_plural;
+}
+
+bool TranslationServer::is_pseudolocalization_enabled() const {
+ return pseudolocalization_enabled;
+}
+
+void TranslationServer::set_pseudolocalization_enabled(bool p_enabled) {
+ pseudolocalization_enabled = p_enabled;
+
+ ResourceLoader::reload_translation_remaps();
+
+ if (OS::get_singleton()->get_main_loop()) {
+ OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_TRANSLATION_CHANGED);
+ }
+}
+
+void TranslationServer::reload_pseudolocalization() {
+ pseudolocalization_accents_enabled = GLOBAL_GET("internationalization/pseudolocalization/replace_with_accents");
+ pseudolocalization_double_vowels_enabled = GLOBAL_GET("internationalization/pseudolocalization/double_vowels");
+ pseudolocalization_fake_bidi_enabled = GLOBAL_GET("internationalization/pseudolocalization/fake_bidi");
+ pseudolocalization_override_enabled = GLOBAL_GET("internationalization/pseudolocalization/override");
+ expansion_ratio = GLOBAL_GET("internationalization/pseudolocalization/expansion_ratio");
+ pseudolocalization_prefix = GLOBAL_GET("internationalization/pseudolocalization/prefix");
+ pseudolocalization_suffix = GLOBAL_GET("internationalization/pseudolocalization/suffix");
+ pseudolocalization_skip_placeholders_enabled = GLOBAL_GET("internationalization/pseudolocalization/skip_placeholders");
+
+ ResourceLoader::reload_translation_remaps();
+
+ if (OS::get_singleton()->get_main_loop()) {
+ OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_TRANSLATION_CHANGED);
+ }
+}
+
+StringName TranslationServer::pseudolocalize(const StringName &p_message) const {
+ String message = p_message;
+ int length = message.length();
+ if (pseudolocalization_override_enabled) {
+ message = get_override_string(message);
+ }
+
+ if (pseudolocalization_double_vowels_enabled) {
+ message = double_vowels(message);
+ }
+
+ if (pseudolocalization_accents_enabled) {
+ message = replace_with_accented_string(message);
+ }
+
+ if (pseudolocalization_fake_bidi_enabled) {
+ message = wrap_with_fakebidi_characters(message);
+ }
+
+ StringName res = add_padding(message, length);
+ return res;
+}
+
+StringName TranslationServer::tool_pseudolocalize(const StringName &p_message) const {
+ String message = p_message;
+ message = double_vowels(message);
+ message = replace_with_accented_string(message);
+ StringName res = "[!!! " + message + " !!!]";
+ return res;
+}
+
+String TranslationServer::get_override_string(String &p_message) const {
+ String res;
+ for (int i = 0; i < p_message.length(); i++) {
+ if (pseudolocalization_skip_placeholders_enabled && is_placeholder(p_message, i)) {
+ res += p_message[i];
+ res += p_message[i + 1];
+ i++;
+ continue;
+ }
+ res += '*';
+ }
+ return res;
+}
+
+String TranslationServer::double_vowels(String &p_message) const {
+ String res;
+ for (int i = 0; i < p_message.length(); i++) {
+ if (pseudolocalization_skip_placeholders_enabled && is_placeholder(p_message, i)) {
+ res += p_message[i];
+ res += p_message[i + 1];
+ i++;
+ continue;
+ }
+ res += p_message[i];
+ if (p_message[i] == 'a' || p_message[i] == 'e' || p_message[i] == 'i' || p_message[i] == 'o' || p_message[i] == 'u' ||
+ p_message[i] == 'A' || p_message[i] == 'E' || p_message[i] == 'I' || p_message[i] == 'O' || p_message[i] == 'U') {
+ res += p_message[i];
+ }
+ }
+ return res;
+};
+
+String TranslationServer::replace_with_accented_string(String &p_message) const {
+ String res;
+ for (int i = 0; i < p_message.length(); i++) {
+ if (pseudolocalization_skip_placeholders_enabled && is_placeholder(p_message, i)) {
+ res += p_message[i];
+ res += p_message[i + 1];
+ i++;
+ continue;
+ }
+ const char32_t *accented = get_accented_version(p_message[i]);
+ if (accented) {
+ res += accented;
+ } else {
+ res += p_message[i];
+ }
+ }
+ return res;
+}
+
+String TranslationServer::wrap_with_fakebidi_characters(String &p_message) const {
+ String res;
+ char32_t fakebidiprefix = U'\u202e';
+ char32_t fakebidisuffix = U'\u202c';
+ res += fakebidiprefix;
+ // The fake bidi unicode gets popped at every newline so pushing it back at every newline.
+ for (int i = 0; i < p_message.length(); i++) {
+ if (p_message[i] == '\n') {
+ res += fakebidisuffix;
+ res += p_message[i];
+ res += fakebidiprefix;
+ } else if (pseudolocalization_skip_placeholders_enabled && is_placeholder(p_message, i)) {
+ res += fakebidisuffix;
+ res += p_message[i];
+ res += p_message[i + 1];
+ res += fakebidiprefix;
+ i++;
+ } else {
+ res += p_message[i];
+ }
+ }
+ res += fakebidisuffix;
+ return res;
+}
+
+String TranslationServer::add_padding(const String &p_message, int p_length) const {
+ String underscores = String("_").repeat(p_length * expansion_ratio / 2);
+ String prefix = pseudolocalization_prefix + underscores;
+ String suffix = underscores + pseudolocalization_suffix;
+
+ return prefix + p_message + suffix;
+}
+
+const char32_t *TranslationServer::get_accented_version(char32_t p_character) const {
+ if (!is_ascii_alphabet_char(p_character)) {
+ return nullptr;
+ }
+
+ for (unsigned int i = 0; i < sizeof(_character_to_accented) / sizeof(_character_to_accented[0]); i++) {
+ if (_character_to_accented[i].character == p_character) {
+ return _character_to_accented[i].accented_character;
+ }
+ }
+
+ return nullptr;
+}
+
+bool TranslationServer::is_placeholder(String &p_message, int p_index) const {
+ return p_index < p_message.length() - 1 && p_message[p_index] == '%' &&
+ (p_message[p_index + 1] == 's' || p_message[p_index + 1] == 'c' || p_message[p_index + 1] == 'd' ||
+ p_message[p_index + 1] == 'o' || p_message[p_index + 1] == 'x' || p_message[p_index + 1] == 'X' || p_message[p_index + 1] == 'f');
+}
+
+#ifdef TOOLS_ENABLED
+void TranslationServer::get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const {
+ const String pf = p_function;
+ if (p_idx == 0) {
+ HashMap<String, String> *target_hash_map = nullptr;
+ if (pf == "get_language_name") {
+ target_hash_map = &language_map;
+ } else if (pf == "get_script_name") {
+ target_hash_map = &script_map;
+ } else if (pf == "get_country_name") {
+ target_hash_map = &country_name_map;
+ }
+
+ if (target_hash_map) {
+ for (const KeyValue<String, String> &E : *target_hash_map) {
+ r_options->push_back(E.key.quote());
+ }
+ }
+ }
+ Object::get_argument_options(p_function, p_idx, r_options);
+}
+#endif // TOOLS_ENABLED
+
+void TranslationServer::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("set_locale", "locale"), &TranslationServer::set_locale);
+ ClassDB::bind_method(D_METHOD("get_locale"), &TranslationServer::get_locale);
+ ClassDB::bind_method(D_METHOD("get_tool_locale"), &TranslationServer::get_tool_locale);
+
+ ClassDB::bind_method(D_METHOD("compare_locales", "locale_a", "locale_b"), &TranslationServer::compare_locales);
+ ClassDB::bind_method(D_METHOD("standardize_locale", "locale"), &TranslationServer::standardize_locale);
+
+ ClassDB::bind_method(D_METHOD("get_all_languages"), &TranslationServer::get_all_languages);
+ ClassDB::bind_method(D_METHOD("get_language_name", "language"), &TranslationServer::get_language_name);
+
+ ClassDB::bind_method(D_METHOD("get_all_scripts"), &TranslationServer::get_all_scripts);
+ ClassDB::bind_method(D_METHOD("get_script_name", "script"), &TranslationServer::get_script_name);
+
+ ClassDB::bind_method(D_METHOD("get_all_countries"), &TranslationServer::get_all_countries);
+ ClassDB::bind_method(D_METHOD("get_country_name", "country"), &TranslationServer::get_country_name);
+
+ ClassDB::bind_method(D_METHOD("get_locale_name", "locale"), &TranslationServer::get_locale_name);
+
+ ClassDB::bind_method(D_METHOD("translate", "message", "context"), &TranslationServer::translate, DEFVAL(StringName()));
+ ClassDB::bind_method(D_METHOD("translate_plural", "message", "plural_message", "n", "context"), &TranslationServer::translate_plural, DEFVAL(StringName()));
+
+ ClassDB::bind_method(D_METHOD("add_translation", "translation"), &TranslationServer::add_translation);
+ ClassDB::bind_method(D_METHOD("remove_translation", "translation"), &TranslationServer::remove_translation);
+ ClassDB::bind_method(D_METHOD("get_translation_object", "locale"), &TranslationServer::get_translation_object);
+
+ ClassDB::bind_method(D_METHOD("clear"), &TranslationServer::clear);
+
+ ClassDB::bind_method(D_METHOD("get_loaded_locales"), &TranslationServer::get_loaded_locales);
+
+ ClassDB::bind_method(D_METHOD("is_pseudolocalization_enabled"), &TranslationServer::is_pseudolocalization_enabled);
+ ClassDB::bind_method(D_METHOD("set_pseudolocalization_enabled", "enabled"), &TranslationServer::set_pseudolocalization_enabled);
+ ClassDB::bind_method(D_METHOD("reload_pseudolocalization"), &TranslationServer::reload_pseudolocalization);
+ ClassDB::bind_method(D_METHOD("pseudolocalize", "message"), &TranslationServer::pseudolocalize);
+ ADD_PROPERTY(PropertyInfo(Variant::Type::BOOL, "pseudolocalization_enabled"), "set_pseudolocalization_enabled", "is_pseudolocalization_enabled");
+}
+
+void TranslationServer::load_translations() {
+ _load_translations("internationalization/locale/translations"); //all
+ _load_translations("internationalization/locale/translations_" + locale.substr(0, 2));
+
+ if (locale.substr(0, 2) != locale) {
+ _load_translations("internationalization/locale/translations_" + locale);
+ }
+}
+
+TranslationServer::TranslationServer() {
+ singleton = this;
+ init_locale_info();
+}
diff --git a/core/string/translation_server.h b/core/string/translation_server.h
new file mode 100644
index 0000000000..ebe81d9712
--- /dev/null
+++ b/core/string/translation_server.h
@@ -0,0 +1,164 @@
+/**************************************************************************/
+/* translation_server.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* 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 TRANSLATION_SERVER_H
+#define TRANSLATION_SERVER_H
+
+#include "core/string/translation.h"
+
+class TranslationServer : public Object {
+ GDCLASS(TranslationServer, Object);
+
+ String locale = "en";
+ String fallback;
+
+ HashSet<Ref<Translation>> translations;
+ Ref<Translation> tool_translation;
+ Ref<Translation> property_translation;
+ Ref<Translation> doc_translation;
+ Ref<Translation> extractable_translation;
+
+ bool enabled = true;
+
+ bool pseudolocalization_enabled = false;
+ bool pseudolocalization_accents_enabled = false;
+ bool pseudolocalization_double_vowels_enabled = false;
+ bool pseudolocalization_fake_bidi_enabled = false;
+ bool pseudolocalization_override_enabled = false;
+ bool pseudolocalization_skip_placeholders_enabled = false;
+ float expansion_ratio = 0.0;
+ String pseudolocalization_prefix;
+ String pseudolocalization_suffix;
+
+ StringName tool_pseudolocalize(const StringName &p_message) const;
+ String get_override_string(String &p_message) const;
+ String double_vowels(String &p_message) const;
+ String replace_with_accented_string(String &p_message) const;
+ String wrap_with_fakebidi_characters(String &p_message) const;
+ String add_padding(const String &p_message, int p_length) const;
+ const char32_t *get_accented_version(char32_t p_character) const;
+ bool is_placeholder(String &p_message, int p_index) const;
+
+ static TranslationServer *singleton;
+ bool _load_translations(const String &p_from);
+ String _standardize_locale(const String &p_locale, bool p_add_defaults) const;
+
+ StringName _get_message_from_translations(const StringName &p_message, const StringName &p_context, const String &p_locale, bool plural, const String &p_message_plural = "", int p_n = 0) const;
+
+ static void _bind_methods();
+
+#ifndef DISABLE_DEPRECATED
+ static void _bind_compatibility_methods();
+#endif
+
+ struct LocaleScriptInfo {
+ String name;
+ String script;
+ String default_country;
+ HashSet<String> supported_countries;
+ };
+ static Vector<LocaleScriptInfo> locale_script_info;
+
+ static HashMap<String, String> language_map;
+ static HashMap<String, String> script_map;
+ static HashMap<String, String> locale_rename_map;
+ static HashMap<String, String> country_name_map;
+ static HashMap<String, String> country_rename_map;
+ static HashMap<String, String> variant_map;
+
+ void init_locale_info();
+
+public:
+ _FORCE_INLINE_ static TranslationServer *get_singleton() { return singleton; }
+
+ void set_enabled(bool p_enabled) { enabled = p_enabled; }
+ _FORCE_INLINE_ bool is_enabled() const { return enabled; }
+
+ void set_locale(const String &p_locale);
+ String get_locale() const;
+ Ref<Translation> get_translation_object(const String &p_locale);
+
+ Vector<String> get_all_languages() const;
+ String get_language_name(const String &p_language) const;
+
+ Vector<String> get_all_scripts() const;
+ String get_script_name(const String &p_script) const;
+
+ Vector<String> get_all_countries() const;
+ String get_country_name(const String &p_country) const;
+
+ String get_locale_name(const String &p_locale) const;
+
+ PackedStringArray get_loaded_locales() const;
+
+ void add_translation(const Ref<Translation> &p_translation);
+ void remove_translation(const Ref<Translation> &p_translation);
+
+ StringName translate(const StringName &p_message, const StringName &p_context = "") const;
+ StringName translate_plural(const StringName &p_message, const StringName &p_message_plural, int p_n, const StringName &p_context = "") const;
+
+ StringName pseudolocalize(const StringName &p_message) const;
+
+ bool is_pseudolocalization_enabled() const;
+ void set_pseudolocalization_enabled(bool p_enabled);
+ void reload_pseudolocalization();
+
+ String standardize_locale(const String &p_locale) const;
+
+ int compare_locales(const String &p_locale_a, const String &p_locale_b) const;
+
+ String get_tool_locale();
+ void set_tool_translation(const Ref<Translation> &p_translation);
+ Ref<Translation> get_tool_translation() const;
+ StringName tool_translate(const StringName &p_message, const StringName &p_context = "") const;
+ StringName tool_translate_plural(const StringName &p_message, const StringName &p_message_plural, int p_n, const StringName &p_context = "") const;
+ void set_property_translation(const Ref<Translation> &p_translation);
+ StringName property_translate(const StringName &p_message, const StringName &p_context = "") const;
+ void set_doc_translation(const Ref<Translation> &p_translation);
+ StringName doc_translate(const StringName &p_message, const StringName &p_context = "") const;
+ StringName doc_translate_plural(const StringName &p_message, const StringName &p_message_plural, int p_n, const StringName &p_context = "") const;
+ void set_extractable_translation(const Ref<Translation> &p_translation);
+ StringName extractable_translate(const StringName &p_message, const StringName &p_context = "") const;
+ StringName extractable_translate_plural(const StringName &p_message, const StringName &p_message_plural, int p_n, const StringName &p_context = "") const;
+
+ void setup();
+
+ void clear();
+
+ void load_translations();
+
+#ifdef TOOLS_ENABLED
+ virtual void get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const override;
+#endif // TOOLS_ENABLED
+
+ TranslationServer();
+};
+
+#endif // TRANSLATION_SERVER_H
diff --git a/core/string/ustring.cpp b/core/string/ustring.cpp
index cf19a1d48f..2cfc48d395 100644
--- a/core/string/ustring.cpp
+++ b/core/string/ustring.cpp
@@ -36,7 +36,7 @@
#include "core/os/memory.h"
#include "core/string/print_string.h"
#include "core/string/string_name.h"
-#include "core/string/translation.h"
+#include "core/string/translation_server.h"
#include "core/string/ucaps.h"
#include "core/variant/variant.h"
#include "core/version_generated.gen.h"
@@ -1537,13 +1537,16 @@ Vector<double> String::split_floats(const String &p_splitter, bool p_allow_empty
int from = 0;
int len = length();
+ String buffer = *this;
while (true) {
int end = find(p_splitter, from);
if (end < 0) {
end = len;
}
if (p_allow_empty || (end > from)) {
- ret.push_back(String::to_float(&get_data()[from]));
+ buffer[end] = 0;
+ ret.push_back(String::to_float(&buffer.get_data()[from]));
+ buffer[end] = _cowdata.get(end);
}
if (end == len) {
@@ -1561,6 +1564,7 @@ Vector<float> String::split_floats_mk(const Vector<String> &p_splitters, bool p_
int from = 0;
int len = length();
+ String buffer = *this;
while (true) {
int idx;
int end = findmk(p_splitters, from, &idx);
@@ -1572,7 +1576,9 @@ Vector<float> String::split_floats_mk(const Vector<String> &p_splitters, bool p_
}
if (p_allow_empty || (end > from)) {
- ret.push_back(String::to_float(&get_data()[from]));
+ buffer[end] = 0;
+ ret.push_back(String::to_float(&buffer.get_data()[from]));
+ buffer[end] = _cowdata.get(end);
}
if (end == len) {
@@ -4026,54 +4032,161 @@ String String::format(const Variant &values, const String &placeholder) const {
return new_string;
}
-String String::replace(const String &p_key, const String &p_with) const {
- String new_string;
+static String _replace_common(const String &p_this, const String &p_key, const String &p_with, bool p_case_insensitive) {
+ if (p_key.is_empty() || p_this.is_empty()) {
+ return p_this;
+ }
+
+ const int key_length = p_key.length();
+
int search_from = 0;
int result = 0;
- while ((result = find(p_key, search_from)) >= 0) {
- new_string += substr(search_from, result - search_from);
- new_string += p_with;
- search_from = result + p_key.length();
+ LocalVector<int> found;
+
+ while ((result = (p_case_insensitive ? p_this.findn(p_key, search_from) : p_this.find(p_key, search_from))) >= 0) {
+ found.push_back(result);
+ search_from = result + key_length;
}
- if (search_from == 0) {
- return *this;
+ if (found.is_empty()) {
+ return p_this;
+ }
+
+ String new_string;
+
+ const int with_length = p_with.length();
+ const int old_length = p_this.length();
+
+ new_string.resize(old_length + found.size() * (with_length - key_length) + 1);
+
+ char32_t *new_ptrw = new_string.ptrw();
+ const char32_t *old_ptr = p_this.ptr();
+ const char32_t *with_ptr = p_with.ptr();
+
+ int last_pos = 0;
+
+ for (const int &pos : found) {
+ if (last_pos != pos) {
+ memcpy(new_ptrw, old_ptr + last_pos, (pos - last_pos) * sizeof(char32_t));
+ new_ptrw += (pos - last_pos);
+ }
+ if (with_length) {
+ memcpy(new_ptrw, with_ptr, with_length * sizeof(char32_t));
+ new_ptrw += with_length;
+ }
+ last_pos = pos + key_length;
}
- new_string += substr(search_from, length() - search_from);
+ if (last_pos != old_length) {
+ memcpy(new_ptrw, old_ptr + last_pos, (old_length - last_pos) * sizeof(char32_t));
+ new_ptrw += old_length - last_pos;
+ }
+
+ *new_ptrw = 0;
return new_string;
}
-String String::replace(const char *p_key, const char *p_with) const {
- String new_string;
+static String _replace_common(const String &p_this, char const *p_key, char const *p_with, bool p_case_insensitive) {
+ int key_length = strlen(p_key);
+
+ if (key_length == 0 || p_this.is_empty()) {
+ return p_this;
+ }
+
int search_from = 0;
int result = 0;
- while ((result = find(p_key, search_from)) >= 0) {
- new_string += substr(search_from, result - search_from);
- new_string += p_with;
- int k = 0;
- while (p_key[k] != '\0') {
- k++;
+ LocalVector<int> found;
+
+ while ((result = (p_case_insensitive ? p_this.findn(p_key, search_from) : p_this.find(p_key, search_from))) >= 0) {
+ found.push_back(result);
+ search_from = result + key_length;
+ }
+
+ if (found.is_empty()) {
+ return p_this;
+ }
+
+ String new_string;
+
+ // Create string to speed up copying as we can't do `memcopy` between `char32_t` and `char`.
+ const String with_string(p_with);
+ const int with_length = with_string.length();
+ const int old_length = p_this.length();
+
+ new_string.resize(old_length + found.size() * (with_length - key_length) + 1);
+
+ char32_t *new_ptrw = new_string.ptrw();
+ const char32_t *old_ptr = p_this.ptr();
+ const char32_t *with_ptr = with_string.ptr();
+
+ int last_pos = 0;
+
+ for (const int &pos : found) {
+ if (last_pos != pos) {
+ memcpy(new_ptrw, old_ptr + last_pos, (pos - last_pos) * sizeof(char32_t));
+ new_ptrw += (pos - last_pos);
+ }
+ if (with_length) {
+ memcpy(new_ptrw, with_ptr, with_length * sizeof(char32_t));
+ new_ptrw += with_length;
}
- search_from = result + k;
+ last_pos = pos + key_length;
}
- if (search_from == 0) {
- return *this;
+ if (last_pos != old_length) {
+ memcpy(new_ptrw, old_ptr + last_pos, (old_length - last_pos) * sizeof(char32_t));
+ new_ptrw += old_length - last_pos;
}
- new_string += substr(search_from, length() - search_from);
+ *new_ptrw = 0;
return new_string;
}
+String String::replace(const String &p_key, const String &p_with) const {
+ return _replace_common(*this, p_key, p_with, false);
+}
+
+String String::replace(const char *p_key, const char *p_with) const {
+ return _replace_common(*this, p_key, p_with, false);
+}
+
String String::replace_first(const String &p_key, const String &p_with) const {
int pos = find(p_key);
if (pos >= 0) {
- return substr(0, pos) + p_with + substr(pos + p_key.length(), length());
+ const int old_length = length();
+ const int key_length = p_key.length();
+ const int with_length = p_with.length();
+
+ String new_string;
+ new_string.resize(old_length + (with_length - key_length) + 1);
+
+ char32_t *new_ptrw = new_string.ptrw();
+ const char32_t *old_ptr = ptr();
+ const char32_t *with_ptr = p_with.ptr();
+
+ if (pos > 0) {
+ memcpy(new_ptrw, old_ptr, pos * sizeof(char32_t));
+ new_ptrw += pos;
+ }
+
+ if (with_length) {
+ memcpy(new_ptrw, with_ptr, with_length * sizeof(char32_t));
+ new_ptrw += with_length;
+ }
+ pos += key_length;
+
+ if (pos != old_length) {
+ memcpy(new_ptrw, old_ptr + pos, (old_length - pos) * sizeof(char32_t));
+ new_ptrw += (old_length - pos);
+ }
+
+ *new_ptrw = 0;
+
+ return new_string;
}
return *this;
@@ -4082,55 +4195,45 @@ String String::replace_first(const String &p_key, const String &p_with) const {
String String::replace_first(const char *p_key, const char *p_with) const {
int pos = find(p_key);
if (pos >= 0) {
- int substring_length = strlen(p_key);
- return substr(0, pos) + p_with + substr(pos + substring_length, length());
- }
+ const int old_length = length();
+ const int key_length = strlen(p_key);
+ const int with_length = strlen(p_with);
- return *this;
-}
-
-String String::replacen(const String &p_key, const String &p_with) const {
- String new_string;
- int search_from = 0;
- int result = 0;
+ String new_string;
+ new_string.resize(old_length + (with_length - key_length) + 1);
- while ((result = findn(p_key, search_from)) >= 0) {
- new_string += substr(search_from, result - search_from);
- new_string += p_with;
- search_from = result + p_key.length();
- }
+ char32_t *new_ptrw = new_string.ptrw();
+ const char32_t *old_ptr = ptr();
- if (search_from == 0) {
- return *this;
- }
+ if (pos > 0) {
+ memcpy(new_ptrw, old_ptr, pos * sizeof(char32_t));
+ new_ptrw += pos;
+ }
- new_string += substr(search_from, length() - search_from);
- return new_string;
-}
+ for (int i = 0; i < with_length; ++i) {
+ *new_ptrw++ = p_with[i];
+ }
+ pos += key_length;
-String String::replacen(const char *p_key, const char *p_with) const {
- String new_string;
- int search_from = 0;
- int result = 0;
- int substring_length = strlen(p_key);
+ if (pos != old_length) {
+ memcpy(new_ptrw, old_ptr + pos, (old_length - pos) * sizeof(char32_t));
+ new_ptrw += (old_length - pos);
+ }
- if (substring_length == 0) {
- return *this; // there's nothing to match or substitute
- }
+ *new_ptrw = 0;
- while ((result = findn(p_key, search_from)) >= 0) {
- new_string += substr(search_from, result - search_from);
- new_string += p_with;
- search_from = result + substring_length;
+ return new_string;
}
- if (search_from == 0) {
- return *this;
- }
+ return *this;
+}
- new_string += substr(search_from, length() - search_from);
+String String::replacen(const String &p_key, const String &p_with) const {
+ return _replace_common(*this, p_key, p_with, true);
+}
- return new_string;
+String String::replacen(const char *p_key, const char *p_with) const {
+ return _replace_common(*this, p_key, p_with, true);
}
String String::repeat(int p_count) const {
@@ -4424,10 +4527,7 @@ String String::simplify_path() const {
dirs.remove_at(i);
i--;
} else if (d == "..") {
- if (i == 0) {
- dirs.remove_at(i);
- i--;
- } else {
+ if (i != 0) {
dirs.remove_at(i);
dirs.remove_at(i - 1);
i -= 2;
diff --git a/doc/classes/AnimationNodeBlendSpace2D.xml b/doc/classes/AnimationNodeBlendSpace2D.xml
index 596767599c..da79e15be1 100644
--- a/doc/classes/AnimationNodeBlendSpace2D.xml
+++ b/doc/classes/AnimationNodeBlendSpace2D.xml
@@ -5,7 +5,7 @@
</brief_description>
<description>
A resource used by [AnimationNodeBlendTree].
- [AnimationNodeBlendSpace1D] represents a virtual 2D space on which [AnimationRootNode]s are placed. Outputs the linear blend of the three adjacent animations using a [Vector2] weight. Adjacent in this context means the three [AnimationRootNode]s making up the triangle that contains the current value.
+ [AnimationNodeBlendSpace2D] represents a virtual 2D space on which [AnimationRootNode]s are placed. Outputs the linear blend of the three adjacent animations using a [Vector2] weight. Adjacent in this context means the three [AnimationRootNode]s making up the triangle that contains the current value.
You can add vertices to the blend space with [method add_blend_point] and automatically triangulate it by setting [member auto_triangles] to [code]true[/code]. Otherwise, use [method add_triangle] and [method remove_triangle] to triangulate the blend space by hand.
</description>
<tutorials>
@@ -93,7 +93,7 @@
<param index="0" name="point" type="int" />
<param index="1" name="pos" type="Vector2" />
<description>
- Updates the position of the point at index [param point] on the blend axis.
+ Updates the position of the point at index [param point] in the blend space.
</description>
</method>
</methods>
diff --git a/doc/classes/ClassDB.xml b/doc/classes/ClassDB.xml
index 59ed0b8fe7..66b67d1a59 100644
--- a/doc/classes/ClassDB.xml
+++ b/doc/classes/ClassDB.xml
@@ -99,6 +99,14 @@
Returns the default value of [param property] of [param class] or its ancestor classes.
</description>
</method>
+ <method name="class_get_property_getter">
+ <return type="StringName" />
+ <param index="0" name="class" type="StringName" />
+ <param index="1" name="property" type="StringName" />
+ <description>
+ Returns the getter method name of [param property] of [param class].
+ </description>
+ </method>
<method name="class_get_property_list" qualifiers="const">
<return type="Dictionary[]" />
<param index="0" name="class" type="StringName" />
@@ -107,6 +115,14 @@
Returns an array with all the properties of [param class] or its ancestry if [param no_inheritance] is [code]false[/code].
</description>
</method>
+ <method name="class_get_property_setter">
+ <return type="StringName" />
+ <param index="0" name="class" type="StringName" />
+ <param index="1" name="property" type="StringName" />
+ <description>
+ Returns the setter method name of [param property] of [param class].
+ </description>
+ </method>
<method name="class_get_signal" qualifiers="const">
<return type="Dictionary" />
<param index="0" name="class" type="StringName" />
diff --git a/doc/classes/Compositor.xml b/doc/classes/Compositor.xml
index 7605083319..0732526957 100644
--- a/doc/classes/Compositor.xml
+++ b/doc/classes/Compositor.xml
@@ -7,6 +7,7 @@
The compositor resource stores attributes used to customize how a [Viewport] is rendered.
</description>
<tutorials>
+ <link title="The Compositor">$DOCS_URL/tutorials/rendering/compositor.html</link>
</tutorials>
<members>
<member name="compositor_effects" type="CompositorEffect[]" setter="set_compositor_effects" getter="get_compositor_effects" default="[]">
diff --git a/doc/classes/CompositorEffect.xml b/doc/classes/CompositorEffect.xml
index cf3aa9fca4..76a3887918 100644
--- a/doc/classes/CompositorEffect.xml
+++ b/doc/classes/CompositorEffect.xml
@@ -7,6 +7,7 @@
This resource defines a custom rendering effect that can be applied to [Viewport]s through the viewports' [Environment]. You can implement a callback that is called during rendering at a given stage of the rendering pipeline and allows you to insert additional passes. Note that this callback happens on the rendering thread. CompositorEffect is an abstract base class and must be extended to implement specific rendering logic.
</description>
<tutorials>
+ <link title="The Compositor">$DOCS_URL/tutorials/rendering/compositor.html</link>
</tutorials>
<methods>
<method name="_render_callback" qualifiers="virtual">
diff --git a/doc/classes/EditorSettings.xml b/doc/classes/EditorSettings.xml
index 4d82a32dae..320b119b6a 100644
--- a/doc/classes/EditorSettings.xml
+++ b/doc/classes/EditorSettings.xml
@@ -311,13 +311,13 @@
The grid size in units. Higher values prevent the grid from appearing "cut off" at certain angles, but make the grid more demanding to render. Depending on the camera's position, the grid may not be fully visible since a shader is used to fade it progressively.
</member>
<member name="editors/3d/grid_xy_plane" type="bool" setter="" getter="">
- If [code]true[/code], render the grid on an XY plane. This can be useful for 3D side-scrolling games.
+ If [code]true[/code], renders the grid on the XY plane in perspective view. This can be useful for 3D side-scrolling games.
</member>
<member name="editors/3d/grid_xz_plane" type="bool" setter="" getter="">
- If [code]true[/code], render the grid on an XZ plane.
+ If [code]true[/code], renders the grid on the XZ plane in perspective view.
</member>
<member name="editors/3d/grid_yz_plane" type="bool" setter="" getter="">
- If [code]true[/code], render the grid on a YZ plane. This can be useful for 3D side-scrolling games.
+ If [code]true[/code], renders the grid on the YZ plane in perspective view. This can be useful for 3D side-scrolling games.
</member>
<member name="editors/3d/navigation/emulate_3_button_mouse" type="bool" setter="" getter="">
If [code]true[/code], enables 3-button mouse emulation mode. This is useful on laptops when using a trackpad.
@@ -656,6 +656,9 @@
Expanding main editor window content to the title, if supported by [DisplayServer]. See [constant DisplayServer.WINDOW_FLAG_EXTEND_TO_TITLE].
Specific to the macOS platform.
</member>
+ <member name="interface/editor/font_allow_msdf" type="bool" setter="" getter="">
+ If set to [code]true[/code], MSDF font rendering will be used for the visual shader graph editor. You may need to set this to [code]false[/code] when using a custom main font, as some fonts will look broken due to the use of self-intersecting outlines in their font data. Downloading the font from the font maker's official website as opposed to a service like Google Fonts can help resolve this issue.
+ </member>
<member name="interface/editor/font_antialiasing" type="int" setter="" getter="">
FreeType's font anti-aliasing mode used to render the editor fonts. Most fonts are not designed to look good with anti-aliasing disabled, so it's recommended to leave this enabled unless you're using a pixel art font.
</member>
diff --git a/doc/classes/Geometry2D.xml b/doc/classes/Geometry2D.xml
index f21696d02c..2dd76ad989 100644
--- a/doc/classes/Geometry2D.xml
+++ b/doc/classes/Geometry2D.xml
@@ -126,8 +126,34 @@
<param index="2" name="from_b" type="Vector2" />
<param index="3" name="dir_b" type="Vector2" />
<description>
- Checks if the two lines ([param from_a], [param dir_a]) and ([param from_b], [param dir_b]) intersect. If yes, return the point of intersection as [Vector2]. If no intersection takes place, returns [code]null[/code].
- [b]Note:[/b] The lines are specified using direction vectors, not end points.
+ Returns the point of intersection between the two lines ([param from_a], [param dir_a]) and ([param from_b], [param dir_b]). Returns a [Vector2], or [code]null[/code] if the lines are parallel.
+ [code]from[/code] and [code]dir[/code] are [i]not[/i] endpoints of a line segment or ray but the slope ([code]dir[/code]) and a known point ([code]from[/code]) on that line.
+ [codeblocks]
+ [gdscript]
+ var from_a = Vector2.ZERO
+ var dir_a = Vector2.RIGHT
+ var from_b = Vector2.DOWN
+
+ # Returns Vector2(1, 0)
+ Geometry2D.line_intersects_line(from_a, dir_a, from_b, Vector2(1, -1))
+ # Returns Vector2(-1, 0)
+ Geometry2D.line_intersects_line(from_a, dir_a, from_b, Vector2(-1, -1))
+ # Returns null
+ Geometry2D.line_intersects_line(from_a, dir_a, from_b, Vector2.RIGHT)
+ [/gdscript]
+ [csharp]
+ var fromA = Vector2.Zero;
+ var dirA = Vector2.Right;
+ var fromB = Vector2.Down;
+
+ // Returns new Vector2(1, 0)
+ Geometry2D.LineIntersectsLine(fromA, dirA, fromB, new Vector2(1, -1));
+ // Returns new Vector2(-1, 0)
+ Geometry2D.LineIntersectsLine(fromA, dirA, fromB, new Vector2(-1, -1));
+ // Returns null
+ Geometry2D.LineIntersectsLine(fromA, dirA, fromB, Vector2.Right);
+ [/csharp]
+ [/codeblocks]
</description>
</method>
<method name="make_atlas">
diff --git a/doc/classes/Image.xml b/doc/classes/Image.xml
index db7796778e..e254fd56e9 100644
--- a/doc/classes/Image.xml
+++ b/doc/classes/Image.xml
@@ -321,6 +321,12 @@
Returns [code]true[/code] if all the image's pixels have an alpha value of 0. Returns [code]false[/code] if any pixel has an alpha value higher than 0.
</description>
</method>
+ <method name="linear_to_srgb">
+ <return type="void" />
+ <description>
+ Converts the entire image from the linear colorspace to the sRGB colorspace. Only works on images with [constant FORMAT_RGB8] or [constant FORMAT_RGBA8] formats.
+ </description>
+ </method>
<method name="load">
<return type="int" enum="Error" />
<param index="0" name="path" type="String" />
@@ -590,7 +596,7 @@
<method name="srgb_to_linear">
<return type="void" />
<description>
- Converts the raw data from the sRGB colorspace to a linear scale.
+ Converts the raw data from the sRGB colorspace to a linear scale. Only works on images with [constant FORMAT_RGB8] or [constant FORMAT_RGBA8] formats.
</description>
</method>
</methods>
diff --git a/doc/classes/NavigationLink2D.xml b/doc/classes/NavigationLink2D.xml
index 0892c9ec44..2e1c962dd1 100644
--- a/doc/classes/NavigationLink2D.xml
+++ b/doc/classes/NavigationLink2D.xml
@@ -29,6 +29,12 @@
Returns whether or not the specified layer of the [member navigation_layers] bitmask is enabled, given a [param layer_number] between 1 and 32.
</description>
</method>
+ <method name="get_navigation_map" qualifiers="const">
+ <return type="RID" />
+ <description>
+ Returns the current navigation map [RID] used by this link.
+ </description>
+ </method>
<method name="get_rid" qualifiers="const">
<return type="RID" />
<description>
@@ -57,6 +63,13 @@
Based on [param value], enables or disables the specified layer in the [member navigation_layers] bitmask, given a [param layer_number] between 1 and 32.
</description>
</method>
+ <method name="set_navigation_map">
+ <return type="void" />
+ <param index="0" name="navigation_map" type="RID" />
+ <description>
+ Sets the [RID] of the navigation map this link should use. By default the link will automatically join the [World2D] default navigation map so this function is only required to override the default map.
+ </description>
+ </method>
</methods>
<members>
<member name="bidirectional" type="bool" setter="set_bidirectional" getter="is_bidirectional" default="true">
diff --git a/doc/classes/NavigationLink3D.xml b/doc/classes/NavigationLink3D.xml
index 0fcc106beb..174228ea5b 100644
--- a/doc/classes/NavigationLink3D.xml
+++ b/doc/classes/NavigationLink3D.xml
@@ -29,6 +29,12 @@
Returns whether or not the specified layer of the [member navigation_layers] bitmask is enabled, given a [param layer_number] between 1 and 32.
</description>
</method>
+ <method name="get_navigation_map" qualifiers="const">
+ <return type="RID" />
+ <description>
+ Returns the current navigation map [RID] used by this link.
+ </description>
+ </method>
<method name="get_rid" qualifiers="const">
<return type="RID" />
<description>
@@ -57,6 +63,13 @@
Based on [param value], enables or disables the specified layer in the [member navigation_layers] bitmask, given a [param layer_number] between 1 and 32.
</description>
</method>
+ <method name="set_navigation_map">
+ <return type="void" />
+ <param index="0" name="navigation_map" type="RID" />
+ <description>
+ Sets the [RID] of the navigation map this link should use. By default the link will automatically join the [World3D] default navigation map so this function is only required to override the default map.
+ </description>
+ </method>
</methods>
<members>
<member name="bidirectional" type="bool" setter="set_bidirectional" getter="is_bidirectional" default="true">
diff --git a/doc/classes/NavigationMesh.xml b/doc/classes/NavigationMesh.xml
index 8be8a89543..5898e6841a 100644
--- a/doc/classes/NavigationMesh.xml
+++ b/doc/classes/NavigationMesh.xml
@@ -139,7 +139,7 @@
The physics layers to scan for static colliders.
Only used when [member geometry_parsed_geometry_type] is [constant PARSED_GEOMETRY_STATIC_COLLIDERS] or [constant PARSED_GEOMETRY_BOTH].
</member>
- <member name="geometry_parsed_geometry_type" type="int" setter="set_parsed_geometry_type" getter="get_parsed_geometry_type" enum="NavigationMesh.ParsedGeometryType" default="0">
+ <member name="geometry_parsed_geometry_type" type="int" setter="set_parsed_geometry_type" getter="get_parsed_geometry_type" enum="NavigationMesh.ParsedGeometryType" default="2">
Determines which type of nodes will be parsed as geometry. See [enum ParsedGeometryType] for possible values.
</member>
<member name="geometry_source_geometry_mode" type="int" setter="set_source_geometry_mode" getter="get_source_geometry_mode" enum="NavigationMesh.SourceGeometryMode" default="0">
diff --git a/doc/classes/NavigationMeshSourceGeometryData2D.xml b/doc/classes/NavigationMeshSourceGeometryData2D.xml
index 1d8689420b..82b6e077fe 100644
--- a/doc/classes/NavigationMeshSourceGeometryData2D.xml
+++ b/doc/classes/NavigationMeshSourceGeometryData2D.xml
@@ -57,6 +57,12 @@
Clears all projected obstructions.
</description>
</method>
+ <method name="get_bounds">
+ <return type="Rect2" />
+ <description>
+ Returns an axis-aligned bounding box that covers all the stored geometry data. The bounds are calculated when calling this function with the result cached until further geometry changes are made.
+ </description>
+ </method>
<method name="get_obstruction_outlines" qualifiers="const">
<return type="PackedVector2Array[]" />
<description>
diff --git a/doc/classes/NavigationMeshSourceGeometryData3D.xml b/doc/classes/NavigationMeshSourceGeometryData3D.xml
index 0b3126a63b..aa43dff52d 100644
--- a/doc/classes/NavigationMeshSourceGeometryData3D.xml
+++ b/doc/classes/NavigationMeshSourceGeometryData3D.xml
@@ -63,6 +63,12 @@
Clears all projected obstructions.
</description>
</method>
+ <method name="get_bounds">
+ <return type="AABB" />
+ <description>
+ Returns an axis-aligned bounding box that covers all the stored geometry data. The bounds are calculated when calling this function with the result cached until further geometry changes are made.
+ </description>
+ </method>
<method name="get_indices" qualifiers="const">
<return type="PackedInt32Array" />
<description>
diff --git a/doc/classes/NavigationPolygon.xml b/doc/classes/NavigationPolygon.xml
index eebdc817a7..68fbc05931 100644
--- a/doc/classes/NavigationPolygon.xml
+++ b/doc/classes/NavigationPolygon.xml
@@ -193,6 +193,9 @@
<member name="parsed_geometry_type" type="int" setter="set_parsed_geometry_type" getter="get_parsed_geometry_type" enum="NavigationPolygon.ParsedGeometryType" default="2">
Determines which type of nodes will be parsed as geometry. See [enum ParsedGeometryType] for possible values.
</member>
+ <member name="sample_partition_type" type="int" setter="set_sample_partition_type" getter="get_sample_partition_type" enum="NavigationPolygon.SamplePartitionType" default="0">
+ Partitioning algorithm for creating the navigation mesh polys. See [enum SamplePartitionType] for possible values.
+ </member>
<member name="source_geometry_group_name" type="StringName" setter="set_source_geometry_group_name" getter="get_source_geometry_group_name" default="&amp;&quot;navigation_polygon_source_geometry_group&quot;">
The group name of nodes that should be parsed for baking source geometry.
Only used when [member source_geometry_mode] is [constant SOURCE_GEOMETRY_GROUPS_WITH_CHILDREN] or [constant SOURCE_GEOMETRY_GROUPS_EXPLICIT].
@@ -202,6 +205,15 @@
</member>
</members>
<constants>
+ <constant name="SAMPLE_PARTITION_CONVEX_PARTITION" value="0" enum="SamplePartitionType">
+ Convex partitioning that yields navigation mesh with convex polygons.
+ </constant>
+ <constant name="SAMPLE_PARTITION_TRIANGULATE" value="1" enum="SamplePartitionType">
+ Triangulation partitioning that yields navigation mesh with triangle polygons.
+ </constant>
+ <constant name="SAMPLE_PARTITION_MAX" value="2" enum="SamplePartitionType">
+ Represents the size of the [enum SamplePartitionType] enum.
+ </constant>
<constant name="PARSED_GEOMETRY_MESH_INSTANCES" value="0" enum="ParsedGeometryType">
Parses mesh instances as obstruction geometry. This includes [Polygon2D], [MeshInstance2D], [MultiMeshInstance2D], and [TileMap] nodes.
Meshes are only parsed when they use a 2D vertices surface format.
diff --git a/doc/classes/NavigationServer3D.xml b/doc/classes/NavigationServer3D.xml
index ff3e6fd8a6..42f6235f8b 100644
--- a/doc/classes/NavigationServer3D.xml
+++ b/doc/classes/NavigationServer3D.xml
@@ -1167,5 +1167,8 @@
<constant name="INFO_EDGE_FREE_COUNT" value="8" enum="ProcessInfo">
Constant to get the number of navigation mesh polygon edges that could not be merged but may be still connected by edge proximity or with links.
</constant>
+ <constant name="INFO_OBSTACLE_COUNT" value="9" enum="ProcessInfo">
+ Constant to get the number of active navigation obstacles.
+ </constant>
</constants>
</class>
diff --git a/doc/classes/Performance.xml b/doc/classes/Performance.xml
index 4d01fe1760..6bb71932dd 100644
--- a/doc/classes/Performance.xml
+++ b/doc/classes/Performance.xml
@@ -221,7 +221,10 @@
<constant name="NAVIGATION_EDGE_FREE_COUNT" value="32" enum="Monitor">
Number of navigation mesh polygon edges that could not be merged in the [NavigationServer3D]. The edges still may be connected by edge proximity or with links.
</constant>
- <constant name="MONITOR_MAX" value="33" enum="Monitor">
+ <constant name="NAVIGATION_OBSTACLE_COUNT" value="33" enum="Monitor">
+ Number of active navigation obstacles in the [NavigationServer3D].
+ </constant>
+ <constant name="MONITOR_MAX" value="34" enum="Monitor">
Represents the size of the [enum Monitor] enum.
</constant>
</constants>
diff --git a/doc/classes/Polygon2D.xml b/doc/classes/Polygon2D.xml
index 90d3522002..4e9208d9bb 100644
--- a/doc/classes/Polygon2D.xml
+++ b/doc/classes/Polygon2D.xml
@@ -71,9 +71,6 @@
<member name="antialiased" type="bool" setter="set_antialiased" getter="get_antialiased" default="false">
If [code]true[/code], polygon edges will be anti-aliased.
</member>
- <member name="bones" type="Array" setter="_set_bones" getter="_get_bones" default="[]">
- Internal list of [Bone2D] nodes used by the assigned [member skeleton]. Edited using the Polygon2D editor ("UV" button on the top toolbar).
- </member>
<member name="color" type="Color" setter="set_color" getter="get_color" default="Color(1, 1, 1, 1)" keywords="colour">
The polygon's fill color. If [member texture] is set, it will be multiplied by this color. It will also be the default color for vertices not set in [member vertex_colors].
</member>
diff --git a/doc/classes/ProjectSettings.xml b/doc/classes/ProjectSettings.xml
index ff2c0bbfd9..37d9995887 100644
--- a/doc/classes/ProjectSettings.xml
+++ b/doc/classes/ProjectSettings.xml
@@ -1004,7 +1004,7 @@
prime-run %command%
[/codeblock]
</member>
- <member name="editor/script/search_in_file_extensions" type="PackedStringArray" setter="" getter="" default="PackedStringArray(&quot;gd&quot;, &quot;gdshader&quot;)">
+ <member name="editor/script/search_in_file_extensions" type="PackedStringArray" setter="" getter="">
Text-based file extensions to include in the script editor's "Find in Files" feature. You can add e.g. [code]tscn[/code] if you wish to also parse your scene files, especially if you use built-in scripts which are serialized in the scene files.
</member>
<member name="editor/script/templates_search_path" type="String" setter="" getter="" default="&quot;res://script_templates&quot;">
@@ -1428,6 +1428,18 @@
<member name="input_devices/pointing/emulate_touch_from_mouse" type="bool" setter="" getter="" default="false">
If [code]true[/code], sends touch input events when clicking or dragging the mouse.
</member>
+ <member name="input_devices/sensors/enable_accelerometer" type="bool" setter="" getter="" default="false">
+ If [code]true[/code], the accelerometer sensor is enabled and [method Input.get_accelerometer] returns valid data.
+ </member>
+ <member name="input_devices/sensors/enable_gravity" type="bool" setter="" getter="" default="false">
+ If [code]true[/code], the gravity sensor is enabled and [method Input.get_gravity] returns valid data.
+ </member>
+ <member name="input_devices/sensors/enable_gyroscope" type="bool" setter="" getter="" default="false">
+ If [code]true[/code], the gyroscope sensor is enabled and [method Input.get_gyroscope] returns valid data.
+ </member>
+ <member name="input_devices/sensors/enable_magnetometer" type="bool" setter="" getter="" default="false">
+ If [code]true[/code], the magnetometer sensor is enabled and [method Input.get_magnetometer] returns valid data.
+ </member>
<member name="internationalization/locale/fallback" type="String" setter="" getter="" default="&quot;en&quot;">
The locale to fall back to if a translation isn't available in a given language. If left empty, [code]en[/code] (English) will be used.
</member>
@@ -2592,6 +2604,10 @@
- Intel GPUs: SYCL libraries
If no GPU acceleration is configured on the system, multi-threaded CPU-based denoising will be performed instead. This CPU-based denoising is significantly slower than the JNLM denoiser in most cases.
</member>
+ <member name="rendering/lightmapping/lightmap_gi/use_bicubic_filter" type="bool" setter="" getter="" default="true">
+ If [code]true[/code], applies a bicubic filter during lightmap sampling. This makes lightmaps look much smoother, at a moderate performance cost.
+ [b]Note:[/b] The bicubic filter exaggerates the 'bleeding' effect that occurs when a lightmap's resolution is low enough.
+ </member>
<member name="rendering/lightmapping/primitive_meshes/texel_size" type="float" setter="" getter="" default="0.2">
The texel_size that is used to calculate the [member Mesh.lightmap_size_hint] on [PrimitiveMesh] resources if [member PrimitiveMesh.add_uv2] is enabled.
</member>
diff --git a/doc/classes/RenderingServer.xml b/doc/classes/RenderingServer.xml
index 9fc0aa20fb..c81d5d4fab 100644
--- a/doc/classes/RenderingServer.xml
+++ b/doc/classes/RenderingServer.xml
@@ -944,7 +944,8 @@
<param index="1" name="item" type="RID" />
<param index="2" name="mirroring" type="Vector2" />
<description>
- A copy of the canvas item will be drawn with a local offset of the mirroring [Vector2].
+ A copy of the canvas item will be drawn with a local offset of the [param mirroring].
+ [b]Note:[/b] This is equivalent to calling [method canvas_set_item_repeat] like [code]canvas_set_item_repeat(item, mirroring, 1)[/code], with an additional check ensuring [param canvas] is a parent of [param item].
</description>
</method>
<method name="canvas_set_item_repeat">
@@ -2206,6 +2207,13 @@
Set the textures on the given [param lightmap] GI instance to the texture array pointed to by the [param light] RID. If the lightmap texture was baked with [member LightmapGI.directional] set to [code]true[/code], then [param uses_sh] must also be [code]true[/code].
</description>
</method>
+ <method name="lightmaps_set_bicubic_filter">
+ <return type="void" />
+ <param index="0" name="enable" type="bool" />
+ <description>
+ Toggles whether a bicubic filter should be used when lightmaps are sampled. This smoothens their appearance at a performance cost.
+ </description>
+ </method>
<method name="make_sphere_mesh">
<return type="RID" />
<param index="0" name="latitudes" type="int" />
diff --git a/doc/classes/ResourceImporterDynamicFont.xml b/doc/classes/ResourceImporterDynamicFont.xml
index f100670e08..b678a04e34 100644
--- a/doc/classes/ResourceImporterDynamicFont.xml
+++ b/doc/classes/ResourceImporterDynamicFont.xml
@@ -69,12 +69,13 @@
<member name="script_support" type="Dictionary" setter="" getter="" default="{}">
Override the list of language scripts supported by this font. If left empty, this is supplied by the font metadata. There is usually no need to change this. See also [member language_support].
</member>
- <member name="subpixel_positioning" type="int" setter="" getter="" default="1">
+ <member name="subpixel_positioning" type="int" setter="" getter="" default="4">
Subpixel positioning improves font rendering appearance, especially at smaller font sizes. The downside is that it takes more time to initially render the font, which can cause stuttering during gameplay, especially if used with large font sizes. This should be set to [b]Disabled[/b] for fonts with a pixel art appearance.
[b]Disabled:[/b] No subpixel positioning. Lowest quality, fastest rendering.
[b]Auto:[/b] Use subpixel positioning at small font sizes (the chosen quality varies depending on font size). Large fonts will not use subpixel positioning. This is a good tradeoff between performance and quality.
[b]One Half of a Pixel:[/b] Always perform intermediate subpixel positioning regardless of font size. High quality, slow rendering.
[b]One Quarter of a Pixel:[/b] Always perform precise subpixel positioning regardless of font size. Highest quality, slowest rendering.
+ [b]Auto (Except Pixel Fonts):[/b] [b]Disabled[/b] for the pixel style fonts (each glyph contours contain only straight horizontal and vertical lines), [b]Auto[/b] for the other fonts.
</member>
</members>
</class>
diff --git a/doc/classes/ResourceImporterImageFont.xml b/doc/classes/ResourceImporterImageFont.xml
index 6dbfa1d651..663c906001 100644
--- a/doc/classes/ResourceImporterImageFont.xml
+++ b/doc/classes/ResourceImporterImageFont.xml
@@ -40,7 +40,7 @@
</member>
<member name="kerning_pairs" type="PackedStringArray" setter="" getter="" default="PackedStringArray()">
Kerning pairs for the font. Kerning pair adjust the spacing between two characters.
- Each string consist of three space separated values: "from" string, "to" string and integer offset. Each combination form the two string for a kerning pair, e.g, [code]ab cd -3[/code] will create kerning pairs [code]ac[/code], [code]ad[/code], [code]bc[/code], and [code]bd[/code] with offset [code]-3[/code].
+ Each string consist of three space separated values: "from" string, "to" string and integer offset. Each combination form the two string for a kerning pair, e.g, [code]ab cd -3[/code] will create kerning pairs [code]ac[/code], [code]ad[/code], [code]bc[/code], and [code]bd[/code] with offset [code]-3[/code]. [code]\uXXXX[/code] escape sequences can be used to add Unicode characters.
</member>
<member name="rows" type="int" setter="" getter="" default="1">
Number of rows in the font image. See also [member columns].
diff --git a/doc/classes/ScriptLanguageExtension.xml b/doc/classes/ScriptLanguageExtension.xml
index cc47ca274d..f9d9e4f513 100644
--- a/doc/classes/ScriptLanguageExtension.xml
+++ b/doc/classes/ScriptLanguageExtension.xml
@@ -314,6 +314,13 @@
<description>
</description>
</method>
+ <method name="_reload_scripts" qualifiers="virtual">
+ <return type="void" />
+ <param index="0" name="scripts" type="Array" />
+ <param index="1" name="soft_reload" type="bool" />
+ <description>
+ </description>
+ </method>
<method name="_reload_tool_script" qualifiers="virtual">
<return type="void" />
<param index="0" name="script" type="Script" />
diff --git a/doc/classes/ShapeCast2D.xml b/doc/classes/ShapeCast2D.xml
index 385e3a9285..6f5da28825 100644
--- a/doc/classes/ShapeCast2D.xml
+++ b/doc/classes/ShapeCast2D.xml
@@ -15,7 +15,7 @@
<return type="void" />
<param index="0" name="node" type="CollisionObject2D" />
<description>
- Adds a collision exception so the shape does not report collisions with the specified [CollisionObject2D] node.
+ Adds a collision exception so the shape does not report collisions with the specified node.
</description>
</method>
<method name="add_exception_rid">
@@ -35,19 +35,19 @@
<return type="void" />
<description>
Updates the collision information for the shape immediately, without waiting for the next [code]_physics_process[/code] call. Use this method, for example, when the shape or its parent has changed state.
- [b]Note:[/b] [code]enabled == true[/code] is not required for this to work.
+ [b]Note:[/b] Setting [member enabled] to [code]true[/code] is not required for this to work.
</description>
</method>
<method name="get_closest_collision_safe_fraction" qualifiers="const">
<return type="float" />
<description>
- The fraction from the [ShapeCast2D]'s origin to its [member target_position] (between 0 and 1) of how far the shape can move without triggering a collision.
+ Returns the fraction from this cast's origin to its [member target_position] of how far the shape can move without triggering a collision, as a value between [code]0.0[/code] and [code]1.0[/code].
</description>
</method>
<method name="get_closest_collision_unsafe_fraction" qualifiers="const">
<return type="float" />
<description>
- The fraction from the [ShapeCast2D]'s origin to its [member target_position] (between 0 and 1) of how far the shape must move to trigger a collision.
+ Returns the fraction from this cast's origin to its [member target_position] of how far the shape must move to trigger a collision, as a value between [code]0.0[/code] and [code]1.0[/code].
In ideal conditions this would be the same as [method get_closest_collision_safe_fraction], however shape casting is calculated in discrete steps, so the precise point of collision can occur between two calculated positions.
</description>
</method>
@@ -97,7 +97,7 @@
<param index="0" name="index" type="int" />
<description>
Returns the collision point of one of the multiple collisions at [param index] where the shape intersects the colliding object.
- [b]Note:[/b] this point is in the [b]global[/b] coordinate system.
+ [b]Note:[/b] This point is in the [b]global[/b] coordinate system.
</description>
</method>
<method name="is_colliding" qualifiers="const">
@@ -110,7 +110,7 @@
<return type="void" />
<param index="0" name="node" type="CollisionObject2D" />
<description>
- Removes a collision exception so the shape does report collisions with the specified [CollisionObject2D] node.
+ Removes a collision exception so the shape does report collisions with the specified node.
</description>
</method>
<method name="remove_exception_rid">
@@ -137,7 +137,7 @@
If [code]true[/code], collisions with [PhysicsBody2D]s will be reported.
</member>
<member name="collision_mask" type="int" setter="set_collision_mask" getter="get_collision_mask" default="1">
- The shape's collision mask. Only objects in at least one collision layer enabled in the mask will be detected.
+ The shape's collision mask. Only objects in at least one collision layer enabled in the mask will be detected. See [url=$DOCS_URL/tutorials/physics/physics_introduction.html#collision-layers-and-masks]Collision layers and masks[/url] in the documentation for more information.
</member>
<member name="collision_result" type="Array" setter="" getter="get_collision_result" default="[]">
Returns the complete collision information from the collision sweep. The data returned is the same as in the [method PhysicsDirectSpaceState2D.get_rest_info] method.
@@ -155,10 +155,10 @@
The number of intersections can be limited with this parameter, to reduce the processing time.
</member>
<member name="shape" type="Shape2D" setter="set_shape" getter="get_shape">
- The [Shape2D]-derived shape to be used for collision queries.
+ The shape to be used for collision queries.
</member>
<member name="target_position" type="Vector2" setter="set_target_position" getter="get_target_position" default="Vector2(0, 50)">
- The shape's destination point, relative to this node's [code]position[/code].
+ The shape's destination point, relative to this node's [member Node2D.position].
</member>
</members>
</class>
diff --git a/doc/classes/ShapeCast3D.xml b/doc/classes/ShapeCast3D.xml
index f70cf169df..ba0b021b4d 100644
--- a/doc/classes/ShapeCast3D.xml
+++ b/doc/classes/ShapeCast3D.xml
@@ -15,7 +15,7 @@
<return type="void" />
<param index="0" name="node" type="CollisionObject3D" />
<description>
- Adds a collision exception so the shape does not report collisions with the specified [CollisionObject3D] node.
+ Adds a collision exception so the shape does not report collisions with the specified node.
</description>
</method>
<method name="add_exception_rid">
@@ -28,26 +28,26 @@
<method name="clear_exceptions">
<return type="void" />
<description>
- Removes all collision exceptions for this [ShapeCast3D].
+ Removes all collision exceptions for this shape.
</description>
</method>
<method name="force_shapecast_update">
<return type="void" />
<description>
Updates the collision information for the shape immediately, without waiting for the next [code]_physics_process[/code] call. Use this method, for example, when the shape or its parent has changed state.
- [b]Note:[/b] [code]enabled == true[/code] is not required for this to work.
+ [b]Note:[/b] Setting [member enabled] to [code]true[/code] is not required for this to work.
</description>
</method>
<method name="get_closest_collision_safe_fraction" qualifiers="const">
<return type="float" />
<description>
- The fraction from the [ShapeCast3D]'s origin to its [member target_position] (between 0 and 1) of how far the shape can move without triggering a collision.
+ Returns the fraction from this cast's origin to its [member target_position] of how far the shape can move without triggering a collision, as a value between [code]0.0[/code] and [code]1.0[/code].
</description>
</method>
<method name="get_closest_collision_unsafe_fraction" qualifiers="const">
<return type="float" />
<description>
- The fraction from the [ShapeCast3D]'s origin to its [member target_position] (between 0 and 1) of how far the shape must move to trigger a collision.
+ Returns the fraction from this cast's origin to its [member target_position] of how far the shape must move to trigger a collision, as a value between [code]0.0[/code] and [code]1.0[/code].
In ideal conditions this would be the same as [method get_closest_collision_safe_fraction], however shape casting is calculated in discrete steps, so the precise point of collision can occur between two calculated positions.
</description>
</method>
@@ -97,7 +97,7 @@
<param index="0" name="index" type="int" />
<description>
Returns the collision point of one of the multiple collisions at [param index] where the shape intersects the colliding object.
- [b]Note:[/b] this point is in the [b]global[/b] coordinate system.
+ [b]Note:[/b] This point is in the [b]global[/b] coordinate system.
</description>
</method>
<method name="is_colliding" qualifiers="const">
@@ -110,7 +110,7 @@
<return type="void" />
<param index="0" name="node" type="CollisionObject3D" />
<description>
- Removes a collision exception so the shape does report collisions with the specified [CollisionObject3D] node.
+ Removes a collision exception so the shape does report collisions with the specified node.
</description>
</method>
<method name="remove_exception_rid">
@@ -166,10 +166,10 @@
The number of intersections can be limited with this parameter, to reduce the processing time.
</member>
<member name="shape" type="Shape3D" setter="set_shape" getter="get_shape">
- The [Shape3D]-derived shape to be used for collision queries.
+ The shape to be used for collision queries.
</member>
<member name="target_position" type="Vector3" setter="set_target_position" getter="get_target_position" default="Vector3(0, -1, 0)">
- The shape's destination point, relative to this node's [code]position[/code].
+ The shape's destination point, relative to this node's [member Node3D.position].
</member>
</members>
</class>
diff --git a/doc/classes/TextServer.xml b/doc/classes/TextServer.xml
index 4fa9700f9c..9d476691bf 100644
--- a/doc/classes/TextServer.xml
+++ b/doc/classes/TextServer.xml
@@ -459,6 +459,13 @@
Returns a string containing all the characters available in the font.
</description>
</method>
+ <method name="font_get_supported_glyphs" qualifiers="const">
+ <return type="PackedInt32Array" />
+ <param index="0" name="font_rid" type="RID" />
+ <description>
+ Returns an array containing all glyph indices in the font.
+ </description>
+ </method>
<method name="font_get_texture_count" qualifiers="const">
<return type="int" />
<param index="0" name="font_rid" type="RID" />
diff --git a/doc/classes/TextServerExtension.xml b/doc/classes/TextServerExtension.xml
index c148cdad52..3c27404f8e 100644
--- a/doc/classes/TextServerExtension.xml
+++ b/doc/classes/TextServerExtension.xml
@@ -496,6 +496,14 @@
Returns a string containing all the characters available in the font.
</description>
</method>
+ <method name="_font_get_supported_glyphs" qualifiers="virtual const">
+ <return type="PackedInt32Array" />
+ <param index="0" name="font_rid" type="RID" />
+ <description>
+ [b]Required.[/b]
+ Returns an array containing all glyph indices in the font.
+ </description>
+ </method>
<method name="_font_get_texture_count" qualifiers="virtual const">
<return type="int" />
<param index="0" name="font_rid" type="RID" />
diff --git a/doc/classes/TileMap.xml b/doc/classes/TileMap.xml
index d3197efc6b..0995a5a672 100644
--- a/doc/classes/TileMap.xml
+++ b/doc/classes/TileMap.xml
@@ -256,6 +256,33 @@
Returns a rectangle enclosing the used (non-empty) tiles of the map, including all layers.
</description>
</method>
+ <method name="is_cell_flipped_h" qualifiers="const">
+ <return type="bool" />
+ <param index="0" name="layer" type="int" />
+ <param index="1" name="coords" type="Vector2i" />
+ <param index="2" name="use_proxies" type="bool" default="false" />
+ <description>
+ Returns [code]true[/code] if the cell on layer [param layer] at coordinates [param coords] is flipped horizontally. The result is valid only for atlas sources.
+ </description>
+ </method>
+ <method name="is_cell_flipped_v" qualifiers="const">
+ <return type="bool" />
+ <param index="0" name="layer" type="int" />
+ <param index="1" name="coords" type="Vector2i" />
+ <param index="2" name="use_proxies" type="bool" default="false" />
+ <description>
+ Returns [code]true[/code] if the cell on layer [param layer] at coordinates [param coords] is flipped vertically. The result is valid only for atlas sources.
+ </description>
+ </method>
+ <method name="is_cell_transposed" qualifiers="const">
+ <return type="bool" />
+ <param index="0" name="layer" type="int" />
+ <param index="1" name="coords" type="Vector2i" />
+ <param index="2" name="use_proxies" type="bool" default="false" />
+ <description>
+ Returns [code]true[/code] if the cell on layer [param layer] at coordinates [param coords] is transposed. The result is valid only for atlas sources.
+ </description>
+ </method>
<method name="is_layer_enabled" qualifiers="const">
<return type="bool" />
<param index="0" name="layer" type="int" />
diff --git a/doc/classes/TileMapLayer.xml b/doc/classes/TileMapLayer.xml
index b9acef2095..bead1c32c0 100644
--- a/doc/classes/TileMapLayer.xml
+++ b/doc/classes/TileMapLayer.xml
@@ -153,6 +153,27 @@
Returns whether the provided [param body] [RID] belongs to one of this [TileMapLayer]'s cells.
</description>
</method>
+ <method name="is_cell_flipped_h" qualifiers="const">
+ <return type="bool" />
+ <param index="0" name="coords" type="Vector2i" />
+ <description>
+ Returns [code]true[/code] if the cell at coordinates [param coords] is flipped horizontally. The result is valid only for atlas sources.
+ </description>
+ </method>
+ <method name="is_cell_flipped_v" qualifiers="const">
+ <return type="bool" />
+ <param index="0" name="coords" type="Vector2i" />
+ <description>
+ Returns [code]true[/code] if the cell at coordinates [param coords] is flipped vertically. The result is valid only for atlas sources.
+ </description>
+ </method>
+ <method name="is_cell_transposed" qualifiers="const">
+ <return type="bool" />
+ <param index="0" name="coords" type="Vector2i" />
+ <description>
+ Returns [code]true[/code] if the cell at coordinates [param coords] is transposed. The result is valid only for atlas sources.
+ </description>
+ </method>
<method name="local_to_map" qualifiers="const">
<return type="Vector2i" />
<param index="0" name="local_position" type="Vector2" />
diff --git a/doc/classes/Timer.xml b/doc/classes/Timer.xml
index 9de1e09273..f8f9393847 100644
--- a/doc/classes/Timer.xml
+++ b/doc/classes/Timer.xml
@@ -28,7 +28,7 @@
<return type="void" />
<param index="0" name="time_sec" type="float" default="-1" />
<description>
- Starts the timer, if it was not started already. Fails if the timer is not inside the tree. If [param time_sec] is greater than [code]0[/code], this value is used for the [member wait_time].
+ Starts the timer, or resets the timer if it was started already. Fails if the timer is not inside the tree. If [param time_sec] is greater than [code]0[/code], this value is used for the [member wait_time].
[b]Note:[/b] This method does not resume a paused timer. See [member paused].
</description>
</method>
diff --git a/doc/classes/Viewport.xml b/doc/classes/Viewport.xml
index f57185ae87..b24f26a764 100644
--- a/doc/classes/Viewport.xml
+++ b/doc/classes/Viewport.xml
@@ -102,6 +102,7 @@
await RenderingServer.frame_post_draw
$Viewport.get_texture().get_image().save_png("user://Screenshot.png")
[/codeblock]
+ [b]Note:[/b] When [member use_hdr_2d] is [code]true[/code] the returned texture will be an HDR image encoded in linear space.
</description>
</method>
<method name="get_viewport_rid" qualifiers="const">
diff --git a/doc/classes/ViewportTexture.xml b/doc/classes/ViewportTexture.xml
index ba2352ab61..f6840d6b09 100644
--- a/doc/classes/ViewportTexture.xml
+++ b/doc/classes/ViewportTexture.xml
@@ -8,6 +8,11 @@
To get a [ViewportTexture] in code, use the [method Viewport.get_texture] method on the target viewport.
[b]Note:[/b] A [ViewportTexture] is always local to its scene (see [member Resource.resource_local_to_scene]). If the scene root is not ready, it may return incorrect data (see [signal Node.ready]).
[b]Note:[/b] Instantiating scenes containing a high-resolution [ViewportTexture] may cause noticeable stutter.
+ [b]Note:[/b] When using a [Viewport] with [member Viewport.use_hdr_2d] set to [code]true[/code] the returned texture will be an HDR image encoded in linear space. This may look darker than normal when displayed directly on screen. To convert to gamma space, you can do the following:
+ [codeblock]
+ img.convert(Image.FORMAT_RGBA8)
+ imb.linear_to_srgb()
+ [/codeblock]
</description>
<tutorials>
<link title="GUI in 3D Viewport Demo">https://godotengine.org/asset-library/asset/2807</link>
diff --git a/drivers/gles3/rasterizer_canvas_gles3.cpp b/drivers/gles3/rasterizer_canvas_gles3.cpp
index 941b1a1b28..7a6df3d2a4 100644
--- a/drivers/gles3/rasterizer_canvas_gles3.cpp
+++ b/drivers/gles3/rasterizer_canvas_gles3.cpp
@@ -647,18 +647,17 @@ void RasterizerCanvasGLES3::_render_items(RID p_to_render_target, int p_item_cou
_record_item_commands(ci, p_to_render_target, p_canvas_transform_inverse, current_clip, blend_mode, p_lights, index, batch_broken, r_sdf_used, Point2());
} else {
Point2 start_pos = ci->repeat_size * -(ci->repeat_times / 2);
- Point2 end_pos = ci->repeat_size * ci->repeat_times + ci->repeat_size + start_pos;
- Point2 pos = start_pos;
-
- do {
- do {
- _record_item_commands(ci, p_to_render_target, p_canvas_transform_inverse, current_clip, blend_mode, p_lights, index, batch_broken, r_sdf_used, pos);
- pos.y += ci->repeat_size.y;
- } while (pos.y < end_pos.y);
-
- pos.x += ci->repeat_size.x;
- pos.y = start_pos.y;
- } while (pos.x < end_pos.x);
+ Point2 offset;
+
+ int repeat_times_x = ci->repeat_size.x ? ci->repeat_times : 0;
+ int repeat_times_y = ci->repeat_size.y ? ci->repeat_times : 0;
+ for (int ry = 0; ry <= repeat_times_y; ry++) {
+ offset.y = start_pos.y + ry * ci->repeat_size.y;
+ for (int rx = 0; rx <= repeat_times_x; rx++) {
+ offset.x = start_pos.x + rx * ci->repeat_size.x;
+ _record_item_commands(ci, p_to_render_target, p_canvas_transform_inverse, current_clip, blend_mode, p_lights, index, batch_broken, r_sdf_used, offset);
+ }
+ }
}
}
@@ -809,7 +808,7 @@ void RasterizerCanvasGLES3::_render_items(RID p_to_render_target, int p_item_cou
state.last_item_index += index;
}
-void RasterizerCanvasGLES3::_record_item_commands(const Item *p_item, RID p_render_target, const Transform2D &p_canvas_transform_inverse, Item *&current_clip, GLES3::CanvasShaderData::BlendMode p_blend_mode, Light *p_lights, uint32_t &r_index, bool &r_batch_broken, bool &r_sdf_used, const Point2 &p_offset) {
+void RasterizerCanvasGLES3::_record_item_commands(const Item *p_item, RID p_render_target, const Transform2D &p_canvas_transform_inverse, Item *&current_clip, GLES3::CanvasShaderData::BlendMode p_blend_mode, Light *p_lights, uint32_t &r_index, bool &r_batch_broken, bool &r_sdf_used, const Point2 &p_repeat_offset) {
RenderingServer::CanvasItemTextureFilter texture_filter = p_item->texture_filter == RS::CANVAS_ITEM_TEXTURE_FILTER_DEFAULT ? state.default_filter : p_item->texture_filter;
if (texture_filter != state.canvas_instance_batches[state.current_batch_index].filter) {
@@ -826,11 +825,11 @@ void RasterizerCanvasGLES3::_record_item_commands(const Item *p_item, RID p_rend
state.canvas_instance_batches[state.current_batch_index].repeat = texture_repeat;
}
- Transform2D base_transform = p_canvas_transform_inverse * p_item->final_transform;
-
- if (p_offset.x || p_offset.y) {
- base_transform *= Transform2D(0, p_offset / p_item->xform_curr.get_scale()); // TODO: Interpolate or explain why not needed.
+ Transform2D base_transform = p_item->final_transform;
+ if (p_item->repeat_source_item && (p_repeat_offset.x || p_repeat_offset.y)) {
+ base_transform.columns[2] += p_item->repeat_source_item->final_transform.basis_xform(p_repeat_offset);
}
+ base_transform = p_canvas_transform_inverse * base_transform;
Transform2D draw_transform; // Used by transform command
diff --git a/drivers/gles3/rasterizer_canvas_gles3.h b/drivers/gles3/rasterizer_canvas_gles3.h
index 46ed479a3d..027f717eb7 100644
--- a/drivers/gles3/rasterizer_canvas_gles3.h
+++ b/drivers/gles3/rasterizer_canvas_gles3.h
@@ -362,7 +362,7 @@ public:
void canvas_render_items(RID p_to_render_target, Item *p_item_list, const Color &p_modulate, Light *p_light_list, Light *p_directional_list, const Transform2D &p_canvas_transform, RS::CanvasItemTextureFilter p_default_filter, RS::CanvasItemTextureRepeat p_default_repeat, bool p_snap_2d_vertices_to_pixel, bool &r_sdf_used, RenderingMethod::RenderInfo *r_render_info = nullptr) override;
void _render_items(RID p_to_render_target, int p_item_count, const Transform2D &p_canvas_transform_inverse, Light *p_lights, bool &r_sdf_used, bool p_to_backbuffer = false, RenderingMethod::RenderInfo *r_render_info = nullptr);
- void _record_item_commands(const Item *p_item, RID p_render_target, const Transform2D &p_canvas_transform_inverse, Item *&current_clip, GLES3::CanvasShaderData::BlendMode p_blend_mode, Light *p_lights, uint32_t &r_index, bool &r_break_batch, bool &r_sdf_used, const Point2 &p_offset);
+ void _record_item_commands(const Item *p_item, RID p_render_target, const Transform2D &p_canvas_transform_inverse, Item *&current_clip, GLES3::CanvasShaderData::BlendMode p_blend_mode, Light *p_lights, uint32_t &r_index, bool &r_break_batch, bool &r_sdf_used, const Point2 &p_repeat_offset);
void _render_batch(Light *p_lights, uint32_t p_index, RenderingMethod::RenderInfo *r_render_info = nullptr);
bool _bind_material(GLES3::CanvasMaterialData *p_material_data, CanvasShaderGLES3::ShaderVariant p_variant, uint64_t p_specialization);
void _new_batch(bool &r_batch_broken);
diff --git a/drivers/gles3/rasterizer_scene_gles3.cpp b/drivers/gles3/rasterizer_scene_gles3.cpp
index 84b6ab4bd8..3ed8042f3f 100644
--- a/drivers/gles3/rasterizer_scene_gles3.cpp
+++ b/drivers/gles3/rasterizer_scene_gles3.cpp
@@ -777,7 +777,6 @@ void RasterizerSceneGLES3::_draw_sky(RID p_env, const Projection &p_projection,
ERR_FAIL_COND(p_env.is_null());
Sky *sky = sky_owner.get_or_null(environment_get_sky(p_env));
- ERR_FAIL_NULL(sky);
GLES3::SkyMaterialData *material_data = nullptr;
RID sky_material;
@@ -851,6 +850,15 @@ void RasterizerSceneGLES3::_draw_sky(RID p_env, const Projection &p_projection,
material_storage->shaders.sky_shader.version_set_uniform(SkyShaderGLES3::SKY_ENERGY_MULTIPLIER, p_sky_energy_multiplier, shader_data->version, SkyShaderGLES3::MODE_BACKGROUND, spec_constants);
material_storage->shaders.sky_shader.version_set_uniform(SkyShaderGLES3::LUMINANCE_MULTIPLIER, p_luminance_multiplier, shader_data->version, SkyShaderGLES3::MODE_BACKGROUND, spec_constants);
+ Color fog_color = environment_get_fog_light_color(p_env).srgb_to_linear() * environment_get_fog_light_energy(p_env);
+ material_storage->shaders.sky_shader.version_set_uniform(SkyShaderGLES3::FOG_ENABLED, environment_get_fog_enabled(p_env), shader_data->version, SkyShaderGLES3::MODE_BACKGROUND, spec_constants);
+ material_storage->shaders.sky_shader.version_set_uniform(SkyShaderGLES3::FOG_AERIAL_PERSPECTIVE, environment_get_fog_aerial_perspective(p_env), shader_data->version, SkyShaderGLES3::MODE_BACKGROUND, spec_constants);
+ material_storage->shaders.sky_shader.version_set_uniform(SkyShaderGLES3::FOG_LIGHT_COLOR, fog_color, shader_data->version, SkyShaderGLES3::MODE_BACKGROUND, spec_constants);
+ material_storage->shaders.sky_shader.version_set_uniform(SkyShaderGLES3::FOG_SUN_SCATTER, environment_get_fog_sun_scatter(p_env), shader_data->version, SkyShaderGLES3::MODE_BACKGROUND, spec_constants);
+ material_storage->shaders.sky_shader.version_set_uniform(SkyShaderGLES3::FOG_DENSITY, environment_get_fog_density(p_env), shader_data->version, SkyShaderGLES3::MODE_BACKGROUND, spec_constants);
+ material_storage->shaders.sky_shader.version_set_uniform(SkyShaderGLES3::FOG_SKY_AFFECT, environment_get_fog_sky_affect(p_env), shader_data->version, SkyShaderGLES3::MODE_BACKGROUND, spec_constants);
+ material_storage->shaders.sky_shader.version_set_uniform(SkyShaderGLES3::DIRECTIONAL_LIGHT_COUNT, sky_globals.directional_light_count, shader_data->version, SkyShaderGLES3::MODE_BACKGROUND, spec_constants);
+
if (p_use_multiview) {
glBindBufferBase(GL_UNIFORM_BUFFER, SKY_MULTIVIEW_UNIFORM_LOCATION, scene_state.multiview_buffer);
glBindBuffer(GL_UNIFORM_BUFFER, 0);
@@ -2587,7 +2595,7 @@ void RasterizerSceneGLES3::render_scene(const Ref<RenderSceneBuffers> &p_render_
scene_state.enable_gl_depth_draw(false);
- if (draw_sky) {
+ if (draw_sky || draw_sky_fog_only) {
RENDER_TIMESTAMP("Render Sky");
scene_state.enable_gl_depth_test(true);
@@ -3202,6 +3210,10 @@ void RasterizerSceneGLES3::_render_list_template(RenderListParameters *p_params,
if (lm->uses_spherical_harmonics) {
spec_constants |= SceneShaderGLES3::USE_SH_LIGHTMAP;
}
+
+ if (lightmap_bicubic_upscale) {
+ spec_constants |= SceneShaderGLES3::LIGHTMAP_BICUBIC_FILTER;
+ }
} else if (inst->lightmap_sh) {
spec_constants |= SceneShaderGLES3::USE_LIGHTMAP_CAPTURE;
} else {
@@ -3344,6 +3356,11 @@ void RasterizerSceneGLES3::_render_list_template(RenderListParameters *p_params,
Vector4 uv_scale(inst->lightmap_uv_scale.position.x, inst->lightmap_uv_scale.position.y, inst->lightmap_uv_scale.size.x, inst->lightmap_uv_scale.size.y);
material_storage->shaders.scene_shader.version_set_uniform(SceneShaderGLES3::LIGHTMAP_UV_SCALE, uv_scale, shader->version, instance_variant, spec_constants);
+ if (lightmap_bicubic_upscale) {
+ Vector2 light_texture_size(lm->light_texture_size.x, lm->light_texture_size.y);
+ material_storage->shaders.scene_shader.version_set_uniform(SceneShaderGLES3::LIGHTMAP_TEXTURE_SIZE, light_texture_size, shader->version, instance_variant, spec_constants);
+ }
+
float exposure_normalization = 1.0;
if (p_render_data->camera_attributes.is_valid()) {
float enf = RSG::camera_attributes->camera_attributes_get_exposure_normalization_factor(p_render_data->camera_attributes);
@@ -4039,6 +4056,10 @@ void RasterizerSceneGLES3::decals_set_filter(RS::DecalFilter p_filter) {
void RasterizerSceneGLES3::light_projectors_set_filter(RS::LightProjectorFilter p_filter) {
}
+void RasterizerSceneGLES3::lightmaps_set_bicubic_filter(bool p_enable) {
+ lightmap_bicubic_upscale = p_enable;
+}
+
RasterizerSceneGLES3::RasterizerSceneGLES3() {
singleton = this;
@@ -4052,6 +4073,7 @@ RasterizerSceneGLES3::RasterizerSceneGLES3() {
positional_soft_shadow_filter_set_quality((RS::ShadowQuality)(int)GLOBAL_GET("rendering/lights_and_shadows/positional_shadow/soft_shadow_filter_quality"));
directional_soft_shadow_filter_set_quality((RS::ShadowQuality)(int)GLOBAL_GET("rendering/lights_and_shadows/directional_shadow/soft_shadow_filter_quality"));
+ lightmaps_set_bicubic_filter(GLOBAL_GET("rendering/lightmapping/lightmap_gi/use_bicubic_filter"));
{
// Setup Lights
diff --git a/drivers/gles3/rasterizer_scene_gles3.h b/drivers/gles3/rasterizer_scene_gles3.h
index 4c70c43244..e4af8f99e9 100644
--- a/drivers/gles3/rasterizer_scene_gles3.h
+++ b/drivers/gles3/rasterizer_scene_gles3.h
@@ -680,6 +680,8 @@ protected:
bool glow_bicubic_upscale = false;
RS::EnvironmentSSRRoughnessQuality ssr_roughness_quality = RS::ENV_SSR_ROUGHNESS_QUALITY_LOW;
+ bool lightmap_bicubic_upscale = false;
+
/* Sky */
struct SkyGlobals {
@@ -863,6 +865,7 @@ public:
void decals_set_filter(RS::DecalFilter p_filter) override;
void light_projectors_set_filter(RS::LightProjectorFilter p_filter) override;
+ virtual void lightmaps_set_bicubic_filter(bool p_enable) override;
RasterizerSceneGLES3();
~RasterizerSceneGLES3();
diff --git a/drivers/gles3/shaders/scene.glsl b/drivers/gles3/shaders/scene.glsl
index 6dd04af6b6..3e3b4d11f7 100644
--- a/drivers/gles3/shaders/scene.glsl
+++ b/drivers/gles3/shaders/scene.glsl
@@ -36,6 +36,7 @@ ADDITIVE_OMNI = false
ADDITIVE_SPOT = false
RENDER_MATERIAL = false
SECOND_REFLECTION_PROBE = false
+LIGHTMAP_BICUBIC_FILTER = false
#[vertex]
@@ -583,6 +584,8 @@ void main() {
#define SHADER_IS_SRGB true
+#define FLAGS_NON_UNIFORM_SCALE (1 << 4)
+
/* Varyings */
#if defined(COLOR_USED)
@@ -923,6 +926,10 @@ uniform lowp uint lightmap_slice;
uniform highp vec4 lightmap_uv_scale;
uniform float lightmap_exposure_normalization;
+#ifdef LIGHTMAP_BICUBIC_FILTER
+uniform highp vec2 lightmap_texture_size;
+#endif
+
#ifdef USE_SH_LIGHTMAP
uniform mediump mat3 lightmap_normal_xform;
#endif // USE_SH_LIGHTMAP
@@ -955,6 +962,7 @@ ivec2 multiview_uv(ivec2 uv) {
uniform highp mat4 world_transform;
uniform mediump float opaque_prepass_threshold;
+uniform highp uint model_flags;
#if defined(RENDER_MATERIAL)
layout(location = 0) out vec4 albedo_output_buffer;
@@ -1414,6 +1422,67 @@ void reflection_process(samplerCube reflection_map,
#endif // !MODE_RENDER_DEPTH
+#ifdef LIGHTMAP_BICUBIC_FILTER
+// w0, w1, w2, and w3 are the four cubic B-spline basis functions
+float w0(float a) {
+ return (1.0 / 6.0) * (a * (a * (-a + 3.0) - 3.0) + 1.0);
+}
+
+float w1(float a) {
+ return (1.0 / 6.0) * (a * a * (3.0 * a - 6.0) + 4.0);
+}
+
+float w2(float a) {
+ return (1.0 / 6.0) * (a * (a * (-3.0 * a + 3.0) + 3.0) + 1.0);
+}
+
+float w3(float a) {
+ return (1.0 / 6.0) * (a * a * a);
+}
+
+// g0 and g1 are the two amplitude functions
+float g0(float a) {
+ return w0(a) + w1(a);
+}
+
+float g1(float a) {
+ return w2(a) + w3(a);
+}
+
+// h0 and h1 are the two offset functions
+float h0(float a) {
+ return -1.0 + w1(a) / (w0(a) + w1(a));
+}
+
+float h1(float a) {
+ return 1.0 + w3(a) / (w2(a) + w3(a));
+}
+
+vec4 textureArray_bicubic(sampler2DArray tex, vec3 uv, vec2 texture_size) {
+ vec2 texel_size = vec2(1.0) / texture_size;
+
+ uv.xy = uv.xy * texture_size + vec2(0.5);
+
+ vec2 iuv = floor(uv.xy);
+ vec2 fuv = fract(uv.xy);
+
+ float g0x = g0(fuv.x);
+ float g1x = g1(fuv.x);
+ float h0x = h0(fuv.x);
+ float h1x = h1(fuv.x);
+ float h0y = h0(fuv.y);
+ float h1y = h1(fuv.y);
+
+ vec2 p0 = (vec2(iuv.x + h0x, iuv.y + h0y) - vec2(0.5)) * texel_size;
+ vec2 p1 = (vec2(iuv.x + h1x, iuv.y + h0y) - vec2(0.5)) * texel_size;
+ vec2 p2 = (vec2(iuv.x + h0x, iuv.y + h1y) - vec2(0.5)) * texel_size;
+ vec2 p3 = (vec2(iuv.x + h1x, iuv.y + h1y) - vec2(0.5)) * texel_size;
+
+ return (g0(fuv.y) * (g0x * texture(tex, vec3(p0, uv.z)) + g1x * texture(tex, vec3(p1, uv.z)))) +
+ (g1(fuv.y) * (g0x * texture(tex, vec3(p2, uv.z)) + g1x * texture(tex, vec3(p3, uv.z))));
+}
+#endif //LIGHTMAP_BICUBIC_FILTER
+
void main() {
//lay out everything, whatever is unused is optimized away anyway
vec3 vertex = vertex_interp;
@@ -1521,6 +1590,13 @@ void main() {
vec3 light_vertex = vertex;
#endif //LIGHT_VERTEX_USED
+ highp mat3 model_normal_matrix;
+ if (bool(model_flags & uint(FLAGS_NON_UNIFORM_SCALE))) {
+ model_normal_matrix = transpose(inverse(mat3(model_matrix)));
+ } else {
+ model_normal_matrix = mat3(model_matrix);
+ }
+
{
#CODE : FRAGMENT
}
@@ -1609,6 +1685,7 @@ void main() {
#ifdef BASE_PASS
/////////////////////// LIGHTING //////////////////////////////
+#ifndef AMBIENT_LIGHT_DISABLED
// IBL precalculations
float ndotv = clamp(dot(normal, view), 0.0, 1.0);
vec3 F = f0 + (max(vec3(1.0 - roughness), f0) - f0) * pow(1.0 - ndotv, 5.0);
@@ -1721,10 +1798,18 @@ void main() {
#ifdef USE_SH_LIGHTMAP
uvw.z *= 4.0; // SH textures use 4 times more data.
+
+#ifdef LIGHTMAP_BICUBIC_FILTER
+ vec3 lm_light_l0 = textureArray_bicubic(lightmap_textures, uvw + vec3(0.0, 0.0, 0.0), lightmap_texture_size).rgb;
+ vec3 lm_light_l1n1 = textureArray_bicubic(lightmap_textures, uvw + vec3(0.0, 0.0, 1.0), lightmap_texture_size).rgb;
+ vec3 lm_light_l1_0 = textureArray_bicubic(lightmap_textures, uvw + vec3(0.0, 0.0, 2.0), lightmap_texture_size).rgb;
+ vec3 lm_light_l1p1 = textureArray_bicubic(lightmap_textures, uvw + vec3(0.0, 0.0, 3.0), lightmap_texture_size).rgb;
+#else
vec3 lm_light_l0 = textureLod(lightmap_textures, uvw + vec3(0.0, 0.0, 0.0), 0.0).rgb;
vec3 lm_light_l1n1 = textureLod(lightmap_textures, uvw + vec3(0.0, 0.0, 1.0), 0.0).rgb;
vec3 lm_light_l1_0 = textureLod(lightmap_textures, uvw + vec3(0.0, 0.0, 2.0), 0.0).rgb;
vec3 lm_light_l1p1 = textureLod(lightmap_textures, uvw + vec3(0.0, 0.0, 3.0), 0.0).rgb;
+#endif
vec3 n = normalize(lightmap_normal_xform * normal);
@@ -1739,25 +1824,25 @@ void main() {
specular_light += lm_light_l1p1 * 0.32573 * r.x * lightmap_exposure_normalization;
}
#else
+#ifdef LIGHTMAP_BICUBIC_FILTER
+ ambient_light += textureArray_bicubic(lightmap_textures, uvw, lightmap_texture_size).rgb * lightmap_exposure_normalization;
+#else
ambient_light += textureLod(lightmap_textures, uvw, 0.0).rgb * lightmap_exposure_normalization;
#endif
+#endif
}
#endif // USE_LIGHTMAP
#endif // USE_LIGHTMAP_CAPTURE
#endif // !DISABLE_LIGHTMAP
- {
-#if defined(AMBIENT_LIGHT_DISABLED)
- ambient_light = vec3(0.0, 0.0, 0.0);
-#else
- ambient_light *= albedo.rgb;
- ambient_light *= ao;
-#endif // AMBIENT_LIGHT_DISABLED
- }
+ ambient_light *= albedo.rgb;
+ ambient_light *= ao;
+
+#endif // !AMBIENT_LIGHT_DISABLED
// convert ao to direct light ao
ao = mix(1.0, ao, ao_light_affect);
-
+#ifndef AMBIENT_LIGHT_DISABLED
{
#if defined(DIFFUSE_TOON)
//simplify for toon, as
@@ -1779,6 +1864,8 @@ void main() {
#endif
}
+#endif // !AMBIENT_LIGHT_DISABLED
+
#ifndef DISABLE_LIGHT_DIRECTIONAL
for (uint i = uint(0); i < scene_data.directional_light_count; i++) {
#if defined(USE_LIGHTMAP) && !defined(DISABLE_LIGHTMAP)
@@ -1871,7 +1958,7 @@ void main() {
alpha = min(alpha, clamp(length(ambient_light), 0.0, 1.0));
#if defined(ALPHA_SCISSOR_USED)
- if (alpha < alpha_scissor) {
+ if (alpha < alpha_scissor_threshold) {
discard;
}
#endif // !ALPHA_SCISSOR_USED
diff --git a/drivers/gles3/shaders/sky.glsl b/drivers/gles3/shaders/sky.glsl
index 9de65ba960..f734e4b355 100644
--- a/drivers/gles3/shaders/sky.glsl
+++ b/drivers/gles3/shaders/sky.glsl
@@ -108,11 +108,11 @@ uniform float sky_energy_multiplier;
uniform float luminance_multiplier;
uniform float fog_aerial_perspective;
-uniform vec3 fog_light_color;
+uniform vec4 fog_light_color;
uniform float fog_sun_scatter;
uniform bool fog_enabled;
uniform float fog_density;
-uniform float z_far;
+uniform float fog_sky_affect;
uniform uint directional_light_count;
#ifdef USE_MULTIVIEW
@@ -135,6 +135,24 @@ vec3 interleaved_gradient_noise(vec2 pos) {
}
#endif
+#if !defined(DISABLE_FOG)
+vec4 fog_process(vec3 view, vec3 sky_color) {
+ vec3 fog_color = mix(fog_light_color.rgb, sky_color, fog_aerial_perspective);
+
+ if (fog_sun_scatter > 0.001) {
+ vec4 sun_scatter = vec4(0.0);
+ float sun_total = 0.0;
+ for (uint i = 0u; i < directional_light_count; i++) {
+ vec3 light_color = directional_lights.data[i].color_size.xyz * directional_lights.data[i].direction_energy.w;
+ float light_amount = pow(max(dot(view, directional_lights.data[i].direction_energy.xyz), 0.0), 8.0);
+ fog_color += light_color * light_amount * fog_sun_scatter;
+ }
+ }
+
+ return vec4(fog_color, 1.0);
+}
+#endif // !DISABLE_FOG
+
void main() {
vec3 cube_normal;
#ifdef USE_MULTIVIEW
@@ -203,6 +221,21 @@ void main() {
// Convert to Linear for tonemapping so color matches scene shader better
color = srgb_to_linear(color);
+
+#if !defined(DISABLE_FOG) && !defined(USE_CUBEMAP_PASS)
+
+ // Draw "fixed" fog before volumetric fog to ensure volumetric fog can appear in front of the sky.
+ if (fog_enabled) {
+ vec4 fog = fog_process(cube_normal, color.rgb);
+ color.rgb = mix(color.rgb, fog.rgb, fog.a * fog_sky_affect);
+ }
+
+ if (custom_fog.a > 0.0) {
+ color.rgb = mix(color.rgb, custom_fog.rgb, custom_fog.a);
+ }
+
+#endif // DISABLE_FOG
+
color *= exposure;
#ifdef APPLY_TONEMAPPING
color = apply_tonemapping(color, white);
diff --git a/drivers/gles3/storage/light_storage.cpp b/drivers/gles3/storage/light_storage.cpp
index f9547502f4..aab1aadf02 100644
--- a/drivers/gles3/storage/light_storage.cpp
+++ b/drivers/gles3/storage/light_storage.cpp
@@ -1046,6 +1046,9 @@ void LightStorage::lightmap_set_textures(RID p_lightmap, RID p_light, bool p_use
lightmap->light_texture = p_light;
lightmap->uses_spherical_harmonics = p_uses_spherical_haromics;
+ Vector3i light_texture_size = GLES3::TextureStorage::get_singleton()->texture_get_size(lightmap->light_texture);
+ lightmap->light_texture_size = Vector2i(light_texture_size.x, light_texture_size.y);
+
GLuint tex = GLES3::TextureStorage::get_singleton()->texture_get_texid(lightmap->light_texture);
glBindTexture(GL_TEXTURE_2D_ARRAY, tex);
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
diff --git a/drivers/gles3/storage/light_storage.h b/drivers/gles3/storage/light_storage.h
index b6e64c9492..81e7220439 100644
--- a/drivers/gles3/storage/light_storage.h
+++ b/drivers/gles3/storage/light_storage.h
@@ -180,6 +180,7 @@ struct Lightmap {
bool interior = false;
AABB bounds = AABB(Vector3(), Vector3(1, 1, 1));
float baked_exposure = 1.0;
+ Vector2i light_texture_size;
int32_t array_index = -1; //unassigned
PackedVector3Array points;
PackedColorArray point_sh;
diff --git a/drivers/gles3/storage/material_storage.cpp b/drivers/gles3/storage/material_storage.cpp
index 41c23fc3ec..25af7ff691 100644
--- a/drivers/gles3/storage/material_storage.cpp
+++ b/drivers/gles3/storage/material_storage.cpp
@@ -586,11 +586,7 @@ void ShaderData::get_shader_uniform_list(List<PropertyInfo> *p_param_list) const
if (E.value.scope != ShaderLanguage::ShaderNode::Uniform::SCOPE_LOCAL) {
continue;
}
- if (E.value.texture_order >= 0) {
- filtered_uniforms.push_back(Pair<StringName, int>(E.key, E.value.texture_order + 100000));
- } else {
- filtered_uniforms.push_back(Pair<StringName, int>(E.key, E.value.order));
- }
+ filtered_uniforms.push_back(Pair<StringName, int>(E.key, E.value.prop_order));
}
int uniform_count = filtered_uniforms.size();
sorter.sort(filtered_uniforms.ptr(), uniform_count);
@@ -640,7 +636,7 @@ bool ShaderData::is_parameter_texture(const StringName &p_param) const {
return false;
}
- return uniforms[p_param].texture_order >= 0;
+ return uniforms[p_param].is_texture();
}
///////////////////////////////////////////////////////////////////////////
@@ -719,7 +715,7 @@ void MaterialData::update_uniform_buffer(const HashMap<StringName, ShaderLanguag
bool uses_global_buffer = false;
for (const KeyValue<StringName, ShaderLanguage::ShaderNode::Uniform> &E : p_uniforms) {
- if (E.value.order < 0) {
+ if (E.value.is_texture()) {
continue; // texture, does not go here
}
diff --git a/drivers/gles3/storage/texture_storage.cpp b/drivers/gles3/storage/texture_storage.cpp
index 3b1373c928..8251c8f52e 100644
--- a/drivers/gles3/storage/texture_storage.cpp
+++ b/drivers/gles3/storage/texture_storage.cpp
@@ -1680,6 +1680,14 @@ uint32_t TextureStorage::texture_get_texid(RID p_texture) const {
return texture->tex_id;
}
+Vector3i TextureStorage::texture_get_size(RID p_texture) const {
+ Texture *texture = texture_owner.get_or_null(p_texture);
+
+ ERR_FAIL_NULL_V(texture, Vector3i(0, 0, 0));
+
+ return Vector3i(texture->width, texture->height, texture->depth);
+}
+
uint32_t TextureStorage::texture_get_width(RID p_texture) const {
Texture *texture = texture_owner.get_or_null(p_texture);
diff --git a/drivers/gles3/storage/texture_storage.h b/drivers/gles3/storage/texture_storage.h
index 1b83efee32..5569abcc73 100644
--- a/drivers/gles3/storage/texture_storage.h
+++ b/drivers/gles3/storage/texture_storage.h
@@ -553,6 +553,7 @@ public:
void texture_set_data(RID p_texture, const Ref<Image> &p_image, int p_layer = 0);
virtual Image::Format texture_get_format(RID p_texture) const override;
uint32_t texture_get_texid(RID p_texture) const;
+ Vector3i texture_get_size(RID p_texture) const;
uint32_t texture_get_width(RID p_texture) const;
uint32_t texture_get_height(RID p_texture) const;
uint32_t texture_get_depth(RID p_texture) const;
diff --git a/editor/animation_track_editor_plugins.cpp b/editor/animation_track_editor_plugins.cpp
index 0297aaaf7f..d36e152ed9 100644
--- a/editor/animation_track_editor_plugins.cpp
+++ b/editor/animation_track_editor_plugins.cpp
@@ -353,9 +353,6 @@ void AnimationTrackEditAudio::set_node(Object *p_object) {
id = p_object->get_instance_id();
}
-void AnimationTrackEditAudio::_bind_methods() {
-}
-
AnimationTrackEditAudio::AnimationTrackEditAudio() {
AudioStreamPreviewGenerator::get_singleton()->connect("preview_updated", callable_mp(this, &AnimationTrackEditAudio::_preview_changed));
}
@@ -952,9 +949,6 @@ void AnimationTrackEditTypeAudio::draw_key(int p_index, float p_pixels_sec, int
}
}
-void AnimationTrackEditTypeAudio::_bind_methods() {
-}
-
AnimationTrackEditTypeAudio::AnimationTrackEditTypeAudio() {
AudioStreamPreviewGenerator::get_singleton()->connect("preview_updated", callable_mp(this, &AnimationTrackEditTypeAudio::_preview_changed));
}
diff --git a/editor/animation_track_editor_plugins.h b/editor/animation_track_editor_plugins.h
index 0e9ad44229..7ddbae1bbc 100644
--- a/editor/animation_track_editor_plugins.h
+++ b/editor/animation_track_editor_plugins.h
@@ -63,9 +63,6 @@ class AnimationTrackEditAudio : public AnimationTrackEdit {
void _preview_changed(ObjectID p_which);
-protected:
- static void _bind_methods();
-
public:
virtual int get_key_height() const override;
virtual Rect2 get_key_rect(int p_index, float p_pixels_sec) override;
@@ -121,9 +118,6 @@ class AnimationTrackEditTypeAudio : public AnimationTrackEdit {
float len_resizing_rel = 0.0f;
bool over_drag_position = false;
-protected:
- static void _bind_methods();
-
public:
virtual void gui_input(const Ref<InputEvent> &p_event) override;
diff --git a/editor/dependency_editor.cpp b/editor/dependency_editor.cpp
index c83e677b37..8ba5811ffa 100644
--- a/editor/dependency_editor.cpp
+++ b/editor/dependency_editor.cpp
@@ -237,9 +237,6 @@ void DependencyEditor::edit(const String &p_path) {
}
}
-void DependencyEditor::_bind_methods() {
-}
-
DependencyEditor::DependencyEditor() {
VBoxContainer *vb = memnew(VBoxContainer);
vb->set_name(TTR("Dependencies"));
@@ -353,9 +350,6 @@ void DependencyEditorOwners::_file_option(int p_option) {
}
}
-void DependencyEditorOwners::_bind_methods() {
-}
-
void DependencyEditorOwners::_fill_owners(EditorFileSystemDirectory *efsd) {
if (!efsd) {
return;
@@ -578,32 +572,34 @@ void DependencyRemoveDialog::ok_pressed() {
}
}
+ bool project_settings_modified = false;
for (int i = 0; i < files_to_delete.size(); ++i) {
// If the file we are deleting for e.g. the main scene, default environment,
// or audio bus layout, we must clear its definition in Project Settings.
if (files_to_delete[i] == String(GLOBAL_GET("application/config/icon"))) {
ProjectSettings::get_singleton()->set("application/config/icon", "");
- }
- if (files_to_delete[i] == String(GLOBAL_GET("application/run/main_scene"))) {
+ project_settings_modified = true;
+ } else if (files_to_delete[i] == String(GLOBAL_GET("application/run/main_scene"))) {
ProjectSettings::get_singleton()->set("application/run/main_scene", "");
- }
- if (files_to_delete[i] == String(GLOBAL_GET("application/boot_splash/image"))) {
+ project_settings_modified = true;
+ } else if (files_to_delete[i] == String(GLOBAL_GET("application/boot_splash/image"))) {
ProjectSettings::get_singleton()->set("application/boot_splash/image", "");
- }
- if (files_to_delete[i] == String(GLOBAL_GET("rendering/environment/defaults/default_environment"))) {
+ project_settings_modified = true;
+ } else if (files_to_delete[i] == String(GLOBAL_GET("rendering/environment/defaults/default_environment"))) {
ProjectSettings::get_singleton()->set("rendering/environment/defaults/default_environment", "");
- }
- if (files_to_delete[i] == String(GLOBAL_GET("display/mouse_cursor/custom_image"))) {
+ project_settings_modified = true;
+ } else if (files_to_delete[i] == String(GLOBAL_GET("display/mouse_cursor/custom_image"))) {
ProjectSettings::get_singleton()->set("display/mouse_cursor/custom_image", "");
- }
- if (files_to_delete[i] == String(GLOBAL_GET("gui/theme/custom"))) {
+ project_settings_modified = true;
+ } else if (files_to_delete[i] == String(GLOBAL_GET("gui/theme/custom"))) {
ProjectSettings::get_singleton()->set("gui/theme/custom", "");
- }
- if (files_to_delete[i] == String(GLOBAL_GET("gui/theme/custom_font"))) {
+ project_settings_modified = true;
+ } else if (files_to_delete[i] == String(GLOBAL_GET("gui/theme/custom_font"))) {
ProjectSettings::get_singleton()->set("gui/theme/custom_font", "");
- }
- if (files_to_delete[i] == String(GLOBAL_GET("audio/buses/default_bus_layout"))) {
+ project_settings_modified = true;
+ } else if (files_to_delete[i] == String(GLOBAL_GET("audio/buses/default_bus_layout"))) {
ProjectSettings::get_singleton()->set("audio/buses/default_bus_layout", "");
+ project_settings_modified = true;
}
String path = OS::get_singleton()->get_resource_dir() + files_to_delete[i].replace_first("res://", "/");
@@ -615,6 +611,9 @@ void DependencyRemoveDialog::ok_pressed() {
emit_signal(SNAME("file_removed"), files_to_delete[i]);
}
}
+ if (project_settings_modified) {
+ ProjectSettings::get_singleton()->save();
+ }
if (dirs_to_delete.size() == 0) {
// If we only deleted files we should only need to tell the file system about the files we touched.
@@ -865,9 +864,6 @@ void OrphanResourcesDialog::_button_pressed(Object *p_item, int p_column, int p_
dep_edit->edit(path);
}
-void OrphanResourcesDialog::_bind_methods() {
-}
-
OrphanResourcesDialog::OrphanResourcesDialog() {
set_title(TTR("Orphan Resource Explorer"));
delete_confirm = memnew(ConfirmationDialog);
diff --git a/editor/dependency_editor.h b/editor/dependency_editor.h
index d43b12f1d2..0256f39979 100644
--- a/editor/dependency_editor.h
+++ b/editor/dependency_editor.h
@@ -60,9 +60,6 @@ class DependencyEditor : public AcceptDialog {
void _update_file();
-protected:
- static void _bind_methods();
-
public:
void edit(const String &p_path);
DependencyEditor();
@@ -81,7 +78,6 @@ class DependencyEditorOwners : public AcceptDialog {
void _fill_owners(EditorFileSystemDirectory *efsd);
- static void _bind_methods();
void _list_rmb_clicked(int p_item, const Vector2 &p_pos, MouseButton p_mouse_button_index);
void _select_file(int p_idx);
void _empty_clicked(const Vector2 &p_pos, MouseButton p_mouse_button_index);
@@ -175,7 +171,6 @@ class OrphanResourcesDialog : public ConfirmationDialog {
void _button_pressed(Object *p_item, int p_column, int p_id, MouseButton p_button);
void refresh();
- static void _bind_methods();
public:
void show();
diff --git a/editor/doc_tools.cpp b/editor/doc_tools.cpp
index 331dacf6ad..bf5b717c19 100644
--- a/editor/doc_tools.cpp
+++ b/editor/doc_tools.cpp
@@ -38,7 +38,7 @@
#include "core/io/marshalls.h"
#include "core/io/resource_importer.h"
#include "core/object/script_language.h"
-#include "core/string/translation.h"
+#include "core/string/translation_server.h"
#include "editor/editor_settings.h"
#include "editor/export/editor_export.h"
#include "scene/resources/theme.h"
diff --git a/editor/editor_asset_installer.cpp b/editor/editor_asset_installer.cpp
index 33049b6bc2..b415c72c15 100644
--- a/editor/editor_asset_installer.cpp
+++ b/editor/editor_asset_installer.cpp
@@ -659,9 +659,6 @@ void EditorAssetInstaller::_notification(int p_what) {
}
}
-void EditorAssetInstaller::_bind_methods() {
-}
-
EditorAssetInstaller::EditorAssetInstaller() {
VBoxContainer *vb = memnew(VBoxContainer);
add_child(vb);
diff --git a/editor/editor_asset_installer.h b/editor/editor_asset_installer.h
index bc63bc9916..41f092f304 100644
--- a/editor/editor_asset_installer.h
+++ b/editor/editor_asset_installer.h
@@ -96,7 +96,6 @@ class EditorAssetInstaller : public ConfirmationDialog {
protected:
void _notification(int p_what);
- static void _bind_methods();
public:
void open_asset(const String &p_path, bool p_autoskip_toplevel = false);
diff --git a/editor/editor_autoload_settings.cpp b/editor/editor_autoload_settings.cpp
index b62351256e..32b2133f23 100644
--- a/editor/editor_autoload_settings.cpp
+++ b/editor/editor_autoload_settings.cpp
@@ -457,7 +457,9 @@ void EditorAutoloadSettings::init_autoloads() {
for (const AutoloadInfo &info : autoload_cache) {
if (info.node && info.in_editor) {
- callable_mp((Node *)get_tree()->get_root(), &Node::add_child).call_deferred(info.node, false, Node::INTERNAL_MODE_DISABLED);
+ // It's important to add the node without deferring because code in plugins or tool scripts
+ // could use the autoload node when they are enabled.
+ get_tree()->get_root()->add_child(info.node);
}
}
}
diff --git a/editor/editor_file_system.cpp b/editor/editor_file_system.cpp
index feca12b409..f75e438582 100644
--- a/editor/editor_file_system.cpp
+++ b/editor/editor_file_system.cpp
@@ -386,6 +386,8 @@ void EditorFileSystem::_scan_filesystem() {
// On the first scan, the first_scan_root_dir is created in _first_scan_filesystem.
if (first_scan) {
sd = first_scan_root_dir;
+ // Will be updated on scan.
+ ResourceUID::get_singleton()->clear();
} else {
Ref<DirAccess> d = DirAccess::create(DirAccess::ACCESS_RESOURCES);
sd = memnew(ScannedDirectory);
@@ -1779,7 +1781,9 @@ String EditorFileSystem::_get_global_script_class(const String &p_type, const St
void EditorFileSystem::_update_file_icon_path(EditorFileSystemDirectory::FileInfo *file_info) {
String icon_path;
- if (file_info->script_class_icon_path.is_empty() && !file_info->deps.is_empty()) {
+ if (file_info->resource_script_class != StringName()) {
+ icon_path = EditorNode::get_editor_data().script_class_get_icon_path(file_info->resource_script_class);
+ } else if (file_info->script_class_icon_path.is_empty() && !file_info->deps.is_empty()) {
const String &script_dep = file_info->deps[0]; // Assuming the first dependency is a script.
const String &script_path = script_dep.contains("::") ? script_dep.get_slice("::", 2) : script_dep;
if (!script_path.is_empty()) {
@@ -3059,7 +3063,6 @@ EditorFileSystem::EditorFileSystem() {
using_fat32_or_exfat = (da->get_filesystem_type() == "FAT32" || da->get_filesystem_type() == "exFAT");
scan_total = 0;
- callable_mp(ResourceUID::get_singleton(), &ResourceUID::clear).call_deferred(); // Will be updated on scan.
ResourceSaver::set_get_resource_id_for_path(_resource_saver_get_resource_id_for_path);
}
diff --git a/editor/editor_folding.cpp b/editor/editor_folding.cpp
index ae065aebf8..18f5610655 100644
--- a/editor/editor_folding.cpp
+++ b/editor/editor_folding.cpp
@@ -93,7 +93,7 @@ void EditorFolding::_fill_folds(const Node *p_root, const Node *p_node, Array &p
if (!p_node->get_owner()) {
return; //not owned, bye
}
- if (p_node->get_owner() != p_root && !p_root->is_editable_instance(p_node)) {
+ if (p_node->get_owner() != p_root && !p_root->is_editable_instance(p_node->get_owner())) {
return;
}
}
diff --git a/editor/editor_locale_dialog.cpp b/editor/editor_locale_dialog.cpp
index f8fd05bf1e..83f1c70c69 100644
--- a/editor/editor_locale_dialog.cpp
+++ b/editor/editor_locale_dialog.cpp
@@ -31,6 +31,7 @@
#include "editor_locale_dialog.h"
#include "core/config/project_settings.h"
+#include "core/string/translation_server.h"
#include "editor/editor_undo_redo_manager.h"
#include "editor/themes/editor_scale.h"
#include "scene/gui/check_button.h"
diff --git a/editor/editor_locale_dialog.h b/editor/editor_locale_dialog.h
index 467861c36b..bc75e1df59 100644
--- a/editor/editor_locale_dialog.h
+++ b/editor/editor_locale_dialog.h
@@ -31,7 +31,6 @@
#ifndef EDITOR_LOCALE_DIALOG_H
#define EDITOR_LOCALE_DIALOG_H
-#include "core/string/translation.h"
#include "scene/gui/dialogs.h"
class Button;
diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp
index f24fa344ae..ad95502a2b 100644
--- a/editor/editor_node.cpp
+++ b/editor/editor_node.cpp
@@ -42,7 +42,7 @@
#include "core/os/os.h"
#include "core/os/time.h"
#include "core/string/print_string.h"
-#include "core/string/translation.h"
+#include "core/string/translation_server.h"
#include "core/version.h"
#include "editor/editor_string_names.h"
#include "main/main.h"
@@ -429,6 +429,7 @@ void EditorNode::_update_from_settings() {
RS::get_singleton()->decals_set_filter(RS::DecalFilter(int(GLOBAL_GET("rendering/textures/decals/filter"))));
RS::get_singleton()->light_projectors_set_filter(RS::LightProjectorFilter(int(GLOBAL_GET("rendering/textures/light_projectors/filter"))));
+ RS::get_singleton()->lightmaps_set_bicubic_filter(GLOBAL_GET("rendering/lightmapping/lightmap_gi/use_bicubic_filter"));
SceneTree *tree = get_tree();
tree->set_debug_collisions_color(GLOBAL_GET("debug/shapes/collision/shape_color"));
@@ -5343,7 +5344,7 @@ bool EditorNode::ensure_main_scene(bool p_from_native) {
if (main_scene.is_empty()) {
current_menu_option = -1;
- pick_main_scene->set_text(TTR("No main scene has ever been defined, select one?\nYou can change it later in \"Project Settings\" under the 'application' category."));
+ pick_main_scene->set_text(TTR("No main scene has ever been defined. Select one?\nYou can change it later in \"Project Settings\" under the 'application' category."));
pick_main_scene->popup_centered();
if (editor_data.get_edited_scene_root()) {
@@ -5358,14 +5359,14 @@ bool EditorNode::ensure_main_scene(bool p_from_native) {
if (!FileAccess::exists(main_scene)) {
current_menu_option = -1;
- pick_main_scene->set_text(vformat(TTR("Selected scene '%s' does not exist, select a valid one?\nYou can change it later in \"Project Settings\" under the 'application' category."), main_scene));
+ pick_main_scene->set_text(vformat(TTR("Selected scene '%s' does not exist. Select a valid one?\nYou can change it later in \"Project Settings\" under the 'application' category."), main_scene));
pick_main_scene->popup_centered();
return false;
}
if (ResourceLoader::get_resource_type(main_scene) != "PackedScene") {
current_menu_option = -1;
- pick_main_scene->set_text(vformat(TTR("Selected scene '%s' is not a scene file, select a valid one?\nYou can change it later in \"Project Settings\" under the 'application' category."), main_scene));
+ pick_main_scene->set_text(vformat(TTR("Selected scene '%s' is not a scene file. Select a valid one?\nYou can change it later in \"Project Settings\" under the 'application' category."), main_scene));
pick_main_scene->popup_centered();
return false;
}
diff --git a/editor/editor_properties.cpp b/editor/editor_properties.cpp
index fdb4ec170b..19a4165041 100644
--- a/editor/editor_properties.cpp
+++ b/editor/editor_properties.cpp
@@ -129,9 +129,6 @@ void EditorPropertyText::set_placeholder(const String &p_string) {
text->set_placeholder(p_string);
}
-void EditorPropertyText::_bind_methods() {
-}
-
EditorPropertyText::EditorPropertyText() {
HBoxContainer *hb = memnew(HBoxContainer);
add_child(hb);
@@ -219,9 +216,6 @@ void EditorPropertyMultilineText::_notification(int p_what) {
}
}
-void EditorPropertyMultilineText::_bind_methods() {
-}
-
EditorPropertyMultilineText::EditorPropertyMultilineText(bool p_expression) {
HBoxContainer *hb = memnew(HBoxContainer);
hb->add_theme_constant_override("separation", 0);
@@ -343,9 +337,6 @@ void EditorPropertyTextEnum::setup(const Vector<String> &p_options, bool p_strin
}
}
-void EditorPropertyTextEnum::_bind_methods() {
-}
-
void EditorPropertyTextEnum::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_ENTER_TREE:
@@ -447,9 +438,6 @@ void EditorPropertyLocale::_locale_focus_exited() {
_locale_selected(locale->get_text());
}
-void EditorPropertyLocale::_bind_methods() {
-}
-
EditorPropertyLocale::EditorPropertyLocale() {
HBoxContainer *locale_hb = memnew(HBoxContainer);
add_child(locale_hb);
@@ -586,9 +574,6 @@ bool EditorPropertyPath::_can_drop_data_fw(const Point2 &p_point, const Variant
return false;
}
-void EditorPropertyPath::_bind_methods() {
-}
-
EditorPropertyPath::EditorPropertyPath() {
HBoxContainer *path_hb = memnew(HBoxContainer);
add_child(path_hb);
@@ -637,9 +622,6 @@ void EditorPropertyClassName::_dialog_created() {
update_property();
}
-void EditorPropertyClassName::_bind_methods() {
-}
-
EditorPropertyClassName::EditorPropertyClassName() {
property = memnew(Button);
property->set_clip_text(true);
@@ -669,9 +651,6 @@ void EditorPropertyCheck::update_property() {
checkbox->set_disabled(is_read_only());
}
-void EditorPropertyCheck::_bind_methods() {
-}
-
EditorPropertyCheck::EditorPropertyCheck() {
checkbox = memnew(CheckBox);
checkbox->set_text(TTR("On"));
@@ -725,9 +704,6 @@ void EditorPropertyEnum::set_option_button_clip(bool p_enable) {
options->set_clip_text(p_enable);
}
-void EditorPropertyEnum::_bind_methods() {
-}
-
EditorPropertyEnum::EditorPropertyEnum() {
options = memnew(OptionButton);
options->set_clip_text(true);
@@ -804,9 +780,6 @@ void EditorPropertyFlags::setup(const Vector<String> &p_options) {
}
}
-void EditorPropertyFlags::_bind_methods() {
-}
-
EditorPropertyFlags::EditorPropertyFlags() {
vbox = memnew(VBoxContainer);
add_child(vbox);
@@ -1280,9 +1253,6 @@ void EditorPropertyLayers::_refresh_names() {
setup(layer_type);
}
-void EditorPropertyLayers::_bind_methods() {
-}
-
EditorPropertyLayers::EditorPropertyLayers() {
HBoxContainer *hb = memnew(HBoxContainer);
hb->set_clip_contents(true);
@@ -1330,9 +1300,6 @@ void EditorPropertyInteger::update_property() {
#endif
}
-void EditorPropertyInteger::_bind_methods() {
-}
-
void EditorPropertyInteger::setup(int64_t p_min, int64_t p_max, int64_t p_step, bool p_hide_slider, bool p_allow_greater, bool p_allow_lesser, const String &p_suffix) {
spin->set_min(p_min);
spin->set_max(p_max);
@@ -1385,9 +1352,6 @@ void EditorPropertyObjectID::setup(const String &p_base_type) {
base_type = p_base_type;
}
-void EditorPropertyObjectID::_bind_methods() {
-}
-
EditorPropertyObjectID::EditorPropertyObjectID() {
edit = memnew(Button);
add_child(edit);
@@ -1413,9 +1377,6 @@ void EditorPropertySignal::update_property() {
edit->set_icon(get_editor_theme_icon(SNAME("Signals")));
}
-void EditorPropertySignal::_bind_methods() {
-}
-
EditorPropertySignal::EditorPropertySignal() {
edit = memnew(Button);
add_child(edit);
@@ -1435,9 +1396,6 @@ void EditorPropertyCallable::update_property() {
edit->set_icon(get_editor_theme_icon(SNAME("Callable")));
}
-void EditorPropertyCallable::_bind_methods() {
-}
-
EditorPropertyCallable::EditorPropertyCallable() {
edit = memnew(Button);
add_child(edit);
@@ -1465,9 +1423,6 @@ void EditorPropertyFloat::update_property() {
spin->set_value_no_signal(val);
}
-void EditorPropertyFloat::_bind_methods() {
-}
-
void EditorPropertyFloat::setup(double p_min, double p_max, double p_step, bool p_hide_slider, bool p_exp_range, bool p_greater, bool p_lesser, const String &p_suffix, bool p_radians_as_degrees) {
radians_as_degrees = p_radians_as_degrees;
spin->set_min(p_min);
@@ -1675,9 +1630,6 @@ void EditorPropertyEasing::_notification(int p_what) {
}
}
-void EditorPropertyEasing::_bind_methods() {
-}
-
EditorPropertyEasing::EditorPropertyEasing() {
easing_draw = memnew(Control);
easing_draw->connect(SceneStringName(draw), callable_mp(this, &EditorPropertyEasing::_draw_easing));
@@ -1740,9 +1692,6 @@ void EditorPropertyRect2::_notification(int p_what) {
}
}
-void EditorPropertyRect2::_bind_methods() {
-}
-
void EditorPropertyRect2::setup(double p_min, double p_max, double p_step, bool p_hide_slider, const String &p_suffix) {
for (int i = 0; i < 4; i++) {
spin[i]->set_min(p_min);
@@ -1837,9 +1786,6 @@ void EditorPropertyRect2i::_notification(int p_what) {
}
}
-void EditorPropertyRect2i::_bind_methods() {
-}
-
void EditorPropertyRect2i::setup(int p_min, int p_max, const String &p_suffix) {
for (int i = 0; i < 4; i++) {
spin[i]->set_min(p_min);
@@ -1933,9 +1879,6 @@ void EditorPropertyPlane::_notification(int p_what) {
}
}
-void EditorPropertyPlane::_bind_methods() {
-}
-
void EditorPropertyPlane::setup(double p_min, double p_max, double p_step, bool p_hide_slider, const String &p_suffix) {
for (int i = 0; i < 4; i++) {
spin[i]->set_min(p_min);
@@ -2085,9 +2028,6 @@ void EditorPropertyQuaternion::_notification(int p_what) {
}
}
-void EditorPropertyQuaternion::_bind_methods() {
-}
-
void EditorPropertyQuaternion::setup(double p_min, double p_max, double p_step, bool p_hide_slider, const String &p_suffix, bool p_hide_editor) {
for (int i = 0; i < 4; i++) {
spin[i]->set_min(p_min);
@@ -2232,9 +2172,6 @@ void EditorPropertyAABB::_notification(int p_what) {
}
}
-void EditorPropertyAABB::_bind_methods() {
-}
-
void EditorPropertyAABB::setup(double p_min, double p_max, double p_step, bool p_hide_slider, const String &p_suffix) {
for (int i = 0; i < 6; i++) {
spin[i]->set_min(p_min);
@@ -2313,9 +2250,6 @@ void EditorPropertyTransform2D::_notification(int p_what) {
}
}
-void EditorPropertyTransform2D::_bind_methods() {
-}
-
void EditorPropertyTransform2D::setup(double p_min, double p_max, double p_step, bool p_hide_slider, const String &p_suffix) {
for (int i = 0; i < 6; i++) {
spin[i]->set_min(p_min);
@@ -2398,9 +2332,6 @@ void EditorPropertyBasis::_notification(int p_what) {
}
}
-void EditorPropertyBasis::_bind_methods() {
-}
-
void EditorPropertyBasis::setup(double p_min, double p_max, double p_step, bool p_hide_slider, const String &p_suffix) {
for (int i = 0; i < 9; i++) {
spin[i]->set_min(p_min);
@@ -2490,9 +2421,6 @@ void EditorPropertyTransform3D::_notification(int p_what) {
}
}
-void EditorPropertyTransform3D::_bind_methods() {
-}
-
void EditorPropertyTransform3D::setup(double p_min, double p_max, double p_step, bool p_hide_slider, const String &p_suffix) {
for (int i = 0; i < 12; i++) {
spin[i]->set_min(p_min);
@@ -2590,9 +2518,6 @@ void EditorPropertyProjection::_notification(int p_what) {
}
}
-void EditorPropertyProjection::_bind_methods() {
-}
-
void EditorPropertyProjection::setup(double p_min, double p_max, double p_step, bool p_hide_slider, const String &p_suffix) {
for (int i = 0; i < 16; i++) {
spin[i]->set_min(p_min);
diff --git a/editor/editor_properties.h b/editor/editor_properties.h
index e9e788ab7b..2ec78cdb44 100644
--- a/editor/editor_properties.h
+++ b/editor/editor_properties.h
@@ -66,7 +66,6 @@ class EditorPropertyText : public EditorProperty {
protected:
virtual void _set_read_only(bool p_read_only) override;
- static void _bind_methods();
public:
void set_string_name(bool p_enabled);
@@ -92,7 +91,6 @@ class EditorPropertyMultilineText : public EditorProperty {
protected:
virtual void _set_read_only(bool p_read_only) override;
void _notification(int p_what);
- static void _bind_methods();
public:
virtual void update_property() override;
@@ -126,7 +124,6 @@ class EditorPropertyTextEnum : public EditorProperty {
protected:
virtual void _set_read_only(bool p_read_only) override;
- static void _bind_methods();
void _notification(int p_what);
public:
@@ -153,7 +150,6 @@ class EditorPropertyPath : public EditorProperty {
protected:
virtual void _set_read_only(bool p_read_only) override;
- static void _bind_methods();
void _notification(int p_what);
public:
@@ -174,7 +170,6 @@ class EditorPropertyLocale : public EditorProperty {
void _locale_focus_exited();
protected:
- static void _bind_methods();
void _notification(int p_what);
public:
@@ -196,7 +191,6 @@ private:
protected:
virtual void _set_read_only(bool p_read_only) override;
- static void _bind_methods();
public:
void setup(const String &p_base_type, const String &p_selected_type);
@@ -212,7 +206,6 @@ class EditorPropertyCheck : public EditorProperty {
protected:
virtual void _set_read_only(bool p_read_only) override;
- static void _bind_methods();
public:
virtual void update_property() override;
@@ -227,7 +220,6 @@ class EditorPropertyEnum : public EditorProperty {
protected:
virtual void _set_read_only(bool p_read_only) override;
- static void _bind_methods();
public:
void setup(const Vector<String> &p_options);
@@ -246,7 +238,6 @@ class EditorPropertyFlags : public EditorProperty {
protected:
virtual void _set_read_only(bool p_read_only) override;
- static void _bind_methods();
public:
void setup(const Vector<String> &p_options);
@@ -327,7 +318,6 @@ private:
protected:
void _notification(int p_what);
virtual void _set_read_only(bool p_read_only) override;
- static void _bind_methods();
public:
void setup(LayerType p_layer_type);
@@ -344,7 +334,6 @@ class EditorPropertyInteger : public EditorProperty {
protected:
virtual void _set_read_only(bool p_read_only) override;
- static void _bind_methods();
public:
virtual void update_property() override;
@@ -360,7 +349,6 @@ class EditorPropertyObjectID : public EditorProperty {
protected:
virtual void _set_read_only(bool p_read_only) override;
- static void _bind_methods();
public:
virtual void update_property() override;
@@ -374,9 +362,6 @@ class EditorPropertySignal : public EditorProperty {
String base_type;
void _edit_pressed();
-protected:
- static void _bind_methods();
-
public:
virtual void update_property() override;
EditorPropertySignal();
@@ -387,9 +372,6 @@ class EditorPropertyCallable : public EditorProperty {
Button *edit = nullptr;
String base_type;
-protected:
- static void _bind_methods();
-
public:
virtual void update_property() override;
EditorPropertyCallable();
@@ -403,7 +385,6 @@ class EditorPropertyFloat : public EditorProperty {
protected:
virtual void _set_read_only(bool p_read_only) override;
- static void _bind_methods();
public:
virtual void update_property() override;
@@ -445,7 +426,6 @@ class EditorPropertyEasing : public EditorProperty {
protected:
virtual void _set_read_only(bool p_read_only) override;
- static void _bind_methods();
public:
virtual void update_property() override;
@@ -461,7 +441,6 @@ class EditorPropertyRect2 : public EditorProperty {
protected:
virtual void _set_read_only(bool p_read_only) override;
void _notification(int p_what);
- static void _bind_methods();
public:
virtual void update_property() override;
@@ -477,7 +456,6 @@ class EditorPropertyRect2i : public EditorProperty {
protected:
virtual void _set_read_only(bool p_read_only) override;
void _notification(int p_what);
- static void _bind_methods();
public:
virtual void update_property() override;
@@ -493,7 +471,6 @@ class EditorPropertyPlane : public EditorProperty {
protected:
virtual void _set_read_only(bool p_read_only) override;
void _notification(int p_what);
- static void _bind_methods();
public:
virtual void update_property() override;
@@ -526,7 +503,6 @@ class EditorPropertyQuaternion : public EditorProperty {
protected:
virtual void _set_read_only(bool p_read_only) override;
void _notification(int p_what);
- static void _bind_methods();
public:
virtual void update_property() override;
@@ -542,7 +518,6 @@ class EditorPropertyAABB : public EditorProperty {
protected:
virtual void _set_read_only(bool p_read_only) override;
void _notification(int p_what);
- static void _bind_methods();
public:
virtual void update_property() override;
@@ -558,7 +533,6 @@ class EditorPropertyTransform2D : public EditorProperty {
protected:
virtual void _set_read_only(bool p_read_only) override;
void _notification(int p_what);
- static void _bind_methods();
public:
virtual void update_property() override;
@@ -574,7 +548,6 @@ class EditorPropertyBasis : public EditorProperty {
protected:
virtual void _set_read_only(bool p_read_only) override;
void _notification(int p_what);
- static void _bind_methods();
public:
virtual void update_property() override;
@@ -590,7 +563,6 @@ class EditorPropertyTransform3D : public EditorProperty {
protected:
virtual void _set_read_only(bool p_read_only) override;
void _notification(int p_what);
- static void _bind_methods();
public:
virtual void update_property() override;
@@ -607,7 +579,6 @@ class EditorPropertyProjection : public EditorProperty {
protected:
virtual void _set_read_only(bool p_read_only) override;
void _notification(int p_what);
- static void _bind_methods();
public:
virtual void update_property() override;
diff --git a/editor/editor_properties_array_dict.cpp b/editor/editor_properties_array_dict.cpp
index 9aa2390230..d58d0520cc 100644
--- a/editor/editor_properties_array_dict.cpp
+++ b/editor/editor_properties_array_dict.cpp
@@ -836,9 +836,6 @@ bool EditorPropertyArray::is_colored(ColorationMode p_mode) {
return p_mode == COLORATION_CONTAINER_RESOURCE;
}
-void EditorPropertyArray::_bind_methods() {
-}
-
EditorPropertyArray::EditorPropertyArray() {
object.instantiate();
page_length = int(EDITOR_GET("interface/inspector/max_array_dictionary_items_per_page"));
@@ -1166,9 +1163,6 @@ void EditorPropertyDictionary::_page_changed(int p_page) {
update_property();
}
-void EditorPropertyDictionary::_bind_methods() {
-}
-
bool EditorPropertyDictionary::is_colored(ColorationMode p_mode) {
return p_mode == COLORATION_CONTAINER_RESOURCE;
}
@@ -1385,9 +1379,6 @@ void EditorPropertyLocalizableString::_page_changed(int p_page) {
update_property();
}
-void EditorPropertyLocalizableString::_bind_methods() {
-}
-
EditorPropertyLocalizableString::EditorPropertyLocalizableString() {
object.instantiate();
page_length = int(EDITOR_GET("interface/inspector/max_array_dictionary_items_per_page"));
diff --git a/editor/editor_properties_array_dict.h b/editor/editor_properties_array_dict.h
index acde766754..024c04956f 100644
--- a/editor/editor_properties_array_dict.h
+++ b/editor/editor_properties_array_dict.h
@@ -150,7 +150,6 @@ protected:
bool updating = false;
bool dropping = false;
- static void _bind_methods();
void _notification(int p_what);
virtual void _add_element();
@@ -234,7 +233,6 @@ class EditorPropertyDictionary : public EditorProperty {
void _object_id_selected(const StringName &p_property, ObjectID p_id);
protected:
- static void _bind_methods();
void _notification(int p_what);
public:
@@ -271,7 +269,6 @@ class EditorPropertyLocalizableString : public EditorProperty {
void _object_id_selected(const StringName &p_property, ObjectID p_id);
protected:
- static void _bind_methods();
void _notification(int p_what);
public:
diff --git a/editor/editor_property_name_processor.cpp b/editor/editor_property_name_processor.cpp
index f23cab676c..7b45ba6a9f 100644
--- a/editor/editor_property_name_processor.cpp
+++ b/editor/editor_property_name_processor.cpp
@@ -30,7 +30,7 @@
#include "editor_property_name_processor.h"
-#include "core/string/translation.h"
+#include "core/string/translation_server.h"
#include "editor_settings.h"
EditorPropertyNameProcessor *EditorPropertyNameProcessor::singleton = nullptr;
diff --git a/editor/editor_resource_picker.cpp b/editor/editor_resource_picker.cpp
index 4cd44e3020..8935b9ad8a 100644
--- a/editor/editor_resource_picker.cpp
+++ b/editor/editor_resource_picker.cpp
@@ -618,9 +618,9 @@ void EditorResourcePicker::_ensure_allowed_types() const {
const String base = allowed_types[i].strip_edges();
if (base == "BaseMaterial3D") {
allowed_types_with_convert.insert("Texture2D");
- } else if (base == "ShaderMaterial") {
+ } else if (ClassDB::is_parent_class("ShaderMaterial", base)) {
allowed_types_with_convert.insert("Shader");
- } else if (base == "Texture2D") {
+ } else if (ClassDB::is_parent_class("ImageTexture", base)) {
allowed_types_with_convert.insert("Image");
}
}
diff --git a/editor/editor_settings.cpp b/editor/editor_settings.cpp
index 0956d12236..b9d530353c 100644
--- a/editor/editor_settings.cpp
+++ b/editor/editor_settings.cpp
@@ -43,7 +43,7 @@
#include "core/object/class_db.h"
#include "core/os/keyboard.h"
#include "core/os/os.h"
-#include "core/string/translation.h"
+#include "core/string/translation_server.h"
#include "core/version.h"
#include "editor/editor_node.h"
#include "editor/editor_paths.h"
@@ -65,7 +65,7 @@ bool EditorSettings::_set(const StringName &p_name, const Variant &p_value) {
_THREAD_SAFE_METHOD_
bool changed = _set_only(p_name, p_value);
- if (changed) {
+ if (changed && initialized) {
changed_settings.insert(p_name);
emit_signal(SNAME("settings_changed"));
}
@@ -330,6 +330,10 @@ bool EditorSettings::has_default_value(const String &p_setting) const {
return props[p_setting].has_default_value;
}
+void EditorSettings::_set_initialized() {
+ initialized = true;
+}
+
void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) {
_THREAD_SAFE_METHOD_
// Sets up the editor setting with a default value and hint PropertyInfo.
@@ -440,6 +444,7 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) {
#endif
EDITOR_SETTING(Variant::INT, PROPERTY_HINT_ENUM, "interface/editor/font_subpixel_positioning", 1, "Disabled,Auto,One Half of a Pixel,One Quarter of a Pixel")
EDITOR_SETTING(Variant::BOOL, PROPERTY_HINT_NONE, "interface/editor/font_disable_embedded_bitmaps", true, "");
+ EDITOR_SETTING(Variant::BOOL, PROPERTY_HINT_NONE, "interface/editor/font_allow_msdf", true, "")
EDITOR_SETTING(Variant::STRING, PROPERTY_HINT_GLOBAL_FILE, "interface/editor/main_font", "", "*.ttf,*.otf,*.woff,*.woff2,*.pfb,*.pfm")
EDITOR_SETTING(Variant::STRING, PROPERTY_HINT_GLOBAL_FILE, "interface/editor/main_font_bold", "", "*.ttf,*.otf,*.woff,*.woff2,*.pfb,*.pfm")
@@ -1926,7 +1931,5 @@ EditorSettings::EditorSettings() {
last_order = 0;
_load_defaults();
-}
-
-EditorSettings::~EditorSettings() {
+ callable_mp(this, &EditorSettings::_set_initialized).call_deferred();
}
diff --git a/editor/editor_settings.h b/editor/editor_settings.h
index 62ac0c60f3..6338f9105c 100644
--- a/editor/editor_settings.h
+++ b/editor/editor_settings.h
@@ -98,6 +98,7 @@ private:
bool save_changed_setting = true;
bool optimize_save = true; //do not save stuff that came from config but was not set from engine
+ bool initialized = false;
bool _set(const StringName &p_name, const Variant &p_value);
bool _set_only(const StringName &p_name, const Variant &p_value);
@@ -108,6 +109,7 @@ private:
bool _property_can_revert(const StringName &p_name) const;
bool _property_get_revert(const StringName &p_name, Variant &r_property) const;
+ void _set_initialized();
void _load_defaults(Ref<ConfigFile> p_extra_config = Ref<ConfigFile>());
void _load_godot2_text_editor_theme();
void _load_default_visual_shader_editor_theme();
@@ -196,7 +198,6 @@ public:
#endif
EditorSettings();
- ~EditorSettings();
};
//not a macro any longer
diff --git a/editor/editor_translation.cpp b/editor/editor_translation.cpp
index 77154ec344..4654d41082 100644
--- a/editor/editor_translation.cpp
+++ b/editor/editor_translation.cpp
@@ -33,6 +33,7 @@
#include "core/io/compression.h"
#include "core/io/file_access_memory.h"
#include "core/io/translation_loader_po.h"
+#include "core/string/translation_server.h"
#include "editor/doc_translations.gen.h"
#include "editor/editor_translations.gen.h"
#include "editor/extractable_translations.gen.h"
diff --git a/editor/export/export_template_manager.cpp b/editor/export/export_template_manager.cpp
index a6f6c87ee1..0caf0ee066 100644
--- a/editor/export/export_template_manager.cpp
+++ b/editor/export/export_template_manager.cpp
@@ -859,9 +859,6 @@ void ExportTemplateManager::_notification(int p_what) {
}
}
-void ExportTemplateManager::_bind_methods() {
-}
-
ExportTemplateManager::ExportTemplateManager() {
set_title(TTR("Export Template Manager"));
set_hide_on_ok(false);
diff --git a/editor/export/export_template_manager.h b/editor/export/export_template_manager.h
index a00d874580..b1c5855878 100644
--- a/editor/export/export_template_manager.h
+++ b/editor/export/export_template_manager.h
@@ -119,7 +119,6 @@ class ExportTemplateManager : public AcceptDialog {
protected:
void _notification(int p_what);
- static void _bind_methods();
public:
static String get_android_build_directory(const Ref<EditorExportPreset> &p_preset);
diff --git a/editor/filesystem_dock.cpp b/editor/filesystem_dock.cpp
index d4bd97a393..a9de8e3bc5 100644
--- a/editor/filesystem_dock.cpp
+++ b/editor/filesystem_dock.cpp
@@ -685,7 +685,15 @@ void FileSystemDock::_tree_multi_selected(Object *p_item, int p_column, bool p_s
}
Vector<String> FileSystemDock::get_selected_paths() const {
- return _tree_get_selected(false);
+ if (display_mode == DISPLAY_MODE_TREE_ONLY) {
+ return _tree_get_selected(false);
+ } else {
+ Vector<String> selected = _file_list_get_selected();
+ if (selected.is_empty()) {
+ selected.push_back(get_current_directory());
+ }
+ return selected;
+ }
}
String FileSystemDock::get_current_path() const {
@@ -953,7 +961,8 @@ void FileSystemDock::_update_file_list(bool p_keep_selection) {
files->set_max_columns(1);
files->set_max_text_lines(1);
files->set_fixed_column_width(0);
- files->set_fixed_icon_size(Size2());
+ const int icon_size = get_theme_constant(SNAME("class_icon_size"), EditorStringName(Editor));
+ files->set_fixed_icon_size(Size2(icon_size, icon_size));
}
Ref<Texture2D> folder_icon = (use_thumbnails) ? folder_thumbnail : get_theme_icon(SNAME("folder"), SNAME("FileDialog"));
@@ -2049,6 +2058,15 @@ Vector<String> FileSystemDock::_tree_get_selected(bool remove_self_inclusion, bo
return selected_strings;
}
+Vector<String> FileSystemDock::_file_list_get_selected() const {
+ Vector<String> selected;
+
+ for (int idx : files->get_selected_items()) {
+ selected.push_back(files->get_item_metadata(idx));
+ }
+ return selected;
+}
+
Vector<String> FileSystemDock::_remove_self_included_paths(Vector<String> selected_strings) {
// Remove paths or files that are included into another.
if (selected_strings.size() > 1) {
diff --git a/editor/filesystem_dock.h b/editor/filesystem_dock.h
index 959ace8eba..2f54cb91db 100644
--- a/editor/filesystem_dock.h
+++ b/editor/filesystem_dock.h
@@ -359,6 +359,7 @@ private:
void _update_display_mode(bool p_force = false);
Vector<String> _tree_get_selected(bool remove_self_inclusion = true, bool p_include_unselected_cursor = false) const;
+ Vector<String> _file_list_get_selected() const;
bool _is_file_type_disabled_by_feature_profile(const StringName &p_class);
diff --git a/editor/gui/editor_object_selector.cpp b/editor/gui/editor_object_selector.cpp
index 5b303760b0..bfd7e18de1 100644
--- a/editor/gui/editor_object_selector.cpp
+++ b/editor/gui/editor_object_selector.cpp
@@ -216,9 +216,6 @@ void EditorObjectSelector::_notification(int p_what) {
}
}
-void EditorObjectSelector::_bind_methods() {
-}
-
EditorObjectSelector::EditorObjectSelector(EditorSelectionHistory *p_history) {
history = p_history;
diff --git a/editor/gui/editor_object_selector.h b/editor/gui/editor_object_selector.h
index 72ff285cf6..60d6df488b 100644
--- a/editor/gui/editor_object_selector.h
+++ b/editor/gui/editor_object_selector.h
@@ -58,7 +58,6 @@ class EditorObjectSelector : public Button {
protected:
void _notification(int p_what);
- static void _bind_methods();
public:
virtual Size2 get_minimum_size() const override;
diff --git a/editor/gui/editor_spin_slider.cpp b/editor/gui/editor_spin_slider.cpp
index 4e8d6d63bf..9f9bdb37b3 100644
--- a/editor/gui/editor_spin_slider.cpp
+++ b/editor/gui/editor_spin_slider.cpp
@@ -614,13 +614,13 @@ void EditorSpinSlider::_value_focus_exited() {
// -> TAB was pressed
// -> modal_close was not called
// -> need to close/hide manually
- if (value_input_closed_frame != Engine::get_singleton()->get_frames_drawn()) {
+ if (!is_visible_in_tree() || value_input_closed_frame != Engine::get_singleton()->get_frames_drawn()) {
+ // Hidden or something else took focus.
if (value_input_popup) {
value_input_popup->hide();
}
- //tab was pressed
} else {
- //enter, click, esc
+ // Enter or Esc was pressed.
grab_focus();
}
diff --git a/editor/gui/scene_tree_editor.cpp b/editor/gui/scene_tree_editor.cpp
index c6cc0e97dd..87d8ddad09 100644
--- a/editor/gui/scene_tree_editor.cpp
+++ b/editor/gui/scene_tree_editor.cpp
@@ -1000,6 +1000,7 @@ void SceneTreeEditor::set_selected(Node *p_node, bool p_emit_selected) {
TreeItem *item = p_node ? _find(tree->get_root(), p_node->get_path()) : nullptr;
if (item) {
+ selected = p_node;
if (auto_expand_selected) {
// Make visible when it's collapsed.
TreeItem *node = item->get_parent();
@@ -1009,8 +1010,24 @@ void SceneTreeEditor::set_selected(Node *p_node, bool p_emit_selected) {
}
item->select(0);
item->set_as_cursor(0);
- selected = p_node;
tree->ensure_cursor_is_visible();
+ } else {
+ // Ensure the node is selected and visible for the user if the node
+ // is not collapsed.
+ bool collapsed = false;
+ TreeItem *node = item;
+ while (node && node != tree->get_root()) {
+ if (node->is_collapsed()) {
+ collapsed = true;
+ break;
+ }
+ node = node->get_parent();
+ }
+ if (!collapsed) {
+ item->select(0);
+ item->set_as_cursor(0);
+ tree->ensure_cursor_is_visible();
+ }
}
} else {
if (!p_node) {
diff --git a/editor/icons/AudioStreamPlayer.svg b/editor/icons/AudioStreamPlayer.svg
index a3c4ad8e35..561e1c3bb0 100644
--- a/editor/icons/AudioStreamPlayer.svg
+++ b/editor/icons/AudioStreamPlayer.svg
@@ -1 +1 @@
-<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" width="16" height="16" version="1.0" viewBox="0 0 2.4 2.4"><path fill="#e0e0e0" stroke="#e0e0e0" stroke-linejoin="round" stroke-width=".176" d="M1.382.335.777.858H.204v.673h.564l.614.531Z"/><path fill="none" stroke="#e0e0e0" stroke-linecap="round" stroke-width=".176" d="M1.718.572a1.06 1.06 0 0 1 0 1.256M1.947.343c.402.5.402 1.213 0 1.714"/></svg> \ No newline at end of file
+<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" width="16" height="16" version="1.0" viewBox="0 0 2.4 2.4"><path fill="#e0e0e0" d="M1.252.15a.1.1 0 0 0-.082.03L.6.75H.318C.225.75.15.817.15.9v.6c0 .083.075.15.168.15H.6l.57.57c.066.067.18.02.18-.074V.256A.106.106 0 0 0 1.252.15" paint-order="markers stroke fill"/><path fill="none" stroke="#e0e0e0" stroke-linecap="round" stroke-linejoin="round" stroke-width=".165" d="M1.575.675c.45.525 0 1.05 0 1.05m.3-1.35c.675.825 0 1.65 0 1.65" paint-order="markers stroke fill"/></svg> \ No newline at end of file
diff --git a/editor/icons/AudioStreamPlayer2D.svg b/editor/icons/AudioStreamPlayer2D.svg
index fa60e30238..7f557b2cfa 100644
--- a/editor/icons/AudioStreamPlayer2D.svg
+++ b/editor/icons/AudioStreamPlayer2D.svg
@@ -1 +1 @@
-<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" width="16" height="16" version="1.0" viewBox="0 0 2.4 2.4"><path fill="#8da5f3" stroke="#8da5f3" stroke-linejoin="round" stroke-width=".176" d="M1.382.335.777.858H.204v.673h.564l.614.531Z"/><path fill="none" stroke="#8da5f3" stroke-linecap="round" stroke-width=".176" d="M1.718.572a1.06 1.06 0 0 1 0 1.256M1.947.343c.402.5.402 1.213 0 1.714"/></svg> \ No newline at end of file
+<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" width="16" height="16" version="1.0" viewBox="0 0 2.4 2.4"><path fill="#8da5f3" d="M1.252.15a.1.1 0 0 0-.082.03L.6.75H.318C.225.75.15.817.15.9v.6c0 .083.075.15.168.15H.6l.57.57c.066.067.18.02.18-.074V.256A.106.106 0 0 0 1.252.15" paint-order="markers stroke fill"/><path fill="none" stroke="#8da5f3" stroke-linecap="round" stroke-linejoin="round" stroke-width=".165" d="M1.575.675c.45.525 0 1.05 0 1.05m.3-1.35c.675.825 0 1.65 0 1.65" paint-order="markers stroke fill"/></svg> \ No newline at end of file
diff --git a/editor/icons/AudioStreamPlayer3D.svg b/editor/icons/AudioStreamPlayer3D.svg
index f6be929e61..5f07d68eec 100644
--- a/editor/icons/AudioStreamPlayer3D.svg
+++ b/editor/icons/AudioStreamPlayer3D.svg
@@ -1 +1 @@
-<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" width="16" height="16" version="1.0" viewBox="0 0 2.4 2.4"><path fill="#fc7f7f" stroke="#fc7f7f" stroke-linejoin="round" stroke-width=".176" d="M1.382.335.777.858H.204v.673h.564l.614.531Z"/><path fill="none" stroke="#fc7f7f" stroke-linecap="round" stroke-width=".176" d="M1.718.572a1.06 1.06 0 0 1 0 1.256M1.947.343c.402.5.402 1.213 0 1.714"/></svg> \ No newline at end of file
+<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" width="16" height="16" version="1.0" viewBox="0 0 2.4 2.4"><path fill="#fc7f7f" d="M1.252.15a.1.1 0 0 0-.082.03L.6.75H.318C.225.75.15.817.15.9v.6c0 .083.075.15.168.15H.6l.57.57c.066.067.18.02.18-.074V.256A.106.106 0 0 0 1.252.15" paint-order="markers stroke fill"/><path fill="none" stroke="#fc7f7f" stroke-linecap="round" stroke-linejoin="round" stroke-width=".165" d="M1.575.675c.45.525 0 1.05 0 1.05m.3-1.35c.675.825 0 1.65 0 1.65" paint-order="markers stroke fill"/></svg> \ No newline at end of file
diff --git a/editor/icons/Panels1.svg b/editor/icons/Panels1.svg
index 33a511a22b..c37d974058 100644
--- a/editor/icons/Panels1.svg
+++ b/editor/icons/Panels1.svg
@@ -1 +1 @@
-<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><path fill="#e0e0e0" d="m0 0h16v16h-16z"/></svg> \ No newline at end of file
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><path fill="#e0e0e0" d="M2 0a2 2 0 0 0-2 2v12.002a2 2 0 0 0 2 2h12.002a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2zm0 4h12v10H2z"/></svg> \ No newline at end of file
diff --git a/editor/icons/Panels2.svg b/editor/icons/Panels2.svg
index 2621c36c57..673ae62aec 100644
--- a/editor/icons/Panels2.svg
+++ b/editor/icons/Panels2.svg
@@ -1 +1 @@
-<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><path fill="#e0e0e0" d="m0 0v7h16v-7zm0 9v7h16v-7z"/></svg> \ No newline at end of file
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><path fill="#e0e0e0" d="M2 0a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2zm0 4h12v4H2zm12 6v4H2v-4z"/></svg> \ No newline at end of file
diff --git a/editor/icons/Panels2Alt.svg b/editor/icons/Panels2Alt.svg
index 0a6c6b6c04..e2f27e415e 100644
--- a/editor/icons/Panels2Alt.svg
+++ b/editor/icons/Panels2Alt.svg
@@ -1 +1 @@
-<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><path fill="#e0e0e0" d="m0 0v16h7v-16zm9 0v16h7v-16z"/></svg> \ No newline at end of file
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><path fill="#e0e0e0" d="M2 0a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2zm0 4h5v10H2zm7 0h5v10H9z"/></svg> \ No newline at end of file
diff --git a/editor/icons/Panels3.svg b/editor/icons/Panels3.svg
index 5ee8b7f120..d5cf987c41 100644
--- a/editor/icons/Panels3.svg
+++ b/editor/icons/Panels3.svg
@@ -1 +1 @@
-<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><path fill="#e0e0e0" d="m0 0v7h16v-7zm0 9v7h7v-7zm9 0v7h7v-7z"/></svg> \ No newline at end of file
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><path fill="#e0e0e0" d="M2 0a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2zm0 4h12v4H2zm0 6h5v4H2zm7 0h5v4H9z"/></svg> \ No newline at end of file
diff --git a/editor/icons/Panels3Alt.svg b/editor/icons/Panels3Alt.svg
index dc373c6fc5..ea6745df6f 100644
--- a/editor/icons/Panels3Alt.svg
+++ b/editor/icons/Panels3Alt.svg
@@ -1 +1 @@
-<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><path fill="#e0e0e0" d="m0 0v7h7v-7zm9 0v16h7v-16zm-9 9v7h7v-7z"/></svg> \ No newline at end of file
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><path fill="#e0e0e0" d="M2 0a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2zm0 4h5v4H2zm7 0h5v10H9zm-7 6h5v4H2z"/></svg> \ No newline at end of file
diff --git a/editor/icons/Panels4.svg b/editor/icons/Panels4.svg
index e0ff57de4d..407dfba1b6 100644
--- a/editor/icons/Panels4.svg
+++ b/editor/icons/Panels4.svg
@@ -1 +1 @@
-<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><path fill="#e0e0e0" d="m0 0v7h7v-7zm9 0v7h7v-7zm-9 9v7h7v-7zm9 0v7h7v-7z"/></svg> \ No newline at end of file
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><path fill="#e0e0e0" d="M2 0a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2zm0 4h5v4H2zm7 0h5v4H9zm-7 6h5v4H2zm7 0h5v4H9z"/></svg> \ No newline at end of file
diff --git a/editor/import/3d/resource_importer_scene.cpp b/editor/import/3d/resource_importer_scene.cpp
index 27b2af8f77..8ad8e6201e 100644
--- a/editor/import/3d/resource_importer_scene.cpp
+++ b/editor/import/3d/resource_importer_scene.cpp
@@ -2544,6 +2544,8 @@ 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, split_angle, skin_pose_transform_array, raycast_normals);
diff --git a/editor/import/3d/scene_import_settings.cpp b/editor/import/3d/scene_import_settings.cpp
index 6b741b7dd7..ed3eaa94c1 100644
--- a/editor/import/3d/scene_import_settings.cpp
+++ b/editor/import/3d/scene_import_settings.cpp
@@ -117,7 +117,9 @@ class SceneImportSettingsData : public Object {
ERR_FAIL_NULL(settings);
if (r_option.name == "rest_pose/load_pose") {
if (!settings->has("rest_pose/load_pose") || int((*settings)["rest_pose/load_pose"]) != 2) {
- (*settings)["rest_pose/external_animation_library"] = Variant();
+ if (settings->has("rest_pose/external_animation_library")) {
+ (*settings)["rest_pose/external_animation_library"] = Variant();
+ }
}
}
if (r_option.name == "rest_pose/selected_animation") {
@@ -134,7 +136,10 @@ class SceneImportSettingsData : public Object {
}
} break;
case 2: {
- Object *res = (*settings)["rest_pose/external_animation_library"];
+ Object *res = nullptr;
+ if (settings->has("rest_pose/external_animation_library")) {
+ res = (*settings)["rest_pose/external_animation_library"];
+ }
Ref<Animation> anim(res);
Ref<AnimationLibrary> library(res);
if (anim.is_valid()) {
diff --git a/editor/import/dynamic_font_import_settings.cpp b/editor/import/dynamic_font_import_settings.cpp
index 00ce3d6a7a..590e3a9ede 100644
--- a/editor/import/dynamic_font_import_settings.cpp
+++ b/editor/import/dynamic_font_import_settings.cpp
@@ -31,6 +31,7 @@
#include "dynamic_font_import_settings.h"
#include "core/config/project_settings.h"
+#include "core/string/translation_server.h"
#include "editor/editor_file_system.h"
#include "editor/editor_inspector.h"
#include "editor/editor_locale_dialog.h"
diff --git a/editor/import/resource_importer_csv_translation.cpp b/editor/import/resource_importer_csv_translation.cpp
index d2705ac98a..c181011402 100644
--- a/editor/import/resource_importer_csv_translation.cpp
+++ b/editor/import/resource_importer_csv_translation.cpp
@@ -33,7 +33,7 @@
#include "core/io/file_access.h"
#include "core/io/resource_saver.h"
#include "core/string/optimized_translation.h"
-#include "core/string/translation.h"
+#include "core/string/translation_server.h"
String ResourceImporterCSVTranslation::get_importer_name() const {
return "csv_translation";
diff --git a/editor/import/resource_importer_dynamic_font.cpp b/editor/import/resource_importer_dynamic_font.cpp
index c52f53146e..fa222b2790 100644
--- a/editor/import/resource_importer_dynamic_font.cpp
+++ b/editor/import/resource_importer_dynamic_font.cpp
@@ -118,7 +118,7 @@ void ResourceImporterDynamicFont::get_import_options(const String &p_path, List<
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "allow_system_fallback"), true));
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "force_autohinter"), false));
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "hinting", PROPERTY_HINT_ENUM, "None,Light,Normal"), 1));
- r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "subpixel_positioning", PROPERTY_HINT_ENUM, "Disabled,Auto,One Half of a Pixel,One Quarter of a Pixel"), 1));
+ r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "subpixel_positioning", PROPERTY_HINT_ENUM, "Disabled,Auto,One Half of a Pixel,One Quarter of a Pixel,Auto (Except Pixel Fonts)"), 4));
r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "oversampling", PROPERTY_HINT_RANGE, "0,10,0.1"), 0.0));
r_options->push_back(ImportOption(PropertyInfo(Variant::NIL, "Fallbacks", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_GROUP), Variant()));
@@ -176,11 +176,44 @@ Error ResourceImporterDynamicFont::import(const String &p_source_file, const Str
font->set_fixed_size(0);
font->set_force_autohinter(autohinter);
font->set_allow_system_fallback(allow_system_fallback);
- font->set_subpixel_positioning((TextServer::SubpixelPositioning)subpixel_positioning);
font->set_hinting((TextServer::Hinting)hinting);
font->set_oversampling(oversampling);
font->set_fallbacks(fallbacks);
+ if (subpixel_positioning == 4 /* Auto (Except Pixel Fonts) */) {
+ PackedInt32Array glyphs = TS->font_get_supported_glyphs(font->get_rids()[0]);
+ bool is_pixel = true;
+ for (int32_t gl : glyphs) {
+ Dictionary ct = TS->font_get_glyph_contours(font->get_rids()[0], 16, gl);
+ PackedInt32Array contours = ct["contours"];
+ PackedVector3Array points = ct["points"];
+ int prev_start = 0;
+ for (int i = 0; i < contours.size(); i++) {
+ for (int j = prev_start; j <= contours[i]; j++) {
+ int next_point = (j < contours[i]) ? (j + 1) : prev_start;
+ if ((points[j].z != TextServer::CONTOUR_CURVE_TAG_ON) || (!Math::is_equal_approx(points[j].x, points[next_point].x) && !Math::is_equal_approx(points[j].y, points[next_point].y))) {
+ is_pixel = false;
+ break;
+ }
+ }
+ prev_start = contours[i] + 1;
+ if (!is_pixel) {
+ break;
+ }
+ }
+ if (!is_pixel) {
+ break;
+ }
+ }
+ if (is_pixel && !glyphs.is_empty()) {
+ print_line(vformat("%s: Pixel font detected, disabling subpixel positioning.", p_source_file));
+ subpixel_positioning = TextServer::SUBPIXEL_POSITIONING_DISABLED;
+ } else {
+ subpixel_positioning = TextServer::SUBPIXEL_POSITIONING_AUTO;
+ }
+ }
+ font->set_subpixel_positioning((TextServer::SubpixelPositioning)subpixel_positioning);
+
Dictionary langs = p_options["language_support"];
for (int i = 0; i < langs.size(); i++) {
String key = langs.get_key_at_index(i);
diff --git a/editor/import/resource_importer_imagefont.cpp b/editor/import/resource_importer_imagefont.cpp
index f1b6d3a6ac..f01381904d 100644
--- a/editor/import/resource_importer_imagefont.cpp
+++ b/editor/import/resource_importer_imagefont.cpp
@@ -293,18 +293,20 @@ Error ResourceImporterImageFont::import(const String &p_source_file, const Strin
}
String from_tokens;
for (int i = 0; i < kp_tokens[0].length(); i++) {
- if (i <= kp_tokens[0].length() - 6 && kp_tokens[0][i] == '\\' && kp_tokens[0][i + 1] == 'u') {
+ if (i <= kp_tokens[0].length() - 6 && kp_tokens[0][i] == '\\' && kp_tokens[0][i + 1] == 'u' && is_hex_digit(kp_tokens[0][i + 2]) && is_hex_digit(kp_tokens[0][i + 3]) && is_hex_digit(kp_tokens[0][i + 4]) && is_hex_digit(kp_tokens[0][i + 5])) {
char32_t charcode = kp_tokens[0].substr(i + 2, 4).hex_to_int();
from_tokens += charcode;
+ i += 5;
} else {
from_tokens += kp_tokens[0][i];
}
}
String to_tokens;
for (int i = 0; i < kp_tokens[1].length(); i++) {
- if (i <= kp_tokens[1].length() - 6 && kp_tokens[1][i] == '\\' && kp_tokens[1][i + 1] == 'u') {
+ if (i <= kp_tokens[1].length() - 6 && kp_tokens[1][i] == '\\' && kp_tokens[1][i + 1] == 'u' && is_hex_digit(kp_tokens[1][i + 2]) && is_hex_digit(kp_tokens[1][i + 3]) && is_hex_digit(kp_tokens[1][i + 4]) && is_hex_digit(kp_tokens[1][i + 5])) {
char32_t charcode = kp_tokens[1].substr(i + 2, 4).hex_to_int();
to_tokens += charcode;
+ i += 5;
} else {
to_tokens += kp_tokens[1][i];
}
diff --git a/editor/localization_editor.cpp b/editor/localization_editor.cpp
index 11d1b3e089..6bc3a27a95 100644
--- a/editor/localization_editor.cpp
+++ b/editor/localization_editor.cpp
@@ -31,7 +31,7 @@
#include "localization_editor.h"
#include "core/config/project_settings.h"
-#include "core/string/translation.h"
+#include "core/string/translation_server.h"
#include "editor/editor_translation_parser.h"
#include "editor/editor_undo_redo_manager.h"
#include "editor/filesystem_dock.h"
diff --git a/editor/node_dock.cpp b/editor/node_dock.cpp
index 0c79e5d747..0da8d8291f 100644
--- a/editor/node_dock.cpp
+++ b/editor/node_dock.cpp
@@ -48,9 +48,6 @@ void NodeDock::show_connections() {
connections->show();
}
-void NodeDock::_bind_methods() {
-}
-
void NodeDock::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_ENTER_TREE:
diff --git a/editor/node_dock.h b/editor/node_dock.h
index e9dcc41d48..adf378bd06 100644
--- a/editor/node_dock.h
+++ b/editor/node_dock.h
@@ -55,7 +55,6 @@ public:
static NodeDock *get_singleton() { return singleton; }
protected:
- static void _bind_methods();
void _notification(int p_what);
public:
diff --git a/editor/plugins/abstract_polygon_2d_editor.cpp b/editor/plugins/abstract_polygon_2d_editor.cpp
index f73c494b25..60d808952e 100644
--- a/editor/plugins/abstract_polygon_2d_editor.cpp
+++ b/editor/plugins/abstract_polygon_2d_editor.cpp
@@ -620,9 +620,6 @@ void AbstractPolygon2DEditor::edit(Node *p_polygon) {
canvas_item_editor->update_viewport();
}
-void AbstractPolygon2DEditor::_bind_methods() {
-}
-
void AbstractPolygon2DEditor::remove_point(const Vertex &p_vertex) {
EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
Vector<Vector2> vertices = _get_polygon(p_vertex.polygon);
diff --git a/editor/plugins/abstract_polygon_2d_editor.h b/editor/plugins/abstract_polygon_2d_editor.h
index 42170d9ffd..66d4e1b7ef 100644
--- a/editor/plugins/abstract_polygon_2d_editor.h
+++ b/editor/plugins/abstract_polygon_2d_editor.h
@@ -109,7 +109,6 @@ protected:
void _notification(int p_what);
void _node_removed(Node *p_node);
- static void _bind_methods();
void remove_point(const Vertex &p_vertex);
Vertex get_active_point() const;
diff --git a/editor/plugins/animation_tree_editor_plugin.cpp b/editor/plugins/animation_tree_editor_plugin.cpp
index 757d410b78..7906b50c2c 100644
--- a/editor/plugins/animation_tree_editor_plugin.cpp
+++ b/editor/plugins/animation_tree_editor_plugin.cpp
@@ -199,9 +199,6 @@ void AnimationTreeEditor::_notification(int p_what) {
}
}
-void AnimationTreeEditor::_bind_methods() {
-}
-
AnimationTreeEditor *AnimationTreeEditor::singleton = nullptr;
void AnimationTreeEditor::add_plugin(AnimationTreeNodeEditorPlugin *p_editor) {
diff --git a/editor/plugins/animation_tree_editor_plugin.h b/editor/plugins/animation_tree_editor_plugin.h
index 8dc820695a..a234af6874 100644
--- a/editor/plugins/animation_tree_editor_plugin.h
+++ b/editor/plugins/animation_tree_editor_plugin.h
@@ -72,7 +72,6 @@ class AnimationTreeEditor : public VBoxContainer {
protected:
void _notification(int p_what);
void _node_removed(Node *p_node);
- static void _bind_methods();
static AnimationTreeEditor *singleton;
diff --git a/editor/plugins/bone_map_editor_plugin.cpp b/editor/plugins/bone_map_editor_plugin.cpp
index c00436b01f..015dfdbca5 100644
--- a/editor/plugins/bone_map_editor_plugin.cpp
+++ b/editor/plugins/bone_map_editor_plugin.cpp
@@ -252,9 +252,6 @@ StringName BonePicker::get_selected_bone() {
return selected->get_text(0);
}
-void BonePicker::_bind_methods() {
-}
-
void BonePicker::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_ENTER_TREE: {
diff --git a/editor/plugins/bone_map_editor_plugin.h b/editor/plugins/bone_map_editor_plugin.h
index f3aa2fc84d..988ad40ec4 100644
--- a/editor/plugins/bone_map_editor_plugin.h
+++ b/editor/plugins/bone_map_editor_plugin.h
@@ -122,8 +122,6 @@ public:
protected:
void _notification(int p_what);
- static void _bind_methods();
-
void _confirm();
private:
diff --git a/editor/plugins/camera_3d_editor_plugin.cpp b/editor/plugins/camera_3d_editor_plugin.cpp
index 62b40043c1..f4116ed364 100644
--- a/editor/plugins/camera_3d_editor_plugin.cpp
+++ b/editor/plugins/camera_3d_editor_plugin.cpp
@@ -46,9 +46,6 @@ void Camera3DEditor::_pressed() {
Node3DEditor::get_singleton()->set_custom_camera(sn);
}
-void Camera3DEditor::_bind_methods() {
-}
-
void Camera3DEditor::edit(Node *p_camera) {
node = p_camera;
diff --git a/editor/plugins/camera_3d_editor_plugin.h b/editor/plugins/camera_3d_editor_plugin.h
index 2e4d8a1ee3..1c6838aa02 100644
--- a/editor/plugins/camera_3d_editor_plugin.h
+++ b/editor/plugins/camera_3d_editor_plugin.h
@@ -45,7 +45,6 @@ class Camera3DEditor : public Control {
protected:
void _node_removed(Node *p_node);
- static void _bind_methods();
public:
void edit(Node *p_camera);
diff --git a/editor/plugins/cpu_particles_2d_editor_plugin.cpp b/editor/plugins/cpu_particles_2d_editor_plugin.cpp
index 92c0fd847b..4869a202d7 100644
--- a/editor/plugins/cpu_particles_2d_editor_plugin.cpp
+++ b/editor/plugins/cpu_particles_2d_editor_plugin.cpp
@@ -258,9 +258,6 @@ void CPUParticles2DEditorPlugin::_notification(int p_what) {
}
}
-void CPUParticles2DEditorPlugin::_bind_methods() {
-}
-
CPUParticles2DEditorPlugin::CPUParticles2DEditorPlugin() {
particles = nullptr;
diff --git a/editor/plugins/cpu_particles_2d_editor_plugin.h b/editor/plugins/cpu_particles_2d_editor_plugin.h
index 4d59c9981e..645e6d8345 100644
--- a/editor/plugins/cpu_particles_2d_editor_plugin.h
+++ b/editor/plugins/cpu_particles_2d_editor_plugin.h
@@ -79,7 +79,6 @@ class CPUParticles2DEditorPlugin : public EditorPlugin {
protected:
void _notification(int p_what);
- static void _bind_methods();
public:
virtual String get_name() const override { return "CPUParticles2D"; }
diff --git a/editor/plugins/cpu_particles_3d_editor_plugin.cpp b/editor/plugins/cpu_particles_3d_editor_plugin.cpp
index be2847d1d7..69f287c134 100644
--- a/editor/plugins/cpu_particles_3d_editor_plugin.cpp
+++ b/editor/plugins/cpu_particles_3d_editor_plugin.cpp
@@ -157,9 +157,6 @@ void CPUParticles3DEditor::_generate_emission_points() {
}
}
-void CPUParticles3DEditor::_bind_methods() {
-}
-
CPUParticles3DEditor::CPUParticles3DEditor() {
particles_editor_hb = memnew(HBoxContainer);
Node3DEditor::get_singleton()->add_control_to_menu_panel(particles_editor_hb);
diff --git a/editor/plugins/cpu_particles_3d_editor_plugin.h b/editor/plugins/cpu_particles_3d_editor_plugin.h
index 99178b7fde..2a1ea93ac8 100644
--- a/editor/plugins/cpu_particles_3d_editor_plugin.h
+++ b/editor/plugins/cpu_particles_3d_editor_plugin.h
@@ -60,7 +60,6 @@ class CPUParticles3DEditor : public GPUParticles3DEditorBase {
protected:
void _notification(int p_notification);
void _node_removed(Node *p_node);
- static void _bind_methods();
public:
void edit(CPUParticles3D *p_particles);
diff --git a/editor/plugins/editor_plugin_settings.cpp b/editor/plugins/editor_plugin_settings.cpp
index afc60b09d6..5949bc141f 100644
--- a/editor/plugins/editor_plugin_settings.cpp
+++ b/editor/plugins/editor_plugin_settings.cpp
@@ -199,9 +199,6 @@ Vector<String> EditorPluginSettings::_get_plugins(const String &p_dir) {
return plugins;
}
-void EditorPluginSettings::_bind_methods() {
-}
-
EditorPluginSettings::EditorPluginSettings() {
ProjectSettings::get_singleton()->add_hidden_prefix("editor_plugins/");
diff --git a/editor/plugins/editor_plugin_settings.h b/editor/plugins/editor_plugin_settings.h
index 5b470b3e58..ea7d0ea8b3 100644
--- a/editor/plugins/editor_plugin_settings.h
+++ b/editor/plugins/editor_plugin_settings.h
@@ -67,8 +67,6 @@ class EditorPluginSettings : public VBoxContainer {
protected:
void _notification(int p_what);
- static void _bind_methods();
-
public:
void update_plugins();
diff --git a/editor/plugins/font_config_plugin.cpp b/editor/plugins/font_config_plugin.cpp
index e6ce63fe36..d712c14861 100644
--- a/editor/plugins/font_config_plugin.cpp
+++ b/editor/plugins/font_config_plugin.cpp
@@ -30,6 +30,7 @@
#include "font_config_plugin.h"
+#include "core/string/translation_server.h"
#include "editor/editor_settings.h"
#include "editor/import/dynamic_font_import_settings.h"
#include "editor/themes/editor_scale.h"
@@ -63,9 +64,6 @@ bool EditorPropertyFontMetaObject::_get(const StringName &p_name, Variant &r_ret
return false;
}
-void EditorPropertyFontMetaObject::_bind_methods() {
-}
-
void EditorPropertyFontMetaObject::set_dict(const Dictionary &p_dict) {
dict = p_dict;
}
diff --git a/editor/plugins/font_config_plugin.h b/editor/plugins/font_config_plugin.h
index 4e798fc3e8..e83f29a77b 100644
--- a/editor/plugins/font_config_plugin.h
+++ b/editor/plugins/font_config_plugin.h
@@ -46,7 +46,6 @@ class EditorPropertyFontMetaObject : public RefCounted {
protected:
bool _set(const StringName &p_name, const Variant &p_value);
bool _get(const StringName &p_name, Variant &r_ret) const;
- static void _bind_methods();
public:
void set_dict(const Dictionary &p_dict);
diff --git a/editor/plugins/gizmos/camera_3d_gizmo_plugin.cpp b/editor/plugins/gizmos/camera_3d_gizmo_plugin.cpp
index 19dd45a3ea..8d0222215c 100644
--- a/editor/plugins/gizmos/camera_3d_gizmo_plugin.cpp
+++ b/editor/plugins/gizmos/camera_3d_gizmo_plugin.cpp
@@ -112,9 +112,12 @@ void Camera3DGizmoPlugin::set_handle(const EditorNode3DGizmo *p_gizmo, int p_id,
float a = _find_closest_angle_to_half_pi_arc(s[0], s[1], 1.0, gt2);
camera->set("fov", CLAMP(a * 2.0, 1, 179));
} else {
+ Camera3D::KeepAspect aspect = camera->get_keep_aspect_mode();
+ Vector3 camera_far = aspect == Camera3D::KeepAspect::KEEP_WIDTH ? Vector3(4096, 0, -1) : Vector3(0, 4096, -1);
+
Vector3 ra, rb;
- Geometry3D::get_closest_points_between_segments(Vector3(0, 0, -1), Vector3(4096, 0, -1), s[0], s[1], ra, rb);
- float d = ra.x * 2;
+ Geometry3D::get_closest_points_between_segments(Vector3(0, 0, -1), camera_far, s[0], s[1], ra, rb);
+ float d = aspect == Camera3D::KeepAspect::KEEP_WIDTH ? ra.x * 2 : ra.y * 2;
if (Node3DEditor::get_singleton()->is_snap_enabled()) {
d = Math::snapped(d, Node3DEditor::get_singleton()->get_translate_snap());
}
@@ -213,25 +216,33 @@ void Camera3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
} break;
case Camera3D::PROJECTION_ORTHOGONAL: {
+ Camera3D::KeepAspect aspect = camera->get_keep_aspect_mode();
+
float size = camera->get_size();
+ float keep_size = size * 0.5;
- float hsize = size * 0.5;
- Vector3 right(hsize * size_factor.x, 0, 0);
- Vector3 up(0, hsize * size_factor.y, 0);
+ Vector3 right, up;
Vector3 back(0, 0, -1.0);
Vector3 front(0, 0, 0);
+ if (aspect == Camera3D::KeepAspect::KEEP_WIDTH) {
+ right = Vector3(keep_size, 0, 0);
+ up = Vector3(0, keep_size / viewport_aspect, 0);
+ handles.push_back(right + back);
+ } else {
+ right = Vector3(keep_size * viewport_aspect, 0, 0);
+ up = Vector3(0, keep_size, 0);
+ handles.push_back(up + back);
+ }
+
ADD_QUAD(-up - right, -up + right, up + right, up - right);
ADD_QUAD(-up - right + back, -up + right + back, up + right + back, up - right + back);
ADD_QUAD(up + right, up + right + back, up - right + back, up - right);
ADD_QUAD(-up + right, -up + right + back, -up - right + back, -up - right);
- handles.push_back(right + back);
-
- right.x = MIN(right.x, hsize * 0.25);
- Vector3 tup(0, up.y + hsize / 2, back.z);
+ right.x = MIN(right.x, keep_size * 0.25);
+ Vector3 tup(0, up.y + keep_size / 2, back.z);
ADD_TRIANGLE(tup, right + up + back, -right + up + back);
-
} break;
case Camera3D::PROJECTION_FRUSTUM: {
diff --git a/editor/plugins/gpu_particles_2d_editor_plugin.cpp b/editor/plugins/gpu_particles_2d_editor_plugin.cpp
index b70c34b785..1b68b55ff6 100644
--- a/editor/plugins/gpu_particles_2d_editor_plugin.cpp
+++ b/editor/plugins/gpu_particles_2d_editor_plugin.cpp
@@ -360,9 +360,6 @@ void GPUParticles2DEditorPlugin::_notification(int p_what) {
}
}
-void GPUParticles2DEditorPlugin::_bind_methods() {
-}
-
GPUParticles2DEditorPlugin::GPUParticles2DEditorPlugin() {
particles = nullptr;
diff --git a/editor/plugins/gpu_particles_2d_editor_plugin.h b/editor/plugins/gpu_particles_2d_editor_plugin.h
index bb0ca5de3a..658e4d87e5 100644
--- a/editor/plugins/gpu_particles_2d_editor_plugin.h
+++ b/editor/plugins/gpu_particles_2d_editor_plugin.h
@@ -86,7 +86,6 @@ class GPUParticles2DEditorPlugin : public EditorPlugin {
protected:
void _notification(int p_what);
- static void _bind_methods();
public:
virtual String get_name() const override { return "GPUParticles2D"; }
diff --git a/editor/plugins/gpu_particles_3d_editor_plugin.cpp b/editor/plugins/gpu_particles_3d_editor_plugin.cpp
index 00c5332464..4e9be0aa53 100644
--- a/editor/plugins/gpu_particles_3d_editor_plugin.cpp
+++ b/editor/plugins/gpu_particles_3d_editor_plugin.cpp
@@ -198,9 +198,6 @@ void GPUParticles3DEditorBase::_node_selected(const NodePath &p_path) {
emission_dialog->popup_centered(Size2(300, 130));
}
-void GPUParticles3DEditorBase::_bind_methods() {
-}
-
GPUParticles3DEditorBase::GPUParticles3DEditorBase() {
emission_dialog = memnew(ConfirmationDialog);
emission_dialog->set_title(TTR("Create Emitter"));
@@ -402,9 +399,6 @@ void GPUParticles3DEditor::_generate_emission_points() {
}
}
-void GPUParticles3DEditor::_bind_methods() {
-}
-
GPUParticles3DEditor::GPUParticles3DEditor() {
node = nullptr;
particles_editor_hb = memnew(HBoxContainer);
diff --git a/editor/plugins/gpu_particles_3d_editor_plugin.h b/editor/plugins/gpu_particles_3d_editor_plugin.h
index 3b2ab2f8ca..5f59f6dca7 100644
--- a/editor/plugins/gpu_particles_3d_editor_plugin.h
+++ b/editor/plugins/gpu_particles_3d_editor_plugin.h
@@ -62,8 +62,6 @@ protected:
virtual void _generate_emission_points(){};
void _node_selected(const NodePath &p_path);
- static void _bind_methods();
-
public:
GPUParticles3DEditorBase();
};
@@ -95,7 +93,6 @@ class GPUParticles3DEditor : public GPUParticles3DEditorBase {
protected:
void _notification(int p_notification);
void _node_removed(Node *p_node);
- static void _bind_methods();
public:
void edit(GPUParticles3D *p_particles);
diff --git a/editor/plugins/gpu_particles_collision_sdf_editor_plugin.cpp b/editor/plugins/gpu_particles_collision_sdf_editor_plugin.cpp
index 25b076d0e5..c21a1b5dd6 100644
--- a/editor/plugins/gpu_particles_collision_sdf_editor_plugin.cpp
+++ b/editor/plugins/gpu_particles_collision_sdf_editor_plugin.cpp
@@ -175,9 +175,6 @@ void GPUParticlesCollisionSDF3DEditorPlugin::_sdf_save_path_and_bake(const Strin
}
}
-void GPUParticlesCollisionSDF3DEditorPlugin::_bind_methods() {
-}
-
GPUParticlesCollisionSDF3DEditorPlugin::GPUParticlesCollisionSDF3DEditorPlugin() {
bake_hb = memnew(HBoxContainer);
bake_hb->set_h_size_flags(Control::SIZE_EXPAND_FILL);
diff --git a/editor/plugins/gpu_particles_collision_sdf_editor_plugin.h b/editor/plugins/gpu_particles_collision_sdf_editor_plugin.h
index bba8bd2584..3e1dae77ea 100644
--- a/editor/plugins/gpu_particles_collision_sdf_editor_plugin.h
+++ b/editor/plugins/gpu_particles_collision_sdf_editor_plugin.h
@@ -58,7 +58,6 @@ class GPUParticlesCollisionSDF3DEditorPlugin : public EditorPlugin {
void _sdf_save_path_and_bake(const String &p_path);
protected:
- static void _bind_methods();
void _notification(int p_what);
public:
diff --git a/editor/plugins/input_event_editor_plugin.cpp b/editor/plugins/input_event_editor_plugin.cpp
index 5a90d2de61..30debfc14f 100644
--- a/editor/plugins/input_event_editor_plugin.cpp
+++ b/editor/plugins/input_event_editor_plugin.cpp
@@ -33,9 +33,6 @@
#include "editor/event_listener_line_edit.h"
#include "editor/input_event_configuration_dialog.h"
-void InputEventConfigContainer::_bind_methods() {
-}
-
void InputEventConfigContainer::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_ENTER_TREE:
diff --git a/editor/plugins/input_event_editor_plugin.h b/editor/plugins/input_event_editor_plugin.h
index 48c16a4807..276bb74f6b 100644
--- a/editor/plugins/input_event_editor_plugin.h
+++ b/editor/plugins/input_event_editor_plugin.h
@@ -51,7 +51,6 @@ class InputEventConfigContainer : public VBoxContainer {
protected:
void _notification(int p_what);
- static void _bind_methods();
public:
void set_event(const Ref<InputEvent> &p_event);
diff --git a/editor/plugins/mesh_library_editor_plugin.cpp b/editor/plugins/mesh_library_editor_plugin.cpp
index 0b2549986c..58cc670475 100644
--- a/editor/plugins/mesh_library_editor_plugin.cpp
+++ b/editor/plugins/mesh_library_editor_plugin.cpp
@@ -242,9 +242,6 @@ void MeshLibraryEditor::_menu_cbk(int p_option) {
}
}
-void MeshLibraryEditor::_bind_methods() {
-}
-
MeshLibraryEditor::MeshLibraryEditor() {
file = memnew(EditorFileDialog);
file->set_file_mode(EditorFileDialog::FILE_MODE_OPEN_FILE);
diff --git a/editor/plugins/mesh_library_editor_plugin.h b/editor/plugins/mesh_library_editor_plugin.h
index 94f46beea1..5a9e32178d 100644
--- a/editor/plugins/mesh_library_editor_plugin.h
+++ b/editor/plugins/mesh_library_editor_plugin.h
@@ -68,9 +68,6 @@ class MeshLibraryEditor : public Control {
static void _import_scene(Node *p_scene, Ref<MeshLibrary> p_library, bool p_merge, bool p_apply_xforms);
static void _import_scene_parse_node(Ref<MeshLibrary> p_library, HashMap<int, MeshInstance3D *> &p_mesh_instances, Node *p_node, bool p_merge, bool p_apply_xforms);
-protected:
- static void _bind_methods();
-
public:
MenuButton *get_menu_button() const { return menu; }
diff --git a/editor/plugins/multimesh_editor_plugin.cpp b/editor/plugins/multimesh_editor_plugin.cpp
index 3980f23c8a..76ffdb02e1 100644
--- a/editor/plugins/multimesh_editor_plugin.cpp
+++ b/editor/plugins/multimesh_editor_plugin.cpp
@@ -266,9 +266,6 @@ void MultiMeshEditor::_browse(bool p_source) {
std->popup_scenetree_dialog(browsed_node);
}
-void MultiMeshEditor::_bind_methods() {
-}
-
MultiMeshEditor::MultiMeshEditor() {
options = memnew(MenuButton);
options->set_switch_on_hover(true);
diff --git a/editor/plugins/multimesh_editor_plugin.h b/editor/plugins/multimesh_editor_plugin.h
index 5051926c64..3fe63bfa92 100644
--- a/editor/plugins/multimesh_editor_plugin.h
+++ b/editor/plugins/multimesh_editor_plugin.h
@@ -79,7 +79,6 @@ class MultiMeshEditor : public Control {
protected:
void _node_removed(Node *p_node);
- static void _bind_methods();
public:
void edit(MultiMeshInstance3D *p_multimesh);
diff --git a/editor/plugins/node_3d_editor_gizmos.cpp b/editor/plugins/node_3d_editor_gizmos.cpp
index de56767929..67d5e44ce5 100644
--- a/editor/plugins/node_3d_editor_gizmos.cpp
+++ b/editor/plugins/node_3d_editor_gizmos.cpp
@@ -81,6 +81,8 @@ void EditorNode3DGizmo::redraw() {
gizmo_plugin->redraw(this);
}
+ _update_bvh();
+
if (Node3DEditor::get_singleton()->is_current_selected_gizmo(this)) {
Node3DEditor::get_singleton()->update_transform_gizmo();
}
@@ -244,6 +246,32 @@ void EditorNode3DGizmo::add_mesh(const Ref<Mesh> &p_mesh, const Ref<Material> &p
instances.push_back(ins);
}
+void EditorNode3DGizmo::_update_bvh() {
+ ERR_FAIL_NULL(spatial_node);
+
+ Transform3D transform = spatial_node->get_global_transform();
+
+ float effective_icon_size = selectable_icon_size > 0.0f ? selectable_icon_size : 0.0f;
+ Vector3 icon_size_vector3 = Vector3(effective_icon_size, effective_icon_size, effective_icon_size);
+ AABB aabb(spatial_node->get_position() - icon_size_vector3 * 100.0f, icon_size_vector3 * 200.0f);
+
+ for (const Vector3 &segment_end : collision_segments) {
+ aabb.expand_to(transform.xform(segment_end));
+ }
+
+ if (collision_mesh.is_valid()) {
+ for (const Face3 &face : collision_mesh->get_faces()) {
+ aabb.expand_to(transform.xform(face.vertex[0]));
+ aabb.expand_to(transform.xform(face.vertex[1]));
+ aabb.expand_to(transform.xform(face.vertex[2]));
+ }
+ }
+
+ Node3DEditor::get_singleton()->update_gizmo_bvh_node(
+ bvh_node_id,
+ aabb);
+}
+
void EditorNode3DGizmo::add_lines(const Vector<Vector3> &p_lines, const Ref<Material> &p_material, bool p_billboard, const Color &p_modulate) {
add_vertices(p_lines, p_material, Mesh::PRIMITIVE_LINES, p_billboard, p_modulate);
}
@@ -765,6 +793,10 @@ void EditorNode3DGizmo::create() {
instances.write[i].create_instance(spatial_node, hidden);
}
+ bvh_node_id = Node3DEditor::get_singleton()->insert_gizmo_bvh_node(
+ spatial_node,
+ AABB(spatial_node->get_position(), Vector3(0, 0, 0)));
+
transform();
}
@@ -774,6 +806,8 @@ void EditorNode3DGizmo::transform() {
for (int i = 0; i < instances.size(); i++) {
RS::get_singleton()->instance_set_transform(instances[i].instance, spatial_node->get_global_transform() * instances[i].xform);
}
+
+ _update_bvh();
}
void EditorNode3DGizmo::free() {
@@ -790,6 +824,9 @@ void EditorNode3DGizmo::free() {
clear();
+ Node3DEditor::get_singleton()->remove_gizmo_bvh_node(bvh_node_id);
+ bvh_node_id = DynamicBVH::ID();
+
valid = false;
}
diff --git a/editor/plugins/node_3d_editor_gizmos.h b/editor/plugins/node_3d_editor_gizmos.h
index d7c368d5d0..c4b275032a 100644
--- a/editor/plugins/node_3d_editor_gizmos.h
+++ b/editor/plugins/node_3d_editor_gizmos.h
@@ -31,6 +31,7 @@
#ifndef NODE_3D_EDITOR_GIZMOS_H
#define NODE_3D_EDITOR_GIZMOS_H
+#include "core/math/dynamic_bvh.h"
#include "core/templates/hash_map.h"
#include "core/templates/local_vector.h"
#include "scene/3d/camera_3d.h"
@@ -72,8 +73,12 @@ class EditorNode3DGizmo : public Node3DGizmo {
Vector<Instance> instances;
Node3D *spatial_node = nullptr;
+ DynamicBVH::ID bvh_node_id;
+
void _set_node_3d(Node *p_node) { set_node_3d(Object::cast_to<Node3D>(p_node)); }
+ void _update_bvh();
+
protected:
static void _bind_methods();
diff --git a/editor/plugins/node_3d_editor_plugin.cpp b/editor/plugins/node_3d_editor_plugin.cpp
index 80d2e85830..f5b0a3b51e 100644
--- a/editor/plugins/node_3d_editor_plugin.cpp
+++ b/editor/plugins/node_3d_editor_plugin.cpp
@@ -800,7 +800,6 @@ ObjectID Node3DEditorViewport::_select_ray(const Point2 &p_pos) const {
RS::get_singleton()->sdfgi_set_debug_probe_select(pos, ray);
}
- Vector<ObjectID> instances = RenderingServer::get_singleton()->instances_cull_ray(pos, pos + ray * camera->get_far(), get_tree()->get_root()->get_world_3d()->get_scenario());
HashSet<Ref<EditorNode3DGizmo>> found_gizmos;
Node *edited_scene = get_tree()->get_edited_scene_root();
@@ -808,9 +807,9 @@ ObjectID Node3DEditorViewport::_select_ray(const Point2 &p_pos) const {
Node *item = nullptr;
float closest_dist = 1e20;
- for (int i = 0; i < instances.size(); i++) {
- Node3D *spat = Object::cast_to<Node3D>(ObjectDB::get_instance(instances[i]));
+ Vector<Node3D *> nodes_with_gizmos = Node3DEditor::get_singleton()->gizmo_bvh_ray_query(pos, pos + ray * camera->get_far());
+ for (Node3D *spat : nodes_with_gizmos) {
if (!spat) {
continue;
}
@@ -863,12 +862,11 @@ void Node3DEditorViewport::_find_items_at_pos(const Point2 &p_pos, Vector<_RayRe
Vector3 ray = get_ray(p_pos);
Vector3 pos = get_ray_pos(p_pos);
- Vector<ObjectID> instances = RenderingServer::get_singleton()->instances_cull_ray(pos, pos + ray * camera->get_far(), get_tree()->get_root()->get_world_3d()->get_scenario());
- HashSet<Node3D *> found_nodes;
+ Vector<Node3D *> nodes_with_gizmos = Node3DEditor::get_singleton()->gizmo_bvh_ray_query(pos, pos + ray * camera->get_far());
- for (int i = 0; i < instances.size(); i++) {
- Node3D *spat = Object::cast_to<Node3D>(ObjectDB::get_instance(instances[i]));
+ HashSet<Node3D *> found_nodes;
+ for (Node3D *spat : nodes_with_gizmos) {
if (!spat) {
continue;
}
@@ -1046,7 +1044,7 @@ void Node3DEditorViewport::_select_region() {
_clear_selected();
}
- Vector<ObjectID> instances = RenderingServer::get_singleton()->instances_cull_convex(frustum, get_tree()->get_root()->get_world_3d()->get_scenario());
+ Vector<Node3D *> nodes_with_gizmos = Node3DEditor::get_singleton()->gizmo_bvh_frustum_query(frustum);
HashSet<Node3D *> found_nodes;
Vector<Node *> selected;
@@ -1055,8 +1053,7 @@ void Node3DEditorViewport::_select_region() {
return;
}
- for (int i = 0; i < instances.size(); i++) {
- Node3D *sp = Object::cast_to<Node3D>(ObjectDB::get_instance(instances[i]));
+ for (Node3D *sp : nodes_with_gizmos) {
if (!sp || _is_node_locked(sp)) {
continue;
}
@@ -1064,21 +1061,23 @@ void Node3DEditorViewport::_select_region() {
if (found_nodes.has(sp)) {
continue;
}
-
found_nodes.insert(sp);
Node *node = Object::cast_to<Node>(sp);
+
+ // Selection requires that the node is the edited scene or its descendant, and has an owner.
if (node != edited_scene) {
+ if (!node->get_owner() || !edited_scene->is_ancestor_of(node)) {
+ continue;
+ }
node = edited_scene->get_deepest_editable_node(node);
- }
-
- // Prevent selection of nodes not owned by the edited scene.
- while (node && node != edited_scene->get_parent()) {
- Node *node_owner = node->get_owner();
- if (node_owner == edited_scene || node == edited_scene || (node_owner != nullptr && edited_scene->is_editable_instance(node_owner))) {
- break;
+ while (node != edited_scene) {
+ Node *node_owner = node->get_owner();
+ if (node_owner == edited_scene || (node_owner != nullptr && edited_scene->is_editable_instance(node_owner))) {
+ break;
+ }
+ node = node->get_parent();
}
- node = node->get_parent();
}
// Replace the node by the group if grouped
@@ -1916,12 +1915,17 @@ void Node3DEditorViewport::_sinput(const Ref<InputEvent> &p_event) {
}
if (after != EditorPlugin::AFTER_GUI_INPUT_CUSTOM) {
- //clicking is always deferred to either move or release
- clicked = _select_ray(b->get_position());
+ // Single item selection.
+ Vector<_RayResult> selection;
+ _find_items_at_pos(b->get_position(), selection, false);
+ if (!selection.is_empty()) {
+ clicked = selection[0].item->get_instance_id();
+ }
+
selection_in_progress = true;
if (clicked.is_null()) {
- //default to regionselect
+ // Default to region select.
cursor.region_select = true;
cursor.region_begin = b->get_position();
cursor.region_end = b->get_position();
@@ -2252,6 +2256,11 @@ void Node3DEditorViewport::_sinput(const Ref<InputEvent> &p_event) {
}
if (_edit.mode == TRANSFORM_NONE) {
+ if (_edit.gizmo.is_null() && is_freelook_active() && k->get_keycode() == Key::ESCAPE) {
+ set_freelook_active(false);
+ return;
+ }
+
if (_edit.gizmo.is_valid() && (k->get_keycode() == Key::ESCAPE || k->get_keycode() == Key::BACKSPACE)) {
// Restore.
_edit.gizmo->commit_handle(_edit.gizmo_handle, _edit.gizmo_handle_secondary, _edit.gizmo_initial_value, true);
@@ -3107,6 +3116,7 @@ void Node3DEditorViewport::_notification(int p_what) {
case NOTIFICATION_DRAG_END: {
// Clear preview material when dropped outside applicable object.
if (spatial_editor->get_preview_material().is_valid() && !is_drag_successful()) {
+ _reset_preview_material();
_remove_preview_material();
} else {
_remove_preview_node();
@@ -7315,9 +7325,9 @@ void Node3DEditor::_init_grid() {
int primary_grid_steps = EDITOR_GET("editors/3d/primary_grid_steps");
// Which grid planes are enabled? Which should we generate?
- grid_enable[0] = grid_visible[0] = EDITOR_GET("editors/3d/grid_xy_plane");
- grid_enable[1] = grid_visible[1] = EDITOR_GET("editors/3d/grid_yz_plane");
- grid_enable[2] = grid_visible[2] = EDITOR_GET("editors/3d/grid_xz_plane");
+ grid_enable[0] = grid_visible[0] = orthogonal || EDITOR_GET("editors/3d/grid_xy_plane");
+ grid_enable[1] = grid_visible[1] = orthogonal || EDITOR_GET("editors/3d/grid_yz_plane");
+ grid_enable[2] = grid_visible[2] = orthogonal || EDITOR_GET("editors/3d/grid_xz_plane");
// Offsets division_level for bigger or smaller grids.
// Default value is -0.2. -1.0 gives Blender-like behavior, 0.5 gives huge grids.
@@ -9223,6 +9233,49 @@ void Node3DEditor::remove_gizmo_plugin(Ref<EditorNode3DGizmoPlugin> p_plugin) {
_update_gizmos_menu();
}
+DynamicBVH::ID Node3DEditor::insert_gizmo_bvh_node(Node3D *p_node, const AABB &p_aabb) {
+ return gizmo_bvh.insert(p_aabb, p_node);
+}
+
+void Node3DEditor::update_gizmo_bvh_node(DynamicBVH::ID p_id, const AABB &p_aabb) {
+ gizmo_bvh.update(p_id, p_aabb);
+ gizmo_bvh.optimize_incremental(1);
+}
+
+void Node3DEditor::remove_gizmo_bvh_node(DynamicBVH::ID p_id) {
+ gizmo_bvh.remove(p_id);
+}
+
+Vector<Node3D *> Node3DEditor::gizmo_bvh_ray_query(const Vector3 &p_ray_start, const Vector3 &p_ray_end) {
+ struct Result {
+ Vector<Node3D *> nodes;
+ bool operator()(void *p_data) {
+ nodes.append((Node3D *)p_data);
+ return false;
+ }
+ } result;
+
+ gizmo_bvh.ray_query(p_ray_start, p_ray_end, result);
+
+ return result.nodes;
+}
+
+Vector<Node3D *> Node3DEditor::gizmo_bvh_frustum_query(const Vector<Plane> &p_frustum) {
+ Vector<Vector3> points = Geometry3D::compute_convex_mesh_points(&p_frustum[0], p_frustum.size());
+
+ struct Result {
+ Vector<Node3D *> nodes;
+ bool operator()(void *p_data) {
+ nodes.append((Node3D *)p_data);
+ return false;
+ }
+ } result;
+
+ gizmo_bvh.convex_query(p_frustum.ptr(), p_frustum.size(), points.ptr(), points.size(), result);
+
+ return result.nodes;
+}
+
Node3DEditorPlugin::Node3DEditorPlugin() {
spatial_editor = memnew(Node3DEditor);
spatial_editor->set_v_size_flags(Control::SIZE_EXPAND_FILL);
diff --git a/editor/plugins/node_3d_editor_plugin.h b/editor/plugins/node_3d_editor_plugin.h
index 5bd14748c0..9e7d46c5e8 100644
--- a/editor/plugins/node_3d_editor_plugin.h
+++ b/editor/plugins/node_3d_editor_plugin.h
@@ -31,6 +31,7 @@
#ifndef NODE_3D_EDITOR_PLUGIN_H
#define NODE_3D_EDITOR_PLUGIN_H
+#include "core/math/dynamic_bvh.h"
#include "editor/plugins/editor_plugin.h"
#include "editor/plugins/node_3d_editor_gizmos.h"
#include "editor/themes/editor_scale.h"
@@ -629,6 +630,8 @@ private:
int current_hover_gizmo_handle;
bool current_hover_gizmo_handle_secondary;
+ DynamicBVH gizmo_bvh;
+
real_t snap_translate_value;
real_t snap_rotate_value;
real_t snap_scale_value;
@@ -933,6 +936,12 @@ public:
void add_gizmo_plugin(Ref<EditorNode3DGizmoPlugin> p_plugin);
void remove_gizmo_plugin(Ref<EditorNode3DGizmoPlugin> p_plugin);
+ DynamicBVH::ID insert_gizmo_bvh_node(Node3D *p_node, const AABB &p_aabb);
+ void update_gizmo_bvh_node(DynamicBVH::ID p_id, const AABB &p_aabb);
+ void remove_gizmo_bvh_node(DynamicBVH::ID p_id);
+ Vector<Node3D *> gizmo_bvh_ray_query(const Vector3 &p_ray_start, const Vector3 &p_ray_end);
+ Vector<Node3D *> gizmo_bvh_frustum_query(const Vector<Plane> &p_frustum);
+
void edit(Node3D *p_spatial);
void clear();
diff --git a/editor/plugins/packed_scene_translation_parser_plugin.cpp b/editor/plugins/packed_scene_translation_parser_plugin.cpp
index e9ddaeb3fe..b38965753e 100644
--- a/editor/plugins/packed_scene_translation_parser_plugin.cpp
+++ b/editor/plugins/packed_scene_translation_parser_plugin.cpp
@@ -83,9 +83,8 @@ Error PackedSceneEditorTranslationParserPlugin::parse_file(const String &p_path,
int idx_last = atr_owners.size() - 1;
if (idx_last > 0 && !parent_path.begins_with(atr_owners[idx_last].first)) {
- // Switch to the previous auto translation owner this was nested in, if that was the case.
+ // Exit from the current owner nesting into the previous one.
atr_owners.remove_at(idx_last);
- idx_last -= 1;
}
if (property == "auto_translate_mode") {
@@ -106,7 +105,7 @@ Error PackedSceneEditorTranslationParserPlugin::parse_file(const String &p_path,
// If `auto_translate_mode` wasn't found, that means it is set to its default value (`AUTO_TRANSLATE_MODE_INHERIT`).
if (!auto_translate_mode_found) {
int idx_last = atr_owners.size() - 1;
- if (idx_last > 0 && atr_owners[idx_last].first == parent_path) {
+ if (idx_last > 0 && parent_path.begins_with(atr_owners[idx_last].first)) {
auto_translating = atr_owners[idx_last].second;
} else {
atr_owners.push_back(Pair(state->get_node_path(i), true));
diff --git a/editor/plugins/physical_bone_3d_editor_plugin.cpp b/editor/plugins/physical_bone_3d_editor_plugin.cpp
index b7c12ab5c0..d7e9701452 100644
--- a/editor/plugins/physical_bone_3d_editor_plugin.cpp
+++ b/editor/plugins/physical_bone_3d_editor_plugin.cpp
@@ -36,9 +36,6 @@
#include "scene/3d/physics/physical_bone_3d.h"
#include "scene/gui/separator.h"
-void PhysicalBone3DEditor::_bind_methods() {
-}
-
void PhysicalBone3DEditor::_on_toggle_button_transform_joint(bool p_is_pressed) {
_set_move_joint();
}
diff --git a/editor/plugins/physical_bone_3d_editor_plugin.h b/editor/plugins/physical_bone_3d_editor_plugin.h
index fb6f30cc57..b057644bb1 100644
--- a/editor/plugins/physical_bone_3d_editor_plugin.h
+++ b/editor/plugins/physical_bone_3d_editor_plugin.h
@@ -45,9 +45,6 @@ class PhysicalBone3DEditor : public Object {
PhysicalBone3D *selected = nullptr;
-protected:
- static void _bind_methods();
-
private:
void _on_toggle_button_transform_joint(bool p_is_pressed);
void _set_move_joint();
diff --git a/editor/plugins/polygon_2d_editor_plugin.cpp b/editor/plugins/polygon_2d_editor_plugin.cpp
index e442c37edd..f0ea322504 100644
--- a/editor/plugins/polygon_2d_editor_plugin.cpp
+++ b/editor/plugins/polygon_2d_editor_plugin.cpp
@@ -1255,44 +1255,43 @@ void Polygon2DEditor::_uv_draw() {
//draw skeleton
NodePath skeleton_path = node->get_skeleton();
- if (node->has_node(skeleton_path)) {
- Skeleton2D *skeleton = Object::cast_to<Skeleton2D>(node->get_node(skeleton_path));
- if (skeleton) {
- for (int i = 0; i < skeleton->get_bone_count(); i++) {
- Bone2D *bone = skeleton->get_bone(i);
- if (bone->get_rest() == Transform2D(0, 0, 0, 0, 0, 0)) {
- continue; //not set
- }
+ Skeleton2D *skeleton = Object::cast_to<Skeleton2D>(node->get_node_or_null(skeleton_path));
+ if (skeleton) {
+ Transform2D skeleton_xform = node->get_global_transform().affine_inverse().translated(-node->get_offset()) * skeleton->get_global_transform();
+ for (int i = 0; i < skeleton->get_bone_count(); i++) {
+ Bone2D *bone = skeleton->get_bone(i);
+ if (bone->get_rest() == Transform2D(0, 0, 0, 0, 0, 0)) {
+ continue; //not set
+ }
- bool current = bone_path == skeleton->get_path_to(bone);
+ bool current = bone_path == skeleton->get_path_to(bone);
- bool found_child = false;
+ bool found_child = false;
- for (int j = 0; j < bone->get_child_count(); j++) {
- Bone2D *n = Object::cast_to<Bone2D>(bone->get_child(j));
- if (!n) {
- continue;
- }
+ for (int j = 0; j < bone->get_child_count(); j++) {
+ Bone2D *n = Object::cast_to<Bone2D>(bone->get_child(j));
+ if (!n) {
+ continue;
+ }
- found_child = true;
+ found_child = true;
- Transform2D bone_xform = node->get_global_transform().affine_inverse().translated(-node->get_offset()) * (skeleton->get_global_transform() * bone->get_skeleton_rest());
- Transform2D endpoint_xform = bone_xform * n->get_transform();
+ Transform2D bone_xform = skeleton_xform * bone->get_skeleton_rest();
+ Transform2D endpoint_xform = bone_xform * n->get_transform();
- Color color = current ? Color(1, 1, 1) : Color(0.5, 0.5, 0.5);
- uv_edit_draw->draw_line(mtx.xform(bone_xform.get_origin()), mtx.xform(endpoint_xform.get_origin()), Color(0, 0, 0), Math::round((current ? 5 : 4) * EDSCALE));
- uv_edit_draw->draw_line(mtx.xform(bone_xform.get_origin()), mtx.xform(endpoint_xform.get_origin()), color, Math::round((current ? 3 : 2) * EDSCALE));
- }
+ Color color = current ? Color(1, 1, 1) : Color(0.5, 0.5, 0.5);
+ uv_edit_draw->draw_line(mtx.xform(bone_xform.get_origin()), mtx.xform(endpoint_xform.get_origin()), Color(0, 0, 0), Math::round((current ? 5 : 4) * EDSCALE));
+ uv_edit_draw->draw_line(mtx.xform(bone_xform.get_origin()), mtx.xform(endpoint_xform.get_origin()), color, Math::round((current ? 3 : 2) * EDSCALE));
+ }
- if (!found_child) {
- //draw normally
- Transform2D bone_xform = node->get_global_transform().affine_inverse().translated(-node->get_offset()) * (skeleton->get_global_transform() * bone->get_skeleton_rest());
- Transform2D endpoint_xform = bone_xform * Transform2D(0, Vector2(bone->get_length(), 0));
+ if (!found_child) {
+ //draw normally
+ Transform2D bone_xform = skeleton_xform * bone->get_skeleton_rest();
+ Transform2D endpoint_xform = bone_xform * Transform2D(0, Vector2(bone->get_length(), 0)).rotated(bone->get_bone_angle());
- Color color = current ? Color(1, 1, 1) : Color(0.5, 0.5, 0.5);
- uv_edit_draw->draw_line(mtx.xform(bone_xform.get_origin()), mtx.xform(endpoint_xform.get_origin()), Color(0, 0, 0), Math::round((current ? 5 : 4) * EDSCALE));
- uv_edit_draw->draw_line(mtx.xform(bone_xform.get_origin()), mtx.xform(endpoint_xform.get_origin()), color, Math::round((current ? 3 : 2) * EDSCALE));
- }
+ Color color = current ? Color(1, 1, 1) : Color(0.5, 0.5, 0.5);
+ uv_edit_draw->draw_line(mtx.xform(bone_xform.get_origin()), mtx.xform(endpoint_xform.get_origin()), Color(0, 0, 0), Math::round((current ? 5 : 4) * EDSCALE));
+ uv_edit_draw->draw_line(mtx.xform(bone_xform.get_origin()), mtx.xform(endpoint_xform.get_origin()), color, Math::round((current ? 3 : 2) * EDSCALE));
}
}
}
diff --git a/editor/plugins/root_motion_editor_plugin.cpp b/editor/plugins/root_motion_editor_plugin.cpp
index 8b31aa92b3..cd422fc291 100644
--- a/editor/plugins/root_motion_editor_plugin.cpp
+++ b/editor/plugins/root_motion_editor_plugin.cpp
@@ -193,9 +193,6 @@ void EditorPropertyRootMotion::_notification(int p_what) {
}
}
-void EditorPropertyRootMotion::_bind_methods() {
-}
-
EditorPropertyRootMotion::EditorPropertyRootMotion() {
HBoxContainer *hbc = memnew(HBoxContainer);
add_child(hbc);
diff --git a/editor/plugins/root_motion_editor_plugin.h b/editor/plugins/root_motion_editor_plugin.h
index 5befdb6006..a3e90360ae 100644
--- a/editor/plugins/root_motion_editor_plugin.h
+++ b/editor/plugins/root_motion_editor_plugin.h
@@ -52,7 +52,6 @@ class EditorPropertyRootMotion : public EditorProperty {
void _node_clear();
protected:
- static void _bind_methods();
void _notification(int p_what);
public:
diff --git a/editor/plugins/script_editor_plugin.cpp b/editor/plugins/script_editor_plugin.cpp
index c51eb44aee..9da66a0862 100644
--- a/editor/plugins/script_editor_plugin.cpp
+++ b/editor/plugins/script_editor_plugin.cpp
@@ -3799,7 +3799,10 @@ void ScriptEditor::_on_find_in_files_result_selected(const String &fpath, int li
ShaderEditorPlugin *shader_editor = Object::cast_to<ShaderEditorPlugin>(EditorNode::get_editor_data().get_editor_by_name("Shader"));
shader_editor->edit(res.ptr());
shader_editor->make_visible(true);
- shader_editor->get_shader_editor(res)->goto_line_selection(line_number - 1, begin, end);
+ TextShaderEditor *text_shader_editor = Object::cast_to<TextShaderEditor>(shader_editor->get_shader_editor(res));
+ if (text_shader_editor) {
+ text_shader_editor->goto_line_selection(line_number - 1, begin, end);
+ }
return;
} else if (fpath.get_extension() == "tscn") {
Ref<FileAccess> f = FileAccess::open(fpath, FileAccess::READ);
diff --git a/editor/plugins/shader/shader_editor.h b/editor/plugins/shader/shader_editor.h
new file mode 100644
index 0000000000..32e690015b
--- /dev/null
+++ b/editor/plugins/shader/shader_editor.h
@@ -0,0 +1,50 @@
+/**************************************************************************/
+/* shader_editor.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* 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 SHADER_EDITOR_H
+#define SHADER_EDITOR_H
+
+#include "scene/gui/control.h"
+#include "scene/resources/shader.h"
+
+class ShaderEditor : public Control {
+ GDCLASS(ShaderEditor, Control);
+
+public:
+ virtual void edit_shader(const Ref<Shader> &p_shader) = 0;
+ virtual void edit_shader_include(const Ref<ShaderInclude> &p_shader_inc) {}
+
+ virtual void apply_shaders() = 0;
+ virtual bool is_unsaved() const = 0;
+ virtual void save_external_data(const String &p_str = "") = 0;
+ virtual void validate_script() = 0;
+};
+
+#endif // SHADER_EDITOR_H
diff --git a/editor/plugins/shader_editor_plugin.cpp b/editor/plugins/shader_editor_plugin.cpp
index 9d1576cdf2..82c436d3a5 100644
--- a/editor/plugins/shader_editor_plugin.cpp
+++ b/editor/plugins/shader_editor_plugin.cpp
@@ -142,7 +142,7 @@ void ShaderEditorPlugin::edit(Object *p_object) {
}
es.shader_inc = Ref<ShaderInclude>(si);
es.shader_editor = memnew(TextShaderEditor);
- es.shader_editor->edit(si);
+ es.shader_editor->edit_shader_include(si);
shader_tabs->add_child(es.shader_editor);
} else {
Shader *s = Object::cast_to<Shader>(p_object);
@@ -156,20 +156,18 @@ void ShaderEditorPlugin::edit(Object *p_object) {
es.shader = Ref<Shader>(s);
Ref<VisualShader> vs = es.shader;
if (vs.is_valid()) {
- es.visual_shader_editor = memnew(VisualShaderEditor);
- shader_tabs->add_child(es.visual_shader_editor);
- es.visual_shader_editor->edit(vs.ptr());
+ es.shader_editor = memnew(VisualShaderEditor);
} else {
es.shader_editor = memnew(TextShaderEditor);
- shader_tabs->add_child(es.shader_editor);
- es.shader_editor->edit(s);
}
+ shader_tabs->add_child(es.shader_editor);
+ es.shader_editor->edit_shader(es.shader);
}
- if (es.shader_editor) {
- es.shader_editor->connect("validation_changed", callable_mp(this, &ShaderEditorPlugin::_update_shader_list));
-
- CodeTextEditor *cte = es.shader_editor->get_code_editor();
+ TextShaderEditor *text_shader_editor = Object::cast_to<TextShaderEditor>(es.shader_editor);
+ if (text_shader_editor) {
+ text_shader_editor->connect("validation_changed", callable_mp(this, &ShaderEditorPlugin::_update_shader_list));
+ CodeTextEditor *cte = text_shader_editor->get_code_editor();
if (cte) {
cte->set_zoom_factor(text_shader_zoom_factor);
cte->connect("zoomed", callable_mp(this, &ShaderEditorPlugin::_set_text_shader_zoom_factor));
@@ -194,7 +192,7 @@ void ShaderEditorPlugin::make_visible(bool p_visible) {
void ShaderEditorPlugin::selected_notify() {
}
-TextShaderEditor *ShaderEditorPlugin::get_shader_editor(const Ref<Shader> &p_for_shader) {
+ShaderEditor *ShaderEditorPlugin::get_shader_editor(const Ref<Shader> &p_for_shader) {
for (EditedShader &edited_shader : edited_shaders) {
if (edited_shader.shader == p_for_shader) {
return edited_shader.shader_editor;
@@ -203,15 +201,6 @@ TextShaderEditor *ShaderEditorPlugin::get_shader_editor(const Ref<Shader> &p_for
return nullptr;
}
-VisualShaderEditor *ShaderEditorPlugin::get_visual_shader_editor(const Ref<Shader> &p_for_shader) {
- for (EditedShader &edited_shader : edited_shaders) {
- if (edited_shader.shader == p_for_shader) {
- return edited_shader.visual_shader_editor;
- }
- }
- return nullptr;
-}
-
void ShaderEditorPlugin::set_window_layout(Ref<ConfigFile> p_layout) {
if (EDITOR_GET("interface/multi_window/restore_windows_on_load") && window_wrapper->is_window_available() && p_layout->has_section_key("ShaderEditor", "window_rect")) {
window_wrapper->restore_window_from_saved_position(
@@ -280,7 +269,7 @@ void ShaderEditorPlugin::get_window_layout(Ref<ConfigFile> p_layout) {
String selected_shader;
for (int i = 0; i < shader_tabs->get_tab_count(); i++) {
EditedShader edited_shader = edited_shaders[i];
- if (edited_shader.shader_editor || edited_shader.visual_shader_editor) {
+ if (edited_shader.shader_editor) {
String shader_path;
if (edited_shader.shader.is_valid()) {
shader_path = edited_shader.shader->get_path();
@@ -290,10 +279,9 @@ void ShaderEditorPlugin::get_window_layout(Ref<ConfigFile> p_layout) {
}
shaders.push_back(shader_path);
- TextShaderEditor *shader_editor = Object::cast_to<TextShaderEditor>(shader_tabs->get_current_tab_control());
- VisualShaderEditor *visual_shader_editor = Object::cast_to<VisualShaderEditor>(shader_tabs->get_current_tab_control());
+ ShaderEditor *shader_editor = Object::cast_to<ShaderEditor>(shader_tabs->get_current_tab_control());
- if ((shader_editor && edited_shader.shader_editor == shader_editor) || (visual_shader_editor && edited_shader.visual_shader_editor == visual_shader_editor)) {
+ if (shader_editor && edited_shader.shader_editor == shader_editor) {
selected_shader = shader_path;
}
}
@@ -366,10 +354,6 @@ void ShaderEditorPlugin::_shader_selected(int p_index) {
edited_shaders[p_index].shader_editor->validate_script();
}
- if (edited_shaders[p_index].visual_shader_editor) {
- edited_shaders[p_index].visual_shader_editor->validate_script();
- }
-
shader_tabs->set_current_tab(p_index);
shader_list->select(p_index);
}
@@ -440,7 +424,7 @@ void ShaderEditorPlugin::_menu_item_pressed(int p_index) {
case FILE_SAVE: {
int index = shader_tabs->get_current_tab();
ERR_FAIL_INDEX(index, shader_tabs->get_tab_count());
- TextShaderEditor *editor = edited_shaders[index].shader_editor;
+ TextShaderEditor *editor = Object::cast_to<TextShaderEditor>(edited_shaders[index].shader_editor);
if (editor) {
if (editor->get_trim_trailing_whitespace_on_save()) {
editor->trim_trailing_whitespace();
@@ -462,7 +446,7 @@ void ShaderEditorPlugin::_menu_item_pressed(int p_index) {
case FILE_SAVE_AS: {
int index = shader_tabs->get_current_tab();
ERR_FAIL_INDEX(index, shader_tabs->get_tab_count());
- TextShaderEditor *editor = edited_shaders[index].shader_editor;
+ TextShaderEditor *editor = Object::cast_to<TextShaderEditor>(edited_shaders[index].shader_editor);
if (editor) {
if (editor->get_trim_trailing_whitespace_on_save()) {
editor->trim_trailing_whitespace();
@@ -623,8 +607,9 @@ void ShaderEditorPlugin::_set_text_shader_zoom_factor(float p_zoom_factor) {
if (text_shader_zoom_factor != p_zoom_factor) {
text_shader_zoom_factor = p_zoom_factor;
for (const EditedShader &edited_shader : edited_shaders) {
- if (edited_shader.shader_editor) {
- CodeTextEditor *cte = edited_shader.shader_editor->get_code_editor();
+ TextShaderEditor *text_shader_editor = Object::cast_to<TextShaderEditor>(edited_shader.shader_editor);
+ if (text_shader_editor) {
+ CodeTextEditor *cte = text_shader_editor->get_code_editor();
if (cte && cte->get_zoom_factor() != text_shader_zoom_factor) {
cte->set_zoom_factor(text_shader_zoom_factor);
}
@@ -655,12 +640,13 @@ void ShaderEditorPlugin::_res_saved_callback(const Ref<Resource> &p_res) {
}
ERR_FAIL_COND(shader_res.is_null());
- if (!edited.shader_editor || !shader_res->is_built_in()) {
+ TextShaderEditor *text_shader_editor = Object::cast_to<TextShaderEditor>(edited.shader_editor);
+ if (!text_shader_editor || !shader_res->is_built_in()) {
continue;
}
if (shader_res->get_path().get_slice("::", 0) == path) {
- edited.shader_editor->tag_saved_version();
+ text_shader_editor->tag_saved_version();
_update_shader_list();
}
}
diff --git a/editor/plugins/shader_editor_plugin.h b/editor/plugins/shader_editor_plugin.h
index 386261d844..f9b9405e45 100644
--- a/editor/plugins/shader_editor_plugin.h
+++ b/editor/plugins/shader_editor_plugin.h
@@ -37,6 +37,7 @@ class HSplitContainer;
class ItemList;
class MenuButton;
class ShaderCreateDialog;
+class ShaderEditor;
class TabContainer;
class TextShaderEditor;
class VisualShaderEditor;
@@ -52,8 +53,7 @@ class ShaderEditorPlugin : public EditorPlugin {
struct EditedShader {
Ref<Shader> shader;
Ref<ShaderInclude> shader_inc;
- TextShaderEditor *shader_editor = nullptr;
- VisualShaderEditor *visual_shader_editor = nullptr;
+ ShaderEditor *shader_editor = nullptr;
String path;
String name;
};
@@ -121,8 +121,7 @@ public:
virtual void make_visible(bool p_visible) override;
virtual void selected_notify() override;
- TextShaderEditor *get_shader_editor(const Ref<Shader> &p_for_shader);
- VisualShaderEditor *get_visual_shader_editor(const Ref<Shader> &p_for_shader);
+ ShaderEditor *get_shader_editor(const Ref<Shader> &p_for_shader);
virtual void set_window_layout(Ref<ConfigFile> p_layout) override;
virtual void get_window_layout(Ref<ConfigFile> p_layout) override;
diff --git a/editor/plugins/shader_file_editor_plugin.cpp b/editor/plugins/shader_file_editor_plugin.cpp
index 05919fb0f6..d732d51f69 100644
--- a/editor/plugins/shader_file_editor_plugin.cpp
+++ b/editor/plugins/shader_file_editor_plugin.cpp
@@ -218,9 +218,6 @@ void ShaderFileEditor::_editor_settings_changed() {
}
}
-void ShaderFileEditor::_bind_methods() {
-}
-
void ShaderFileEditor::edit(const Ref<RDShaderFile> &p_shader) {
if (p_shader.is_null()) {
if (shader_file.is_valid()) {
diff --git a/editor/plugins/shader_file_editor_plugin.h b/editor/plugins/shader_file_editor_plugin.h
index 9a915513ef..fea770b7e0 100644
--- a/editor/plugins/shader_file_editor_plugin.h
+++ b/editor/plugins/shader_file_editor_plugin.h
@@ -62,7 +62,6 @@ class ShaderFileEditor : public PanelContainer {
protected:
void _notification(int p_what);
- static void _bind_methods();
public:
static ShaderFileEditor *singleton;
diff --git a/editor/plugins/skeleton_2d_editor_plugin.cpp b/editor/plugins/skeleton_2d_editor_plugin.cpp
index 8308fe6d6e..97c5c0c7dd 100644
--- a/editor/plugins/skeleton_2d_editor_plugin.cpp
+++ b/editor/plugins/skeleton_2d_editor_plugin.cpp
@@ -90,9 +90,6 @@ void Skeleton2DEditor::_menu_option(int p_option) {
}
}
-void Skeleton2DEditor::_bind_methods() {
-}
-
Skeleton2DEditor::Skeleton2DEditor() {
options = memnew(MenuButton);
diff --git a/editor/plugins/skeleton_2d_editor_plugin.h b/editor/plugins/skeleton_2d_editor_plugin.h
index 74fd59f1c4..89e2e5d0f2 100644
--- a/editor/plugins/skeleton_2d_editor_plugin.h
+++ b/editor/plugins/skeleton_2d_editor_plugin.h
@@ -57,7 +57,6 @@ class Skeleton2DEditor : public Control {
protected:
void _node_removed(Node *p_node);
- static void _bind_methods();
public:
void edit(Skeleton2D *p_sprite);
diff --git a/editor/plugins/skeleton_ik_3d_editor_plugin.cpp b/editor/plugins/skeleton_ik_3d_editor_plugin.cpp
index 1753df5cb7..9b98b6ffa2 100644
--- a/editor/plugins/skeleton_ik_3d_editor_plugin.cpp
+++ b/editor/plugins/skeleton_ik_3d_editor_plugin.cpp
@@ -73,9 +73,6 @@ void SkeletonIK3DEditorPlugin::make_visible(bool p_visible) {
}
}
-void SkeletonIK3DEditorPlugin::_bind_methods() {
-}
-
SkeletonIK3DEditorPlugin::SkeletonIK3DEditorPlugin() {
play_btn = memnew(Button);
play_btn->set_icon(EditorNode::get_singleton()->get_editor_theme()->get_icon(SNAME("Play"), EditorStringName(EditorIcons)));
diff --git a/editor/plugins/skeleton_ik_3d_editor_plugin.h b/editor/plugins/skeleton_ik_3d_editor_plugin.h
index 2ef5467263..95ab3696be 100644
--- a/editor/plugins/skeleton_ik_3d_editor_plugin.h
+++ b/editor/plugins/skeleton_ik_3d_editor_plugin.h
@@ -45,9 +45,6 @@ class SkeletonIK3DEditorPlugin : public EditorPlugin {
void _play();
-protected:
- static void _bind_methods();
-
public:
virtual String get_name() const override { return "SkeletonIK3D"; }
bool has_main_screen() const override { return false; }
diff --git a/editor/plugins/text_shader_editor.cpp b/editor/plugins/text_shader_editor.cpp
index 276c57533f..6cd92bdf57 100644
--- a/editor/plugins/text_shader_editor.cpp
+++ b/editor/plugins/text_shader_editor.cpp
@@ -895,7 +895,7 @@ void TextShaderEditor::_reload() {
}
}
-void TextShaderEditor::edit(const Ref<Shader> &p_shader) {
+void TextShaderEditor::edit_shader(const Ref<Shader> &p_shader) {
if (p_shader.is_null() || !p_shader->is_text_shader()) {
return;
}
@@ -910,7 +910,7 @@ void TextShaderEditor::edit(const Ref<Shader> &p_shader) {
code_editor->set_edited_shader(shader);
}
-void TextShaderEditor::edit(const Ref<ShaderInclude> &p_shader_inc) {
+void TextShaderEditor::edit_shader_include(const Ref<ShaderInclude> &p_shader_inc) {
if (p_shader_inc.is_null()) {
return;
}
@@ -1141,6 +1141,7 @@ TextShaderEditor::TextShaderEditor() {
context_menu->connect(SceneStringName(id_pressed), callable_mp(this, &TextShaderEditor::_menu_option));
VBoxContainer *main_container = memnew(VBoxContainer);
+ main_container->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT);
HBoxContainer *hbc = memnew(HBoxContainer);
edit_menu = memnew(MenuButton);
diff --git a/editor/plugins/text_shader_editor.h b/editor/plugins/text_shader_editor.h
index 61066ed7c6..55efb4e30e 100644
--- a/editor/plugins/text_shader_editor.h
+++ b/editor/plugins/text_shader_editor.h
@@ -32,6 +32,7 @@
#define TEXT_SHADER_EDITOR_H
#include "editor/code_editor.h"
+#include "editor/plugins/shader/shader_editor.h"
#include "scene/gui/margin_container.h"
#include "scene/gui/menu_button.h"
#include "scene/gui/rich_text_label.h"
@@ -104,8 +105,8 @@ public:
ShaderTextEditor();
};
-class TextShaderEditor : public MarginContainer {
- GDCLASS(TextShaderEditor, MarginContainer);
+class TextShaderEditor : public ShaderEditor {
+ GDCLASS(TextShaderEditor, ShaderEditor);
enum {
EDIT_UNDO,
@@ -188,19 +189,21 @@ protected:
void _bookmark_item_pressed(int p_idx);
public:
+ virtual void edit_shader(const Ref<Shader> &p_shader) override;
+ virtual void edit_shader_include(const Ref<ShaderInclude> &p_shader_inc) override;
+
+ virtual void apply_shaders() override;
+ virtual bool is_unsaved() const override;
+ virtual void save_external_data(const String &p_str = "") override;
+ virtual void validate_script() override;
+
bool was_compilation_successful() const { return compilation_success; }
bool get_trim_trailing_whitespace_on_save() const { return trim_trailing_whitespace_on_save; }
bool get_trim_final_newlines_on_save() const { return trim_final_newlines_on_save; }
- void apply_shaders();
void ensure_select_current();
- void edit(const Ref<Shader> &p_shader);
- void edit(const Ref<ShaderInclude> &p_shader_inc);
void goto_line_selection(int p_line, int p_begin, int p_end);
- void save_external_data(const String &p_str = "");
void trim_trailing_whitespace();
void trim_final_newlines();
- void validate_script();
- bool is_unsaved() const;
void tag_saved_version();
ShaderTextEditor *get_code_editor() { return code_editor; }
diff --git a/editor/plugins/tiles/tile_atlas_view.cpp b/editor/plugins/tiles/tile_atlas_view.cpp
index 3942baed8a..b806d1e042 100644
--- a/editor/plugins/tiles/tile_atlas_view.cpp
+++ b/editor/plugins/tiles/tile_atlas_view.cpp
@@ -372,7 +372,7 @@ void TileAtlasView::_draw_base_tiles_shape_grid() {
for (int i = 0; i < tile_set_atlas_source->get_tiles_count(); i++) {
Vector2i tile_id = tile_set_atlas_source->get_tile_id(i);
Vector2 in_tile_base_offset = tile_set_atlas_source->get_tile_data(tile_id, 0)->get_texture_origin();
- if (tile_set_atlas_source->is_position_in_tile_texture_region(tile_id, 0, -tile_shape_size / 2) && tile_set_atlas_source->is_position_in_tile_texture_region(tile_id, 0, tile_shape_size / 2 - Vector2(1, 1))) {
+ if (tile_set_atlas_source->is_rect_in_tile_texture_region(tile_id, 0, Rect2(Vector2(-tile_shape_size) / 2, tile_shape_size))) {
for (int frame = 0; frame < tile_set_atlas_source->get_tile_animation_frames_count(tile_id); frame++) {
Color color = grid_color;
if (frame > 0) {
diff --git a/editor/plugins/tiles/tile_data_editors.cpp b/editor/plugins/tiles/tile_data_editors.cpp
index f985bbc629..af52243c41 100644
--- a/editor/plugins/tiles/tile_data_editors.cpp
+++ b/editor/plugins/tiles/tile_data_editors.cpp
@@ -165,10 +165,14 @@ void GenericTilePolygonEditor::_base_control_draw() {
base_control->draw_set_transform_matrix(xform);
// Draw fill rect under texture region.
- Rect2 texture_rect(-background_region.size / 2, background_region.size);
+ Rect2 texture_rect(Vector2(), background_region.size);
if (tile_data) {
texture_rect.position -= tile_data->get_texture_origin();
+ if (tile_data->get_transpose()) {
+ texture_rect.size = Size2(texture_rect.size.y, texture_rect.size.x);
+ }
}
+ texture_rect.position -= texture_rect.size / 2; // Half-size offset must be applied after transposing.
base_control->draw_rect(texture_rect, Color(1, 1, 1, 0.3));
// Draw the background.
@@ -180,18 +184,14 @@ void GenericTilePolygonEditor::_base_control_draw() {
if (tile_data->get_flip_v()) {
region_size.y = -region_size.y;
}
- base_control->draw_texture_rect_region(background_atlas_source->get_texture(), Rect2(-background_region.size / 2 - tile_data->get_texture_origin(), region_size), background_region, tile_data->get_modulate(), tile_data->get_transpose());
+ // Destination rect position must account for transposing, size must not.
+ base_control->draw_texture_rect_region(background_atlas_source->get_texture(), Rect2(texture_rect.position, region_size), background_region, tile_data->get_modulate(), tile_data->get_transpose());
}
// Compute and draw the grid area.
Rect2 grid_area = Rect2(-base_tile_size / 2, base_tile_size);
- if (tile_data) {
- grid_area.expand_to(-background_region.get_size() / 2 - tile_data->get_texture_origin());
- grid_area.expand_to(background_region.get_size() / 2 - tile_data->get_texture_origin());
- } else {
- grid_area.expand_to(-background_region.get_size() / 2);
- grid_area.expand_to(background_region.get_size() / 2);
- }
+ grid_area.expand_to(texture_rect.position);
+ grid_area.expand_to(texture_rect.get_end());
base_control->draw_rect(grid_area, Color(1, 1, 1, 0.3), false);
// Draw grid.
@@ -523,6 +523,21 @@ void GenericTilePolygonEditor::_base_control_gui_input(Ref<InputEvent> p_event)
xform.set_origin(base_control->get_size() / 2 + panning);
xform.set_scale(Vector2(editor_zoom_widget->get_zoom(), editor_zoom_widget->get_zoom()));
+ Ref<InputEventPanGesture> pan_gesture = p_event;
+ if (pan_gesture.is_valid()) {
+ panning += pan_gesture->get_delta() * 8;
+ drag_last_pos = Vector2();
+ button_center_view->set_disabled(panning.is_zero_approx());
+ accept_event();
+ }
+
+ Ref<InputEventMagnifyGesture> magnify_gesture = p_event;
+ if (magnify_gesture.is_valid()) {
+ editor_zoom_widget->set_zoom(editor_zoom_widget->get_zoom() * magnify_gesture->get_factor());
+ _zoom_changed();
+ accept_event();
+ }
+
Ref<InputEventMouseMotion> mm = p_event;
if (mm.is_valid()) {
if (drag_type == DRAG_TYPE_DRAG_POINT) {
@@ -1385,10 +1400,8 @@ void TileDataTextureOriginEditor::draw_over_tile(CanvasItem *p_canvas_item, Tran
TileSetSource *source = *(tile_set->get_source(p_cell.source_id));
TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source);
- if (atlas_source->is_position_in_tile_texture_region(p_cell.get_atlas_coords(), p_cell.alternative_tile, -tile_set_tile_size / 2) && atlas_source->is_position_in_tile_texture_region(p_cell.get_atlas_coords(), p_cell.alternative_tile, tile_set_tile_size / 2 - Vector2(1, 1))) {
- Transform2D tile_xform;
- tile_xform.set_scale(tile_set_tile_size);
- tile_set->draw_tile_shape(p_canvas_item, p_transform * tile_xform, color);
+ if (atlas_source->is_rect_in_tile_texture_region(p_cell.get_atlas_coords(), p_cell.alternative_tile, Rect2(Vector2(-tile_set_tile_size) / 2, tile_set_tile_size))) {
+ tile_set->draw_tile_shape(p_canvas_item, p_transform.scaled_local(tile_set_tile_size), color);
}
if (atlas_source->is_position_in_tile_texture_region(p_cell.get_atlas_coords(), p_cell.alternative_tile, Vector2())) {
diff --git a/editor/plugins/tiles/tile_map_layer_editor.cpp b/editor/plugins/tiles/tile_map_layer_editor.cpp
index b76e673414..63a54372b5 100644
--- a/editor/plugins/tiles/tile_map_layer_editor.cpp
+++ b/editor/plugins/tiles/tile_map_layer_editor.cpp
@@ -2368,7 +2368,7 @@ TileMapLayerEditorTilesPlugin::TileMapLayerEditorTilesPlugin() {
tiles_bottom_panel->set_name(TTR("Tiles"));
missing_source_label = memnew(Label);
- missing_source_label->set_text(TTR("This TileMap's TileSet has no source configured. Go to the TileSet bottom panel to add one."));
+ missing_source_label->set_text(TTR("This TileMap's TileSet has no Tile Source configured. Go to the TileSet bottom panel to add one."));
missing_source_label->set_h_size_flags(Control::SIZE_EXPAND_FILL);
missing_source_label->set_v_size_flags(Control::SIZE_EXPAND_FILL);
missing_source_label->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_CENTER);
diff --git a/editor/plugins/tiles/tile_set_atlas_source_editor.cpp b/editor/plugins/tiles/tile_set_atlas_source_editor.cpp
index 7e34a36a6e..224c4e434f 100644
--- a/editor/plugins/tiles/tile_set_atlas_source_editor.cpp
+++ b/editor/plugins/tiles/tile_set_atlas_source_editor.cpp
@@ -2612,7 +2612,6 @@ TileSetAtlasSourceEditor::TileSetAtlasSourceEditor() {
atlas_source_inspector->set_v_size_flags(SIZE_EXPAND_FILL);
atlas_source_inspector->set_show_categories(false, true);
atlas_source_inspector->set_use_doc_hints(true);
- atlas_source_inspector->add_inspector_plugin(memnew(TileSourceInspectorPlugin));
middle_vbox_container->add_child(atlas_source_inspector);
// -- Right side --
@@ -2740,9 +2739,8 @@ TileSetAtlasSourceEditor::TileSetAtlasSourceEditor() {
add_child(confirm_auto_create_tiles);
// Inspector plugin.
- Ref<EditorInspectorPluginTileData> tile_data_inspector_plugin;
- tile_data_inspector_plugin.instantiate();
- EditorInspector::add_inspector_plugin(tile_data_inspector_plugin);
+ EditorInspector::add_inspector_plugin(memnew(EditorInspectorPluginTileData));
+ EditorInspector::add_inspector_plugin(memnew(TileSourceInspectorPlugin));
}
TileSetAtlasSourceEditor::~TileSetAtlasSourceEditor() {
diff --git a/editor/plugins/tiles/tile_set_editor.cpp b/editor/plugins/tiles/tile_set_editor.cpp
index 263e9cfa3b..7e5336ce06 100644
--- a/editor/plugins/tiles/tile_set_editor.cpp
+++ b/editor/plugins/tiles/tile_set_editor.cpp
@@ -818,7 +818,7 @@ TileSetEditor::TileSetEditor() {
tabs_bar = memnew(TabBar);
tabs_bar->set_tab_alignment(TabBar::ALIGNMENT_CENTER);
tabs_bar->set_clip_tabs(false);
- tabs_bar->add_tab(TTR("Tiles"));
+ tabs_bar->add_tab(TTR("Tile Sources"));
tabs_bar->add_tab(TTR("Patterns"));
tabs_bar->connect("tab_changed", callable_mp(this, &TileSetEditor::_tab_changed));
diff --git a/editor/plugins/tiles/tile_set_scenes_collection_source_editor.cpp b/editor/plugins/tiles/tile_set_scenes_collection_source_editor.cpp
index 22ef779b8d..305407efdb 100644
--- a/editor/plugins/tiles/tile_set_scenes_collection_source_editor.cpp
+++ b/editor/plugins/tiles/tile_set_scenes_collection_source_editor.cpp
@@ -534,7 +534,6 @@ TileSetScenesCollectionSourceEditor::TileSetScenesCollectionSourceEditor() {
scenes_collection_source_inspector = memnew(EditorInspector);
scenes_collection_source_inspector->set_vertical_scroll_mode(ScrollContainer::SCROLL_MODE_DISABLED);
scenes_collection_source_inspector->set_use_doc_hints(true);
- scenes_collection_source_inspector->add_inspector_plugin(memnew(TileSourceInspectorPlugin));
middle_vbox_container->add_child(scenes_collection_source_inspector);
// Tile inspector.
@@ -581,6 +580,8 @@ TileSetScenesCollectionSourceEditor::TileSetScenesCollectionSourceEditor() {
scene_tile_delete_button->set_disabled(true);
scene_tile_delete_button->connect(SceneStringName(pressed), callable_mp(this, &TileSetScenesCollectionSourceEditor::_source_delete_pressed));
scenes_bottom_actions->add_child(scene_tile_delete_button);
+
+ EditorInspector::add_inspector_plugin(memnew(TileSourceInspectorPlugin));
}
TileSetScenesCollectionSourceEditor::~TileSetScenesCollectionSourceEditor() {
diff --git a/editor/plugins/visual_shader_editor_plugin.cpp b/editor/plugins/visual_shader_editor_plugin.cpp
index f4ce35f8f2..b2669cf1af 100644
--- a/editor/plugins/visual_shader_editor_plugin.cpp
+++ b/editor/plugins/visual_shader_editor_plugin.cpp
@@ -1508,17 +1508,18 @@ Vector2 VisualShaderEditor::selection_center;
List<VisualShaderEditor::CopyItem> VisualShaderEditor::copy_items_buffer;
List<VisualShader::Connection> VisualShaderEditor::copy_connections_buffer;
-void VisualShaderEditor::edit(VisualShader *p_visual_shader) {
+void VisualShaderEditor::edit_shader(const Ref<Shader> &p_shader) {
bool changed = false;
- if (p_visual_shader) {
+ VisualShader *visual_shader_ptr = Object::cast_to<VisualShader>(p_shader.ptr());
+ if (visual_shader_ptr) {
if (visual_shader.is_null()) {
changed = true;
} else {
- if (visual_shader.ptr() != p_visual_shader) {
+ if (visual_shader.ptr() != visual_shader_ptr) {
changed = true;
}
}
- visual_shader = Ref<VisualShader>(p_visual_shader);
+ visual_shader = p_shader;
graph_plugin->register_shader(visual_shader.ptr());
visual_shader->connect_changed(callable_mp(this, &VisualShaderEditor::_update_preview));
@@ -1545,6 +1546,19 @@ void VisualShaderEditor::edit(VisualShader *p_visual_shader) {
}
}
+void VisualShaderEditor::apply_shaders() {
+ // Stub. TODO: Implement apply_shaders in visual shaders for parity with text shaders.
+}
+
+bool VisualShaderEditor::is_unsaved() const {
+ // Stub. TODO: Implement is_unsaved in visual shaders for parity with text shaders.
+ return false;
+}
+
+void VisualShaderEditor::save_external_data(const String &p_str) {
+ ResourceSaver::save(visual_shader, visual_shader->get_path());
+}
+
void VisualShaderEditor::validate_script() {
if (visual_shader.is_valid()) {
_update_nodes();
@@ -6053,8 +6067,7 @@ VisualShaderEditor::VisualShaderEditor() {
graph = memnew(GraphEdit);
graph->get_menu_hbox()->set_h_size_flags(SIZE_EXPAND_FILL);
- graph->set_v_size_flags(SIZE_EXPAND_FILL);
- graph->set_h_size_flags(SIZE_EXPAND_FILL);
+ graph->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT);
graph->set_grid_pattern(GraphEdit::GridPattern::GRID_PATTERN_DOTS);
int grid_pattern = EDITOR_GET("editors/visual_editors/grid_pattern");
graph->set_grid_pattern((GraphEdit::GridPattern)grid_pattern);
@@ -7561,7 +7574,7 @@ void EditorPropertyVisualShaderMode::_option_selected(int p_which) {
if (!shader_editor) {
return;
}
- VisualShaderEditor *editor = shader_editor->get_visual_shader_editor(visual_shader);
+ VisualShaderEditor *editor = Object::cast_to<VisualShaderEditor>(shader_editor->get_shader_editor(visual_shader));
if (!editor) {
return;
}
@@ -7652,9 +7665,6 @@ void EditorPropertyVisualShaderMode::set_option_button_clip(bool p_enable) {
options->set_clip_text(p_enable);
}
-void EditorPropertyVisualShaderMode::_bind_methods() {
-}
-
EditorPropertyVisualShaderMode::EditorPropertyVisualShaderMode() {
options = memnew(OptionButton);
options->set_clip_text(true);
@@ -7786,9 +7796,6 @@ void VisualShaderNodePortPreview::_notification(int p_what) {
}
}
-void VisualShaderNodePortPreview::_bind_methods() {
-}
-
//////////////////////////////////
String VisualShaderConversionPlugin::converts_to() const {
diff --git a/editor/plugins/visual_shader_editor_plugin.h b/editor/plugins/visual_shader_editor_plugin.h
index a9826fd617..6ce096f821 100644
--- a/editor/plugins/visual_shader_editor_plugin.h
+++ b/editor/plugins/visual_shader_editor_plugin.h
@@ -34,6 +34,7 @@
#include "editor/editor_properties.h"
#include "editor/plugins/editor_plugin.h"
#include "editor/plugins/editor_resource_conversion_plugin.h"
+#include "editor/plugins/shader/shader_editor.h"
#include "scene/gui/graph_edit.h"
#include "scene/resources/syntax_highlighter.h"
#include "scene/resources/visual_shader.h"
@@ -195,8 +196,8 @@ public:
VisualShaderEditedProperty() {}
};
-class VisualShaderEditor : public VBoxContainer {
- GDCLASS(VisualShaderEditor, VBoxContainer);
+class VisualShaderEditor : public ShaderEditor {
+ GDCLASS(VisualShaderEditor, ShaderEditor);
friend class VisualShaderGraphPlugin;
PopupPanel *property_editor_popup = nullptr;
@@ -596,6 +597,12 @@ protected:
static void _bind_methods();
public:
+ virtual void edit_shader(const Ref<Shader> &p_shader) override;
+ virtual void apply_shaders() override;
+ virtual bool is_unsaved() const override;
+ virtual void save_external_data(const String &p_str = "") override;
+ virtual void validate_script() override;
+
void add_plugin(const Ref<VisualShaderNodePlugin> &p_plugin);
void remove_plugin(const Ref<VisualShaderNodePlugin> &p_plugin);
@@ -609,11 +616,8 @@ public:
virtual Size2 get_minimum_size() const override;
- void edit(VisualShader *p_visual_shader);
Ref<VisualShader> get_visual_shader() const { return visual_shader; }
- void validate_script();
-
VisualShaderEditor();
};
@@ -630,9 +634,6 @@ class EditorPropertyVisualShaderMode : public EditorProperty {
void _option_selected(int p_which);
-protected:
- static void _bind_methods();
-
public:
void setup(const Vector<String> &p_options);
virtual void update_property() override;
@@ -658,7 +659,6 @@ class VisualShaderNodePortPreview : public Control {
void _shader_changed(); //must regen
protected:
void _notification(int p_what);
- static void _bind_methods();
public:
virtual Size2 get_minimum_size() const override;
diff --git a/editor/plugins/voxel_gi_editor_plugin.cpp b/editor/plugins/voxel_gi_editor_plugin.cpp
index e7b2435567..3e835d5cb6 100644
--- a/editor/plugins/voxel_gi_editor_plugin.cpp
+++ b/editor/plugins/voxel_gi_editor_plugin.cpp
@@ -177,9 +177,6 @@ void VoxelGIEditorPlugin::_voxel_gi_save_path_and_bake(const String &p_path) {
}
}
-void VoxelGIEditorPlugin::_bind_methods() {
-}
-
VoxelGIEditorPlugin::VoxelGIEditorPlugin() {
bake_hb = memnew(HBoxContainer);
bake_hb->set_h_size_flags(Control::SIZE_EXPAND_FILL);
diff --git a/editor/plugins/voxel_gi_editor_plugin.h b/editor/plugins/voxel_gi_editor_plugin.h
index 58ef22ddc6..d09822dda6 100644
--- a/editor/plugins/voxel_gi_editor_plugin.h
+++ b/editor/plugins/voxel_gi_editor_plugin.h
@@ -58,7 +58,6 @@ class VoxelGIEditorPlugin : public EditorPlugin {
void _voxel_gi_save_path_and_bake(const String &p_path);
protected:
- static void _bind_methods();
void _notification(int p_what);
public:
diff --git a/editor/project_manager/quick_settings_dialog.cpp b/editor/project_manager/quick_settings_dialog.cpp
index a98d9073b0..4502b42fe6 100644
--- a/editor/project_manager/quick_settings_dialog.cpp
+++ b/editor/project_manager/quick_settings_dialog.cpp
@@ -31,7 +31,7 @@
#include "quick_settings_dialog.h"
#include "core/config/project_settings.h"
-#include "core/string/translation.h"
+#include "core/string/translation_server.h"
#include "editor/editor_settings.h"
#include "editor/editor_string_names.h"
#include "editor/themes/editor_scale.h"
diff --git a/editor/property_selector.cpp b/editor/property_selector.cpp
index 0394c9e249..d47270841d 100644
--- a/editor/property_selector.cpp
+++ b/editor/property_selector.cpp
@@ -123,50 +123,6 @@ void PropertySelector::_update_search() {
TreeItem *category = nullptr;
bool found = false;
-
- Ref<Texture2D> type_icons[] = {
- search_options->get_editor_theme_icon(SNAME("Variant")),
- search_options->get_editor_theme_icon(SNAME("bool")),
- search_options->get_editor_theme_icon(SNAME("int")),
- search_options->get_editor_theme_icon(SNAME("float")),
- search_options->get_editor_theme_icon(SNAME("String")),
- search_options->get_editor_theme_icon(SNAME("Vector2")),
- search_options->get_editor_theme_icon(SNAME("Vector2i")),
- search_options->get_editor_theme_icon(SNAME("Rect2")),
- search_options->get_editor_theme_icon(SNAME("Rect2i")),
- search_options->get_editor_theme_icon(SNAME("Vector3")),
- search_options->get_editor_theme_icon(SNAME("Vector3i")),
- search_options->get_editor_theme_icon(SNAME("Transform2D")),
- search_options->get_editor_theme_icon(SNAME("Vector4")),
- search_options->get_editor_theme_icon(SNAME("Vector4i")),
- search_options->get_editor_theme_icon(SNAME("Plane")),
- search_options->get_editor_theme_icon(SNAME("Quaternion")),
- search_options->get_editor_theme_icon(SNAME("AABB")),
- search_options->get_editor_theme_icon(SNAME("Basis")),
- search_options->get_editor_theme_icon(SNAME("Transform3D")),
- search_options->get_editor_theme_icon(SNAME("Projection")),
- search_options->get_editor_theme_icon(SNAME("Color")),
- search_options->get_editor_theme_icon(SNAME("StringName")),
- search_options->get_editor_theme_icon(SNAME("NodePath")),
- search_options->get_editor_theme_icon(SNAME("RID")),
- search_options->get_editor_theme_icon(SNAME("MiniObject")),
- search_options->get_editor_theme_icon(SNAME("Callable")),
- search_options->get_editor_theme_icon(SNAME("Signal")),
- search_options->get_editor_theme_icon(SNAME("Dictionary")),
- search_options->get_editor_theme_icon(SNAME("Array")),
- search_options->get_editor_theme_icon(SNAME("PackedByteArray")),
- search_options->get_editor_theme_icon(SNAME("PackedInt32Array")),
- search_options->get_editor_theme_icon(SNAME("PackedInt64Array")),
- search_options->get_editor_theme_icon(SNAME("PackedFloat32Array")),
- search_options->get_editor_theme_icon(SNAME("PackedFloat64Array")),
- search_options->get_editor_theme_icon(SNAME("PackedStringArray")),
- search_options->get_editor_theme_icon(SNAME("PackedVector2Array")),
- search_options->get_editor_theme_icon(SNAME("PackedVector3Array")),
- search_options->get_editor_theme_icon(SNAME("PackedColorArray")),
- search_options->get_editor_theme_icon(SNAME("PackedVector4Array")),
- };
- static_assert((sizeof(type_icons) / sizeof(type_icons[0])) == Variant::VARIANT_MAX, "Number of type icons doesn't match the number of Variant types.");
-
for (const PropertyInfo &E : props) {
if (E.usage == PROPERTY_USAGE_CATEGORY) {
if (category && category->get_first_child() == nullptr) {
@@ -194,14 +150,14 @@ void PropertySelector::_update_search() {
continue;
}
- if (type_filter.size() && !type_filter.has(E.type)) {
+ if (!type_filter.is_empty() && !type_filter.has(E.type)) {
continue;
}
TreeItem *item = search_options->create_item(category ? category : root);
item->set_text(0, E.name);
item->set_metadata(0, E.name);
- item->set_icon(0, type_icons[E.type]);
+ item->set_icon(0, search_options->get_editor_theme_icon(Variant::get_type_name(E.type)));
if (!found && !search_box->get_text().is_empty() && E.name.containsn(search_text)) {
item->select(0);
@@ -209,6 +165,9 @@ void PropertySelector::_update_search() {
}
item->set_selectable(0, true);
+
+ _create_subproperties(item, E.type);
+ item->set_collapsed(true);
}
if (category && category->get_first_child() == nullptr) {
@@ -407,6 +366,133 @@ void PropertySelector::_hide_requested() {
_cancel_pressed(); // From AcceptDialog.
}
+void PropertySelector::_create_subproperties(TreeItem *p_parent_item, Variant::Type p_type) {
+ switch (p_type) {
+ case Variant::VECTOR2: {
+ _create_subproperty(p_parent_item, "x", Variant::FLOAT);
+ _create_subproperty(p_parent_item, "y", Variant::FLOAT);
+ } break;
+
+ case Variant::VECTOR2I: {
+ _create_subproperty(p_parent_item, "x", Variant::INT);
+ _create_subproperty(p_parent_item, "y", Variant::INT);
+ } break;
+
+ case Variant::RECT2: {
+ _create_subproperty(p_parent_item, "position", Variant::VECTOR2);
+ _create_subproperty(p_parent_item, "size", Variant::VECTOR2);
+ _create_subproperty(p_parent_item, "end", Variant::VECTOR2);
+ } break;
+
+ case Variant::RECT2I: {
+ _create_subproperty(p_parent_item, "position", Variant::VECTOR2I);
+ _create_subproperty(p_parent_item, "size", Variant::VECTOR2I);
+ _create_subproperty(p_parent_item, "end", Variant::VECTOR2I);
+ } break;
+
+ case Variant::VECTOR3: {
+ _create_subproperty(p_parent_item, "x", Variant::FLOAT);
+ _create_subproperty(p_parent_item, "y", Variant::FLOAT);
+ _create_subproperty(p_parent_item, "z", Variant::FLOAT);
+ } break;
+
+ case Variant::VECTOR3I: {
+ _create_subproperty(p_parent_item, "x", Variant::INT);
+ _create_subproperty(p_parent_item, "y", Variant::INT);
+ _create_subproperty(p_parent_item, "z", Variant::INT);
+ } break;
+
+ case Variant::TRANSFORM2D: {
+ _create_subproperty(p_parent_item, "origin", Variant::VECTOR2);
+ _create_subproperty(p_parent_item, "x", Variant::VECTOR2);
+ _create_subproperty(p_parent_item, "y", Variant::VECTOR2);
+ } break;
+
+ case Variant::VECTOR4: {
+ _create_subproperty(p_parent_item, "x", Variant::FLOAT);
+ _create_subproperty(p_parent_item, "y", Variant::FLOAT);
+ _create_subproperty(p_parent_item, "z", Variant::FLOAT);
+ _create_subproperty(p_parent_item, "w", Variant::FLOAT);
+ } break;
+
+ case Variant::VECTOR4I: {
+ _create_subproperty(p_parent_item, "x", Variant::INT);
+ _create_subproperty(p_parent_item, "y", Variant::INT);
+ _create_subproperty(p_parent_item, "z", Variant::INT);
+ _create_subproperty(p_parent_item, "w", Variant::INT);
+ } break;
+
+ case Variant::PLANE: {
+ _create_subproperty(p_parent_item, "x", Variant::FLOAT);
+ _create_subproperty(p_parent_item, "y", Variant::FLOAT);
+ _create_subproperty(p_parent_item, "z", Variant::FLOAT);
+ _create_subproperty(p_parent_item, "normal", Variant::VECTOR3);
+ _create_subproperty(p_parent_item, "d", Variant::FLOAT);
+ } break;
+
+ case Variant::QUATERNION: {
+ _create_subproperty(p_parent_item, "x", Variant::FLOAT);
+ _create_subproperty(p_parent_item, "y", Variant::FLOAT);
+ _create_subproperty(p_parent_item, "z", Variant::FLOAT);
+ _create_subproperty(p_parent_item, "w", Variant::FLOAT);
+ } break;
+
+ case Variant::AABB: {
+ _create_subproperty(p_parent_item, "position", Variant::VECTOR3);
+ _create_subproperty(p_parent_item, "size", Variant::VECTOR3);
+ _create_subproperty(p_parent_item, "end", Variant::VECTOR3);
+ } break;
+
+ case Variant::BASIS: {
+ _create_subproperty(p_parent_item, "x", Variant::VECTOR3);
+ _create_subproperty(p_parent_item, "y", Variant::VECTOR3);
+ _create_subproperty(p_parent_item, "z", Variant::VECTOR3);
+ } break;
+
+ case Variant::TRANSFORM3D: {
+ _create_subproperty(p_parent_item, "basis", Variant::BASIS);
+ _create_subproperty(p_parent_item, "origin", Variant::VECTOR3);
+ } break;
+
+ case Variant::PROJECTION: {
+ _create_subproperty(p_parent_item, "x", Variant::VECTOR4);
+ _create_subproperty(p_parent_item, "y", Variant::VECTOR4);
+ _create_subproperty(p_parent_item, "z", Variant::VECTOR4);
+ _create_subproperty(p_parent_item, "w", Variant::VECTOR4);
+ } break;
+
+ case Variant::COLOR: {
+ _create_subproperty(p_parent_item, "r", Variant::FLOAT);
+ _create_subproperty(p_parent_item, "g", Variant::FLOAT);
+ _create_subproperty(p_parent_item, "b", Variant::FLOAT);
+ _create_subproperty(p_parent_item, "a", Variant::FLOAT);
+ _create_subproperty(p_parent_item, "r8", Variant::INT);
+ _create_subproperty(p_parent_item, "g8", Variant::INT);
+ _create_subproperty(p_parent_item, "b8", Variant::INT);
+ _create_subproperty(p_parent_item, "a8", Variant::INT);
+ _create_subproperty(p_parent_item, "h", Variant::FLOAT);
+ _create_subproperty(p_parent_item, "s", Variant::FLOAT);
+ _create_subproperty(p_parent_item, "v", Variant::FLOAT);
+ } break;
+
+ default: {
+ }
+ }
+}
+
+void PropertySelector::_create_subproperty(TreeItem *p_parent_item, const String &p_name, Variant::Type p_type) {
+ if (!type_filter.is_empty() && !type_filter.has(p_type)) {
+ return;
+ }
+
+ TreeItem *item = search_options->create_item(p_parent_item);
+ item->set_text(0, p_name);
+ item->set_metadata(0, String(p_parent_item->get_metadata(0)) + ":" + p_name);
+ item->set_icon(0, search_options->get_editor_theme_icon(Variant::get_type_name(p_type)));
+
+ _create_subproperties(item, p_type);
+}
+
void PropertySelector::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_ENTER_TREE: {
@@ -576,7 +662,6 @@ PropertySelector::PropertySelector() {
search_options->connect("item_activated", callable_mp(this, &PropertySelector::_confirmed));
search_options->connect("cell_selected", callable_mp(this, &PropertySelector::_item_selected));
search_options->set_hide_root(true);
- search_options->set_hide_folding(true);
help_bit = memnew(EditorHelpBit);
help_bit->set_content_height_limits(80 * EDSCALE, 80 * EDSCALE);
diff --git a/editor/property_selector.h b/editor/property_selector.h
index b162ee6cfd..34cade9267 100644
--- a/editor/property_selector.h
+++ b/editor/property_selector.h
@@ -36,6 +36,7 @@
class EditorHelpBit;
class LineEdit;
class Tree;
+class TreeItem;
class PropertySelector : public ConfirmationDialog {
GDCLASS(PropertySelector, ConfirmationDialog);
@@ -62,6 +63,9 @@ class PropertySelector : public ConfirmationDialog {
Vector<Variant::Type> type_filter;
+ void _create_subproperties(TreeItem *p_parent_item, Variant::Type p_type);
+ void _create_subproperty(TreeItem *p_parent_item, const String &p_name, Variant::Type p_type);
+
protected:
void _notification(int p_what);
static void _bind_methods();
diff --git a/editor/scene_tree_dock.cpp b/editor/scene_tree_dock.cpp
index 56924fc1fe..30050901d9 100644
--- a/editor/scene_tree_dock.cpp
+++ b/editor/scene_tree_dock.cpp
@@ -1551,7 +1551,7 @@ void SceneTreeDock::_notification(int p_what) {
node_shortcuts_toggle->set_tooltip_text(TTR("Toggle the display of favorite nodes."));
node_shortcuts_toggle->set_pressed(EDITOR_GET("_use_favorites_root_selection"));
node_shortcuts_toggle->set_anchors_and_offsets_preset(Control::PRESET_CENTER_RIGHT);
- node_shortcuts_toggle->connect(SceneStringName(pressed), callable_mp(this, &SceneTreeDock::_update_create_root_dialog));
+ node_shortcuts_toggle->connect(SceneStringName(pressed), callable_mp(this, &SceneTreeDock::_update_create_root_dialog).bind(false));
top_row->add_child(node_shortcuts_toggle);
create_root_dialog->add_child(top_row);
@@ -1601,7 +1601,7 @@ void SceneTreeDock::_notification(int p_what) {
button_clipboard->set_icon(get_editor_theme_icon(SNAME("ActionPaste")));
button_clipboard->connect(SceneStringName(pressed), callable_mp(this, &SceneTreeDock::_tool_selected).bind(TOOL_PASTE, false));
- _update_create_root_dialog();
+ _update_create_root_dialog(true);
} break;
case NOTIFICATION_ENTER_TREE: {
@@ -4116,9 +4116,12 @@ void SceneTreeDock::_local_tree_selected() {
edit_local->set_pressed(true);
}
-void SceneTreeDock::_update_create_root_dialog() {
- EditorSettings::get_singleton()->set_setting("_use_favorites_root_selection", node_shortcuts_toggle->is_pressed());
- EditorSettings::get_singleton()->save();
+void SceneTreeDock::_update_create_root_dialog(bool p_initializing) {
+ if (!p_initializing) {
+ EditorSettings::get_singleton()->set_setting("_use_favorites_root_selection", node_shortcuts_toggle->is_pressed());
+ EditorSettings::get_singleton()->save();
+ }
+
if (node_shortcuts_toggle->is_pressed()) {
for (int i = 0; i < favorite_node_shortcuts->get_child_count(); i++) {
favorite_node_shortcuts->get_child(i)->queue_free();
@@ -4542,7 +4545,7 @@ SceneTreeDock::SceneTreeDock(Node *p_scene_root, EditorSelection *p_editor_selec
create_dialog->set_base_type("Node");
add_child(create_dialog);
create_dialog->connect("create", callable_mp(this, &SceneTreeDock::_create));
- create_dialog->connect("favorites_updated", callable_mp(this, &SceneTreeDock::_update_create_root_dialog));
+ create_dialog->connect("favorites_updated", callable_mp(this, &SceneTreeDock::_update_create_root_dialog).bind(false));
#ifdef MODULE_REGEX_ENABLED
rename_dialog = memnew(RenameDialog(scene_tree));
diff --git a/editor/scene_tree_dock.h b/editor/scene_tree_dock.h
index 5028cd5cc9..f01e6598cf 100644
--- a/editor/scene_tree_dock.h
+++ b/editor/scene_tree_dock.h
@@ -282,7 +282,7 @@ class SceneTreeDock : public VBoxContainer {
void _remote_tree_selected();
void _local_tree_selected();
- void _update_create_root_dialog();
+ void _update_create_root_dialog(bool p_initializing = false);
void _favorite_root_selected(const String &p_class);
void _feature_profile_changed();
diff --git a/editor/themes/editor_fonts.cpp b/editor/themes/editor_fonts.cpp
index fc994a17d1..c50d1237e0 100644
--- a/editor/themes/editor_fonts.cpp
+++ b/editor/themes/editor_fonts.cpp
@@ -117,6 +117,7 @@ void editor_register_fonts(const Ref<Theme> &p_theme) {
int font_hinting_setting = (int)EDITOR_GET("interface/editor/font_hinting");
TextServer::SubpixelPositioning font_subpixel_positioning = (TextServer::SubpixelPositioning)(int)EDITOR_GET("interface/editor/font_subpixel_positioning");
bool font_disable_embedded_bitmaps = (bool)EDITOR_GET("interface/editor/font_disable_embedded_bitmaps");
+ bool font_allow_msdf = (bool)EDITOR_GET("interface/editor/font_allow_msdf");
TextServer::Hinting font_hinting;
TextServer::Hinting font_mono_hinting;
@@ -153,7 +154,7 @@ void editor_register_fonts(const Ref<Theme> &p_theme) {
const float embolden_strength = 0.6;
Ref<Font> default_font = load_internal_font(_font_NotoSans_Regular, _font_NotoSans_Regular_size, font_hinting, font_antialiasing, true, font_subpixel_positioning, font_disable_embedded_bitmaps, false);
- Ref<Font> default_font_msdf = load_internal_font(_font_NotoSans_Regular, _font_NotoSans_Regular_size, font_hinting, font_antialiasing, true, font_subpixel_positioning, font_disable_embedded_bitmaps, true);
+ Ref<Font> default_font_msdf = load_internal_font(_font_NotoSans_Regular, _font_NotoSans_Regular_size, font_hinting, font_antialiasing, true, font_subpixel_positioning, font_disable_embedded_bitmaps, font_allow_msdf);
TypedArray<Font> fallbacks;
Ref<FontFile> arabic_font = load_internal_font(_font_NotoNaskhArabicUI_Regular, _font_NotoNaskhArabicUI_Regular_size, font_hinting, font_antialiasing, true, font_subpixel_positioning, font_disable_embedded_bitmaps, false, &fallbacks);
@@ -173,7 +174,7 @@ void editor_register_fonts(const Ref<Theme> &p_theme) {
default_font_msdf->set_fallbacks(fallbacks);
Ref<FontFile> default_font_bold = load_internal_font(_font_NotoSans_Bold, _font_NotoSans_Bold_size, font_hinting, font_antialiasing, true, font_subpixel_positioning, font_disable_embedded_bitmaps, false);
- Ref<FontFile> default_font_bold_msdf = load_internal_font(_font_NotoSans_Bold, _font_NotoSans_Bold_size, font_hinting, font_antialiasing, true, font_subpixel_positioning, font_disable_embedded_bitmaps, true);
+ Ref<FontFile> default_font_bold_msdf = load_internal_font(_font_NotoSans_Bold, _font_NotoSans_Bold_size, font_hinting, font_antialiasing, true, font_subpixel_positioning, font_disable_embedded_bitmaps, font_allow_msdf);
TypedArray<Font> fallbacks_bold;
Ref<FontFile> arabic_font_bold = load_internal_font(_font_NotoNaskhArabicUI_Bold, _font_NotoNaskhArabicUI_Bold_size, font_hinting, font_antialiasing, true, font_subpixel_positioning, font_disable_embedded_bitmaps, false, &fallbacks_bold);
@@ -234,7 +235,7 @@ void editor_register_fonts(const Ref<Theme> &p_theme) {
Ref<FontVariation> default_fc_msdf;
default_fc_msdf.instantiate();
if (custom_font_path.length() > 0 && dir->file_exists(custom_font_path)) {
- Ref<FontFile> custom_font = load_external_font(custom_font_path, font_hinting, font_antialiasing, true, font_subpixel_positioning, font_disable_embedded_bitmaps);
+ Ref<FontFile> custom_font = load_external_font(custom_font_path, font_hinting, font_antialiasing, true, font_subpixel_positioning, font_disable_embedded_bitmaps, font_allow_msdf);
{
TypedArray<Font> fallback_custom;
fallback_custom.push_back(default_font_msdf);
@@ -277,7 +278,7 @@ void editor_register_fonts(const Ref<Theme> &p_theme) {
Ref<FontVariation> bold_fc_msdf;
bold_fc_msdf.instantiate();
if (custom_font_path_bold.length() > 0 && dir->file_exists(custom_font_path_bold)) {
- Ref<FontFile> custom_font = load_external_font(custom_font_path_bold, font_hinting, font_antialiasing, true, font_subpixel_positioning, font_disable_embedded_bitmaps);
+ Ref<FontFile> custom_font = load_external_font(custom_font_path_bold, font_hinting, font_antialiasing, true, font_subpixel_positioning, font_disable_embedded_bitmaps, font_allow_msdf);
{
TypedArray<Font> fallback_custom;
fallback_custom.push_back(default_font_bold_msdf);
@@ -285,7 +286,7 @@ void editor_register_fonts(const Ref<Theme> &p_theme) {
}
bold_fc_msdf->set_base_font(custom_font);
} else if (custom_font_path.length() > 0 && dir->file_exists(custom_font_path)) {
- Ref<FontFile> custom_font = load_external_font(custom_font_path, font_hinting, font_antialiasing, true, font_subpixel_positioning, font_disable_embedded_bitmaps);
+ Ref<FontFile> custom_font = load_external_font(custom_font_path, font_hinting, font_antialiasing, true, font_subpixel_positioning, font_disable_embedded_bitmaps, font_allow_msdf);
{
TypedArray<Font> fallback_custom;
fallback_custom.push_back(default_font_bold_msdf);
diff --git a/editor/themes/editor_theme_manager.cpp b/editor/themes/editor_theme_manager.cpp
index 9d8cbb053d..b271970f21 100644
--- a/editor/themes/editor_theme_manager.cpp
+++ b/editor/themes/editor_theme_manager.cpp
@@ -865,7 +865,7 @@ void EditorThemeManager::_populate_standard_styles(const Ref<EditorTheme> &p_the
// CheckBox.
{
Ref<StyleBoxFlat> checkbox_style = p_config.panel_container_style->duplicate();
- checkbox_style->set_content_margin_all(p_config.base_margin * EDSCALE);
+ checkbox_style->set_content_margin_individual((p_config.increased_margin + 2) * EDSCALE, p_config.base_margin * EDSCALE, (p_config.increased_margin + 2) * EDSCALE, p_config.base_margin * EDSCALE);
p_theme->set_stylebox(CoreStringName(normal), "CheckBox", checkbox_style);
p_theme->set_stylebox(SceneStringName(pressed), "CheckBox", checkbox_style);
diff --git a/main/main.cpp b/main/main.cpp
index d0b58d9abd..bdae1bb1b0 100644
--- a/main/main.cpp
+++ b/main/main.cpp
@@ -49,7 +49,7 @@
#include "core/os/os.h"
#include "core/os/time.h"
#include "core/register_core_types.h"
-#include "core/string/translation.h"
+#include "core/string/translation_server.h"
#include "core/version.h"
#include "drivers/register_driver_types.h"
#include "main/app_icon.gen.h"
@@ -1511,6 +1511,9 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
main_args.push_back(arg);
main_args.push_back(N->get());
N = N->next();
+ // GDScript docgen requires Autoloads, but loading those also creates a main loop.
+ // This forces main loop to quit without adding more GDScript-specific exceptions to setup.
+ quit_after = 1;
} else {
OS::get_singleton()->print("Missing relative or absolute path to project for --gdscript-docs, aborting.\n");
goto error;
@@ -2537,7 +2540,10 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
OS::get_singleton()->benchmark_end_measure("Startup", "Main::Setup");
if (p_second_phase) {
- return setup2();
+ exit_err = setup2();
+ if (exit_err != OK) {
+ goto error;
+ }
}
return OK;
@@ -2835,6 +2841,30 @@ Error Main::setup2(bool p_show_boot_logo) {
if (err != OK || display_server == nullptr) {
ERR_PRINT("Unable to create DisplayServer, all display drivers failed.\nUse \"--headless\" command line argument to run the engine in headless mode if this is desired (e.g. for continuous integration).");
+
+ if (display_server) {
+ memdelete(display_server);
+ }
+
+ GDExtensionManager::get_singleton()->deinitialize_extensions(GDExtension::INITIALIZATION_LEVEL_SERVERS);
+ uninitialize_modules(MODULE_INITIALIZATION_LEVEL_SERVERS);
+ unregister_server_types();
+
+ if (input) {
+ memdelete(input);
+ }
+ if (tsman) {
+ memdelete(tsman);
+ }
+#ifndef _3D_DISABLED
+ if (physics_server_3d_manager) {
+ memdelete(physics_server_3d_manager);
+ }
+#endif // _3D_DISABLED
+ if (physics_server_2d_manager) {
+ memdelete(physics_server_2d_manager);
+ }
+
return err;
}
@@ -3019,9 +3049,6 @@ Error Main::setup2(bool p_show_boot_logo) {
id->set_emulate_mouse_from_touch(bool(GLOBAL_DEF_BASIC("input_devices/pointing/emulate_mouse_from_touch", true)));
}
- GLOBAL_DEF_BASIC("input_devices/pointing/android/enable_long_press_as_right_click", false);
- GLOBAL_DEF_BASIC("input_devices/pointing/android/enable_pan_and_scale_gestures", false);
- GLOBAL_DEF_BASIC(PropertyInfo(Variant::INT, "input_devices/pointing/android/rotary_input_scroll_axis", PROPERTY_HINT_ENUM, "Horizontal,Vertical"), 1);
OS::get_singleton()->benchmark_end_measure("Startup", "Setup Window and Boot");
}
@@ -3134,6 +3161,14 @@ Error Main::setup2(bool p_show_boot_logo) {
OS::get_singleton()->benchmark_end_measure("Scene", "Modules and Extensions");
}
+ PackedStringArray extensions;
+ extensions.push_back("gd");
+ if (ClassDB::class_exists("CSharpScript")) {
+ extensions.push_back("cs");
+ }
+ extensions.push_back("gdshader");
+ GLOBAL_DEF_NOVAL(PropertyInfo(Variant::PACKED_STRING_ARRAY, "editor/script/search_in_file_extensions"), extensions); // Note: should be defined after Scene level modules init to see .NET.
+
OS::get_singleton()->benchmark_end_measure("Startup", "Scene");
#ifdef TOOLS_ENABLED
diff --git a/main/performance.cpp b/main/performance.cpp
index 8eda697b16..0547b3bff0 100644
--- a/main/performance.cpp
+++ b/main/performance.cpp
@@ -91,6 +91,7 @@ void Performance::_bind_methods() {
BIND_ENUM_CONSTANT(NAVIGATION_EDGE_MERGE_COUNT);
BIND_ENUM_CONSTANT(NAVIGATION_EDGE_CONNECTION_COUNT);
BIND_ENUM_CONSTANT(NAVIGATION_EDGE_FREE_COUNT);
+ BIND_ENUM_CONSTANT(NAVIGATION_OBSTACLE_COUNT);
BIND_ENUM_CONSTANT(MONITOR_MAX);
}
@@ -141,6 +142,7 @@ String Performance::get_monitor_name(Monitor p_monitor) const {
PNAME("navigation/edges_merged"),
PNAME("navigation/edges_connected"),
PNAME("navigation/edges_free"),
+ PNAME("navigation/obstacles"),
};
@@ -225,6 +227,8 @@ double Performance::get_monitor(Monitor p_monitor) const {
return NavigationServer3D::get_singleton()->get_process_info(NavigationServer3D::INFO_EDGE_CONNECTION_COUNT);
case NAVIGATION_EDGE_FREE_COUNT:
return NavigationServer3D::get_singleton()->get_process_info(NavigationServer3D::INFO_EDGE_FREE_COUNT);
+ case NAVIGATION_OBSTACLE_COUNT:
+ return NavigationServer3D::get_singleton()->get_process_info(NavigationServer3D::INFO_OBSTACLE_COUNT);
default: {
}
@@ -272,6 +276,7 @@ Performance::MonitorType Performance::get_monitor_type(Monitor p_monitor) const
MONITOR_TYPE_QUANTITY,
MONITOR_TYPE_QUANTITY,
MONITOR_TYPE_QUANTITY,
+ MONITOR_TYPE_QUANTITY,
};
diff --git a/main/performance.h b/main/performance.h
index 34162b2da9..05d678fe55 100644
--- a/main/performance.h
+++ b/main/performance.h
@@ -100,6 +100,7 @@ public:
NAVIGATION_EDGE_MERGE_COUNT,
NAVIGATION_EDGE_CONNECTION_COUNT,
NAVIGATION_EDGE_FREE_COUNT,
+ NAVIGATION_OBSTACLE_COUNT,
MONITOR_MAX
};
diff --git a/modules/csg/csg_shape.cpp b/modules/csg/csg_shape.cpp
index 296cda627a..8777651545 100644
--- a/modules/csg/csg_shape.cpp
+++ b/modules/csg/csg_shape.cpp
@@ -460,27 +460,31 @@ void CSGShape3D::_update_shape() {
_update_collision_faces();
}
-void CSGShape3D::_update_collision_faces() {
- if (use_collision && is_root_shape() && root_collision_shape.is_valid()) {
- CSGBrush *n = _get_brush();
- ERR_FAIL_NULL_MSG(n, "Cannot get CSGBrush.");
- Vector<Vector3> physics_faces;
- physics_faces.resize(n->faces.size() * 3);
- Vector3 *physicsw = physics_faces.ptrw();
-
- for (int i = 0; i < n->faces.size(); i++) {
- int order[3] = { 0, 1, 2 };
+Vector<Vector3> CSGShape3D::_get_brush_collision_faces() {
+ Vector<Vector3> collision_faces;
+ CSGBrush *n = _get_brush();
+ ERR_FAIL_NULL_V_MSG(n, collision_faces, "Cannot get CSGBrush.");
+ collision_faces.resize(n->faces.size() * 3);
+ Vector3 *collision_faces_ptrw = collision_faces.ptrw();
- if (n->faces[i].invert) {
- SWAP(order[1], order[2]);
- }
+ for (int i = 0; i < n->faces.size(); i++) {
+ int order[3] = { 0, 1, 2 };
- physicsw[i * 3 + 0] = n->faces[i].vertices[order[0]];
- physicsw[i * 3 + 1] = n->faces[i].vertices[order[1]];
- physicsw[i * 3 + 2] = n->faces[i].vertices[order[2]];
+ if (n->faces[i].invert) {
+ SWAP(order[1], order[2]);
}
- root_collision_shape->set_faces(physics_faces);
+ collision_faces_ptrw[i * 3 + 0] = n->faces[i].vertices[order[0]];
+ collision_faces_ptrw[i * 3 + 1] = n->faces[i].vertices[order[1]];
+ collision_faces_ptrw[i * 3 + 2] = n->faces[i].vertices[order[2]];
+ }
+
+ return collision_faces;
+}
+
+void CSGShape3D::_update_collision_faces() {
+ if (use_collision && is_root_shape() && root_collision_shape.is_valid()) {
+ root_collision_shape->set_faces(_get_brush_collision_faces());
if (_is_debug_collision_shape_visible()) {
_update_debug_collision_shape();
@@ -488,6 +492,26 @@ void CSGShape3D::_update_collision_faces() {
}
}
+Ref<ArrayMesh> CSGShape3D::bake_static_mesh() {
+ Ref<ArrayMesh> baked_mesh;
+ if (is_root_shape() && root_mesh.is_valid()) {
+ baked_mesh = root_mesh;
+ }
+ return baked_mesh;
+}
+
+Ref<ConcavePolygonShape3D> CSGShape3D::bake_collision_shape() {
+ Ref<ConcavePolygonShape3D> baked_collision_shape;
+ if (is_root_shape() && root_collision_shape.is_valid()) {
+ baked_collision_shape.instantiate();
+ baked_collision_shape->set_faces(root_collision_shape->get_faces());
+ } else if (is_root_shape()) {
+ baked_collision_shape.instantiate();
+ baked_collision_shape->set_faces(_get_brush_collision_faces());
+ }
+ return baked_collision_shape;
+}
+
bool CSGShape3D::_is_debug_collision_shape_visible() {
return is_inside_tree() && (get_tree()->is_debugging_collisions_hint() || Engine::get_singleton()->is_editor_hint());
}
@@ -704,6 +728,9 @@ void CSGShape3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_meshes"), &CSGShape3D::get_meshes);
+ ClassDB::bind_method(D_METHOD("bake_static_mesh"), &CSGShape3D::bake_static_mesh);
+ ClassDB::bind_method(D_METHOD("bake_collision_shape"), &CSGShape3D::bake_collision_shape);
+
ADD_PROPERTY(PropertyInfo(Variant::INT, "operation", PROPERTY_HINT_ENUM, "Union,Intersection,Subtraction"), "set_operation", "get_operation");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "snap", PROPERTY_HINT_RANGE, "0.000001,1,0.000001,suffix:m"), "set_snap", "get_snap");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "calculate_tangents"), "set_calculate_tangents", "is_calculating_tangents");
@@ -934,7 +961,8 @@ CSGBrush *CSGMesh3D::_build_brush() {
void CSGMesh3D::_mesh_changed() {
_make_dirty();
- update_gizmos();
+
+ callable_mp((Node3D *)this, &Node3D::update_gizmos).call_deferred();
}
void CSGMesh3D::set_material(const Ref<Material> &p_material) {
diff --git a/modules/csg/csg_shape.h b/modules/csg/csg_shape.h
index bb7c8be431..8f23ae2f9e 100644
--- a/modules/csg/csg_shape.h
+++ b/modules/csg/csg_shape.h
@@ -113,6 +113,7 @@ private:
void _update_debug_collision_shape();
void _clear_debug_collision_shape();
void _on_transform_changed();
+ Vector<Vector3> _get_brush_collision_faces();
protected:
void _notification(int p_what);
@@ -161,6 +162,10 @@ public:
bool is_calculating_tangents() const;
bool is_root_shape() const;
+
+ Ref<ArrayMesh> bake_static_mesh();
+ Ref<ConcavePolygonShape3D> bake_collision_shape();
+
CSGShape3D();
~CSGShape3D();
};
diff --git a/modules/csg/doc_classes/CSGShape3D.xml b/modules/csg/doc_classes/CSGShape3D.xml
index f9017e47c7..ac62d8dd83 100644
--- a/modules/csg/doc_classes/CSGShape3D.xml
+++ b/modules/csg/doc_classes/CSGShape3D.xml
@@ -5,12 +5,29 @@
</brief_description>
<description>
This is the CSG base class that provides CSG operation support to the various CSG nodes in Godot.
- [b]Note:[/b] CSG nodes are intended to be used for level prototyping. Creating CSG nodes has a significant CPU cost compared to creating a [MeshInstance3D] with a [PrimitiveMesh]. Moving a CSG node within another CSG node also has a significant CPU cost, so it should be avoided during gameplay.
+ [b]Performance:[/b] CSG nodes are only intended for prototyping as they have a significant CPU performance cost.
+ Consider baking final CSG operation results into static geometry that replaces the CSG nodes.
+ Individual CSG root node results can be baked to nodes with static resources with the editor menu that appears when a CSG root node is selected.
+ Individual CSG root nodes can also be baked to static resources with scripts by calling [method bake_static_mesh] for the visual mesh or [method bake_collision_shape] for the physics collision.
+ Entire scenes of CSG nodes can be baked to static geometry and exported with the editor gltf scene exporter.
</description>
<tutorials>
<link title="Prototyping levels with CSG">$DOCS_URL/tutorials/3d/csg_tools.html</link>
</tutorials>
<methods>
+ <method name="bake_collision_shape">
+ <return type="ConcavePolygonShape3D" />
+ <description>
+ Returns a baked physics [ConcavePolygonShape3D] of this node's CSG operation result. Returns an empty shape if the node is not a CSG root node or has no valid geometry.
+ [b]Performance:[/b] If the CSG operation results in a very detailed geometry with many faces physics performance will be very slow. Concave shapes should in general only be used for static level geometry and not with dynamic objects that are moving.
+ </description>
+ </method>
+ <method name="bake_static_mesh">
+ <return type="ArrayMesh" />
+ <description>
+ Returns a baked static [ArrayMesh] of this node's CSG operation result. Materials from involved CSG nodes are added as extra mesh surfaces. Returns an empty mesh if the node is not a CSG root node or has no valid geometry.
+ </description>
+ </method>
<method name="get_collision_layer_value" qualifiers="const">
<return type="bool" />
<param index="0" name="layer_number" type="int" />
diff --git a/modules/csg/editor/csg_gizmos.cpp b/modules/csg/editor/csg_gizmos.cpp
index ea7b6d225e..72676f4a40 100644
--- a/modules/csg/editor/csg_gizmos.cpp
+++ b/modules/csg/editor/csg_gizmos.cpp
@@ -38,6 +38,135 @@
#include "editor/plugins/gizmos/gizmo_3d_helper.h"
#include "editor/plugins/node_3d_editor_plugin.h"
#include "scene/3d/camera_3d.h"
+#include "scene/3d/mesh_instance_3d.h"
+#include "scene/3d/physics/collision_shape_3d.h"
+#include "scene/3d/physics/static_body_3d.h"
+#include "scene/gui/dialogs.h"
+#include "scene/gui/menu_button.h"
+
+void CSGShapeEditor::_node_removed(Node *p_node) {
+ if (p_node == node) {
+ node = nullptr;
+ options->hide();
+ }
+}
+
+void CSGShapeEditor::edit(CSGShape3D *p_csg_shape) {
+ node = p_csg_shape;
+ if (node) {
+ options->show();
+ } else {
+ options->hide();
+ }
+}
+
+void CSGShapeEditor::_notification(int p_what) {
+ switch (p_what) {
+ case NOTIFICATION_THEME_CHANGED: {
+ options->set_icon(get_editor_theme_icon(SNAME("CSGCombiner3D")));
+ } break;
+ }
+}
+
+void CSGShapeEditor::_menu_option(int p_option) {
+ Array meshes = node->get_meshes();
+ if (meshes.is_empty()) {
+ err_dialog->set_text(TTR("CSG operation returned an empty array."));
+ err_dialog->popup_centered();
+ return;
+ }
+
+ switch (p_option) {
+ case MENU_OPTION_BAKE_MESH_INSTANCE: {
+ _create_baked_mesh_instance();
+ } break;
+ case MENU_OPTION_BAKE_COLLISION_SHAPE: {
+ _create_baked_collision_shape();
+ } break;
+ }
+}
+
+void CSGShapeEditor::_create_baked_mesh_instance() {
+ if (node == get_tree()->get_edited_scene_root()) {
+ err_dialog->set_text(TTR("Can not add a baked mesh as sibling for the scene root.\nMove the CSG root node below a parent node."));
+ err_dialog->popup_centered();
+ return;
+ }
+
+ Ref<ArrayMesh> mesh = node->bake_static_mesh();
+ if (mesh.is_null()) {
+ err_dialog->set_text(TTR("CSG operation returned an empty mesh."));
+ err_dialog->popup_centered();
+ return;
+ }
+
+ EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
+ ur->create_action(TTR("Create baked CSGShape3D Mesh Instance"));
+
+ Node *owner = get_tree()->get_edited_scene_root();
+
+ MeshInstance3D *mi = memnew(MeshInstance3D);
+ mi->set_mesh(mesh);
+ mi->set_name("CSGBakedMeshInstance3D");
+ mi->set_transform(node->get_transform());
+ ur->add_do_method(node, "add_sibling", mi, true);
+ ur->add_do_method(mi, "set_owner", owner);
+ ur->add_do_method(Node3DEditor::get_singleton(), SceneStringName(_request_gizmo), mi);
+
+ ur->add_do_reference(mi);
+ ur->add_undo_method(node->get_parent(), "remove_child", mi);
+
+ ur->commit_action();
+}
+
+void CSGShapeEditor::_create_baked_collision_shape() {
+ if (node == get_tree()->get_edited_scene_root()) {
+ err_dialog->set_text(TTR("Can not add a baked collision shape as sibling for the scene root.\nMove the CSG root node below a parent node."));
+ err_dialog->popup_centered();
+ return;
+ }
+
+ Ref<Shape3D> shape = node->bake_collision_shape();
+ if (shape.is_null()) {
+ err_dialog->set_text(TTR("CSG operation returned an empty shape."));
+ err_dialog->popup_centered();
+ return;
+ }
+
+ EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
+ ur->create_action(TTR("Create baked CSGShape3D Collision Shape"));
+
+ Node *owner = get_tree()->get_edited_scene_root();
+
+ CollisionShape3D *cshape = memnew(CollisionShape3D);
+ cshape->set_shape(shape);
+ cshape->set_name("CSGBakedCollisionShape3D");
+ cshape->set_transform(node->get_transform());
+ ur->add_do_method(node, "add_sibling", cshape, true);
+ ur->add_do_method(cshape, "set_owner", owner);
+ ur->add_do_method(Node3DEditor::get_singleton(), SceneStringName(_request_gizmo), cshape);
+
+ ur->add_do_reference(cshape);
+ ur->add_undo_method(node->get_parent(), "remove_child", cshape);
+
+ ur->commit_action();
+}
+
+CSGShapeEditor::CSGShapeEditor() {
+ options = memnew(MenuButton);
+ options->hide();
+ options->set_text(TTR("CSG"));
+ options->set_switch_on_hover(true);
+ Node3DEditor::get_singleton()->add_control_to_menu_panel(options);
+
+ options->get_popup()->add_item(TTR("Bake Mesh Instance"), MENU_OPTION_BAKE_MESH_INSTANCE);
+ options->get_popup()->add_item(TTR("Bake Collision Shape"), MENU_OPTION_BAKE_COLLISION_SHAPE);
+
+ options->get_popup()->connect(SceneStringName(id_pressed), callable_mp(this, &CSGShapeEditor::_menu_option));
+
+ err_dialog = memnew(AcceptDialog);
+ add_child(err_dialog);
+}
///////////
@@ -393,9 +522,26 @@ void CSGShape3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
}
}
+void EditorPluginCSG::edit(Object *p_object) {
+ CSGShape3D *csg_shape = Object::cast_to<CSGShape3D>(p_object);
+ if (csg_shape && csg_shape->is_root_shape()) {
+ csg_shape_editor->edit(csg_shape);
+ } else {
+ csg_shape_editor->edit(nullptr);
+ }
+}
+
+bool EditorPluginCSG::handles(Object *p_object) const {
+ CSGShape3D *csg_shape = Object::cast_to<CSGShape3D>(p_object);
+ return csg_shape && csg_shape->is_root_shape();
+}
+
EditorPluginCSG::EditorPluginCSG() {
Ref<CSGShape3DGizmoPlugin> gizmo_plugin = Ref<CSGShape3DGizmoPlugin>(memnew(CSGShape3DGizmoPlugin));
Node3DEditor::get_singleton()->add_gizmo_plugin(gizmo_plugin);
+
+ csg_shape_editor = memnew(CSGShapeEditor);
+ EditorNode::get_singleton()->get_main_screen_control()->add_child(csg_shape_editor);
}
#endif // TOOLS_ENABLED
diff --git a/modules/csg/editor/csg_gizmos.h b/modules/csg/editor/csg_gizmos.h
index de19b33e7d..c562fe9fe7 100644
--- a/modules/csg/editor/csg_gizmos.h
+++ b/modules/csg/editor/csg_gizmos.h
@@ -37,8 +37,11 @@
#include "editor/plugins/editor_plugin.h"
#include "editor/plugins/node_3d_editor_gizmos.h"
+#include "scene/gui/control.h"
+class AcceptDialog;
class Gizmo3DHelper;
+class MenuButton;
class CSGShape3DGizmoPlugin : public EditorNode3DGizmoPlugin {
GDCLASS(CSGShape3DGizmoPlugin, EditorNode3DGizmoPlugin);
@@ -62,10 +65,43 @@ public:
~CSGShape3DGizmoPlugin();
};
+class CSGShapeEditor : public Control {
+ GDCLASS(CSGShapeEditor, Control);
+
+ enum Menu {
+ MENU_OPTION_BAKE_MESH_INSTANCE,
+ MENU_OPTION_BAKE_COLLISION_SHAPE,
+ };
+
+ CSGShape3D *node = nullptr;
+ MenuButton *options = nullptr;
+ AcceptDialog *err_dialog = nullptr;
+
+ void _menu_option(int p_option);
+
+ void _create_baked_mesh_instance();
+ void _create_baked_collision_shape();
+
+protected:
+ void _node_removed(Node *p_node);
+
+ void _notification(int p_what);
+
+public:
+ void edit(CSGShape3D *p_csg_shape);
+ CSGShapeEditor();
+};
+
class EditorPluginCSG : public EditorPlugin {
GDCLASS(EditorPluginCSG, EditorPlugin);
+ CSGShapeEditor *csg_shape_editor = nullptr;
+
public:
+ virtual String get_name() const override { return "CSGShape3D"; }
+ virtual void edit(Object *p_object) override;
+ virtual bool handles(Object *p_object) const override;
+
EditorPluginCSG();
};
diff --git a/modules/dds/texture_loader_dds.cpp b/modules/dds/texture_loader_dds.cpp
index b2de6b656e..6ea44c5fc3 100644
--- a/modules/dds/texture_loader_dds.cpp
+++ b/modules/dds/texture_loader_dds.cpp
@@ -42,14 +42,18 @@ enum {
DDSD_PITCH = 0x00000008,
DDSD_LINEARSIZE = 0x00080000,
DDSD_MIPMAPCOUNT = 0x00020000,
- DDPF_FOURCC = 0x00000004,
DDPF_ALPHAPIXELS = 0x00000001,
- DDPF_RGB = 0x00000040
+ DDPF_ALPHAONLY = 0x00000002,
+ DDPF_FOURCC = 0x00000004,
+ DDPF_RGB = 0x00000040,
+ DDPF_RG_SNORM = 0x00080000
};
enum DDSFourCC {
DDFCC_DXT1 = PF_FOURCC("DXT1"),
+ DDFCC_DXT2 = PF_FOURCC("DXT2"),
DDFCC_DXT3 = PF_FOURCC("DXT3"),
+ DDFCC_DXT4 = PF_FOURCC("DXT4"),
DDFCC_DXT5 = PF_FOURCC("DXT5"),
DDFCC_ATI1 = PF_FOURCC("ATI1"),
DDFCC_BC4U = PF_FOURCC("BC4U"),
@@ -68,17 +72,25 @@ enum DDSFourCC {
// Reference: https://learn.microsoft.com/en-us/windows/win32/api/dxgiformat/ne-dxgiformat-dxgi_format
enum DXGIFormat {
DXGI_R32G32B32A32_FLOAT = 2,
+ DXGI_R32G32B32_FLOAT = 6,
DXGI_R16G16B16A16_FLOAT = 10,
DXGI_R32G32_FLOAT = 16,
DXGI_R10G10B10A2_UNORM = 24,
DXGI_R8G8B8A8_UNORM = 28,
+ DXGI_R8G8B8A8_UNORM_SRGB = 29,
DXGI_R16G16_FLOAT = 34,
DXGI_R32_FLOAT = 41,
+ DXGI_R8G8_UNORM = 49,
DXGI_R16_FLOAT = 54,
+ DXGI_R8_UNORM = 61,
+ DXGI_A8_UNORM = 65,
DXGI_R9G9B9E5 = 67,
DXGI_BC1_UNORM = 71,
+ DXGI_BC1_UNORM_SRGB = 72,
DXGI_BC2_UNORM = 74,
+ DXGI_BC2_UNORM_SRGB = 75,
DXGI_BC3_UNORM = 77,
+ DXGI_BC3_UNORM_SRGB = 78,
DXGI_BC4_UNORM = 80,
DXGI_BC5_UNORM = 83,
DXGI_B5G6R5_UNORM = 85,
@@ -87,6 +99,7 @@ enum DXGIFormat {
DXGI_BC6H_UF16 = 95,
DXGI_BC6H_SF16 = 96,
DXGI_BC7_UNORM = 98,
+ DXGI_BC7_UNORM_SRGB = 99,
DXGI_B4G4R4A4_UNORM = 115
};
@@ -100,25 +113,29 @@ enum DDSFormat {
DDS_ATI2,
DDS_BC6U,
DDS_BC6S,
- DDS_BC7U,
+ DDS_BC7,
DDS_R16F,
DDS_RG16F,
DDS_RGBA16F,
DDS_R32F,
DDS_RG32F,
+ DDS_RGB32F,
DDS_RGBA32F,
DDS_RGB9E5,
- DDS_BGRA8,
- DDS_BGR8,
- DDS_RGBA8,
DDS_RGB8,
+ DDS_RGBA8,
+ DDS_BGR8,
+ DDS_BGRA8,
DDS_BGR5A1,
DDS_BGR565,
+ DDS_B2GR3,
+ DDS_B2GR3A8,
DDS_BGR10A2,
DDS_RGB10A2,
DDS_BGRA4,
DDS_LUMINANCE,
DDS_LUMINANCE_ALPHA,
+ DDS_LUMINANCE_ALPHA_4,
DDS_MAX
};
@@ -132,31 +149,35 @@ struct DDSFormatInfo {
static const DDSFormatInfo dds_format_info[DDS_MAX] = {
{ "DXT1/BC1", true, 4, 8, Image::FORMAT_DXT1 },
- { "DXT3/BC2", true, 4, 16, Image::FORMAT_DXT3 },
- { "DXT5/BC3", true, 4, 16, Image::FORMAT_DXT5 },
+ { "DXT2/DXT3/BC2", true, 4, 16, Image::FORMAT_DXT3 },
+ { "DXT4/DXT5/BC3", true, 4, 16, Image::FORMAT_DXT5 },
{ "ATI1/BC4", true, 4, 8, Image::FORMAT_RGTC_R },
{ "ATI2/A2XY/BC5", true, 4, 16, Image::FORMAT_RGTC_RG },
- { "BC6U", true, 4, 16, Image::FORMAT_BPTC_RGBFU },
- { "BC6S", true, 4, 16, Image::FORMAT_BPTC_RGBF },
- { "BC7U", true, 4, 16, Image::FORMAT_BPTC_RGBA },
+ { "BC6UF", true, 4, 16, Image::FORMAT_BPTC_RGBFU },
+ { "BC6SF", true, 4, 16, Image::FORMAT_BPTC_RGBF },
+ { "BC7", true, 4, 16, Image::FORMAT_BPTC_RGBA },
{ "R16F", false, 1, 2, Image::FORMAT_RH },
{ "RG16F", false, 1, 4, Image::FORMAT_RGH },
{ "RGBA16F", false, 1, 8, Image::FORMAT_RGBAH },
{ "R32F", false, 1, 4, Image::FORMAT_RF },
{ "RG32F", false, 1, 8, Image::FORMAT_RGF },
+ { "RGB32F", false, 1, 12, Image::FORMAT_RGBF },
{ "RGBA32F", false, 1, 16, Image::FORMAT_RGBAF },
{ "RGB9E5", false, 1, 4, Image::FORMAT_RGBE9995 },
- { "BGRA8", false, 1, 4, Image::FORMAT_RGBA8 },
- { "BGR8", false, 1, 3, Image::FORMAT_RGB8 },
- { "RGBA8", false, 1, 4, Image::FORMAT_RGBA8 },
{ "RGB8", false, 1, 3, Image::FORMAT_RGB8 },
+ { "RGBA8", false, 1, 4, Image::FORMAT_RGBA8 },
+ { "BGR8", false, 1, 3, Image::FORMAT_RGB8 },
+ { "BGRA8", false, 1, 4, Image::FORMAT_RGBA8 },
{ "BGR5A1", false, 1, 2, Image::FORMAT_RGBA8 },
{ "BGR565", false, 1, 2, Image::FORMAT_RGB8 },
+ { "B2GR3", false, 1, 1, Image::FORMAT_RGB8 },
+ { "B2GR3A8", false, 1, 2, Image::FORMAT_RGBA8 },
{ "BGR10A2", false, 1, 4, Image::FORMAT_RGBA8 },
{ "RGB10A2", false, 1, 4, Image::FORMAT_RGBA8 },
{ "BGRA4", false, 1, 2, Image::FORMAT_RGBA8 },
{ "GRAYSCALE", false, 1, 1, Image::FORMAT_L8 },
- { "GRAYSCALE_ALPHA", false, 1, 2, Image::FORMAT_LA8 }
+ { "GRAYSCALE_ALPHA", false, 1, 2, Image::FORMAT_LA8 },
+ { "GRAYSCALE_ALPHA_4", false, 1, 1, Image::FORMAT_LA8 }
};
static DDSFormat dxgi_to_dds_format(uint32_t p_dxgi_format) {
@@ -164,6 +185,9 @@ static DDSFormat dxgi_to_dds_format(uint32_t p_dxgi_format) {
case DXGI_R32G32B32A32_FLOAT: {
return DDS_RGBA32F;
}
+ case DXGI_R32G32B32_FLOAT: {
+ return DDS_RGB32F;
+ }
case DXGI_R16G16B16A16_FLOAT: {
return DDS_RGBA16F;
}
@@ -173,7 +197,8 @@ static DDSFormat dxgi_to_dds_format(uint32_t p_dxgi_format) {
case DXGI_R10G10B10A2_UNORM: {
return DDS_RGB10A2;
}
- case DXGI_R8G8B8A8_UNORM: {
+ case DXGI_R8G8B8A8_UNORM:
+ case DXGI_R8G8B8A8_UNORM_SRGB: {
return DDS_RGBA8;
}
case DXGI_R16G16_FLOAT: {
@@ -182,19 +207,29 @@ static DDSFormat dxgi_to_dds_format(uint32_t p_dxgi_format) {
case DXGI_R32_FLOAT: {
return DDS_R32F;
}
+ case DXGI_R8_UNORM:
+ case DXGI_A8_UNORM: {
+ return DDS_LUMINANCE;
+ }
case DXGI_R16_FLOAT: {
return DDS_R16F;
}
+ case DXGI_R8G8_UNORM: {
+ return DDS_LUMINANCE_ALPHA;
+ }
case DXGI_R9G9B9E5: {
return DDS_RGB9E5;
}
- case DXGI_BC1_UNORM: {
+ case DXGI_BC1_UNORM:
+ case DXGI_BC1_UNORM_SRGB: {
return DDS_DXT1;
}
- case DXGI_BC2_UNORM: {
+ case DXGI_BC2_UNORM:
+ case DXGI_BC2_UNORM_SRGB: {
return DDS_DXT3;
}
- case DXGI_BC3_UNORM: {
+ case DXGI_BC3_UNORM:
+ case DXGI_BC3_UNORM_SRGB: {
return DDS_DXT5;
}
case DXGI_BC4_UNORM: {
@@ -218,8 +253,9 @@ static DDSFormat dxgi_to_dds_format(uint32_t p_dxgi_format) {
case DXGI_BC6H_SF16: {
return DDS_BC6S;
}
- case DXGI_BC7_UNORM: {
- return DDS_BC7U;
+ case DXGI_BC7_UNORM:
+ case DXGI_BC7_UNORM_SRGB: {
+ return DDS_BC7;
}
case DXGI_B4G4R4A4_UNORM: {
return DDS_BGRA4;
@@ -299,9 +335,11 @@ Ref<Resource> ResourceFormatDDS::load(const String &p_path, const String &p_orig
case DDFCC_DXT1: {
dds_format = DDS_DXT1;
} break;
+ case DDFCC_DXT2:
case DDFCC_DXT3: {
dds_format = DDS_DXT3;
} break;
+ case DDFCC_DXT4:
case DDFCC_DXT5: {
dds_format = DDS_DXT5;
} break;
@@ -363,6 +401,8 @@ Ref<Resource> ResourceFormatDDS::load(const String &p_path, const String &p_orig
dds_format = DDS_RGB10A2;
} else if (format_rgb_bits == 16 && format_red_mask == 0xf00 && format_green_mask == 0xf0 && format_blue_mask == 0xf && format_alpha_mask == 0xf000) {
dds_format = DDS_BGRA4;
+ } else if (format_rgb_bits == 16 && format_red_mask == 0xe0 && format_green_mask == 0x1c && format_blue_mask == 0x3 && format_alpha_mask == 0xff00) {
+ dds_format = DDS_B2GR3A8;
}
} else {
@@ -373,18 +413,38 @@ Ref<Resource> ResourceFormatDDS::load(const String &p_path, const String &p_orig
dds_format = DDS_RGB8;
} else if (format_rgb_bits == 16 && format_red_mask == 0x0000f800 && format_green_mask == 0x000007e0 && format_blue_mask == 0x0000001f) {
dds_format = DDS_BGR565;
+ } else if (format_rgb_bits == 8 && format_red_mask == 0xe0 && format_green_mask == 0x1c && format_blue_mask == 0x3) {
+ dds_format = DDS_B2GR3;
}
}
} else {
// Other formats.
- if (format_flags & DDPF_ALPHAPIXELS && format_rgb_bits == 16 && format_red_mask == 0xff && format_alpha_mask == 0xff00) {
- dds_format = DDS_LUMINANCE_ALPHA;
- } else if (!(format_flags & DDPF_ALPHAPIXELS) && format_rgb_bits == 8 && format_red_mask == 0xff) {
+ if (format_flags & DDPF_ALPHAONLY && format_rgb_bits == 8 && format_alpha_mask == 0xff) {
+ // Alpha only.
dds_format = DDS_LUMINANCE;
}
}
+ // Depending on the writer, luminance formats may or may not have the DDPF_RGB or DDPF_LUMINANCE flags defined,
+ // so we check for these formats after everything else failed.
+ if (dds_format == DDS_MAX) {
+ if (format_flags & DDPF_ALPHAPIXELS) {
+ // With alpha.
+ if (format_rgb_bits == 16 && format_red_mask == 0xff && format_alpha_mask == 0xff00) {
+ dds_format = DDS_LUMINANCE_ALPHA;
+ } else if (format_rgb_bits == 8 && format_red_mask == 0xf && format_alpha_mask == 0xf0) {
+ dds_format = DDS_LUMINANCE_ALPHA_4;
+ }
+
+ } else {
+ // Without alpha.
+ if (format_rgb_bits == 8 && format_red_mask == 0xff) {
+ dds_format = DDS_LUMINANCE;
+ }
+ }
+ }
+
// No format detected, error.
if (dds_format == DDS_MAX) {
ERR_FAIL_V_MSG(Ref<Resource>(), "Unrecognized or unsupported color layout in DDS '" + p_path + "'.");
@@ -433,10 +493,24 @@ Ref<Resource> ResourceFormatDDS::load(const String &p_path, const String &p_orig
}
// Calculate the space these formats will take up after decoding.
- if (dds_format == DDS_BGR565) {
- size = size * 3 / 2;
- } else if (dds_format == DDS_BGR5A1 || dds_format == DDS_BGRA4) {
- size = size * 2;
+ switch (dds_format) {
+ case DDS_BGR565:
+ size = size * 3 / 2;
+ break;
+
+ case DDS_BGR5A1:
+ case DDS_BGRA4:
+ case DDS_B2GR3A8:
+ case DDS_LUMINANCE_ALPHA_4:
+ size = size * 2;
+ break;
+
+ case DDS_B2GR3:
+ size = size * 3;
+ break;
+
+ default:
+ break;
}
src_data.resize(size);
@@ -503,6 +577,44 @@ Ref<Resource> ResourceFormatDDS::load(const String &p_path, const String &p_orig
}
} break;
+ case DDS_B2GR3: {
+ // To RGB8.
+ int colcount = size / 3;
+
+ for (int i = colcount - 1; i >= 0; i--) {
+ int src_ofs = i;
+ int dst_ofs = i * 3;
+
+ uint8_t b = (wb[src_ofs] & 0x3) << 6;
+ uint8_t g = (wb[src_ofs] & 0x1C) << 3;
+ uint8_t r = (wb[src_ofs] & 0xE0);
+
+ wb[dst_ofs] = r;
+ wb[dst_ofs + 1] = g;
+ wb[dst_ofs + 2] = b;
+ }
+
+ } break;
+ case DDS_B2GR3A8: {
+ // To RGBA8.
+ int colcount = size / 4;
+
+ for (int i = colcount - 1; i >= 0; i--) {
+ int src_ofs = i * 2;
+ int dst_ofs = i * 4;
+
+ uint8_t b = (wb[src_ofs] & 0x3) << 6;
+ uint8_t g = (wb[src_ofs] & 0x1C) << 3;
+ uint8_t r = (wb[src_ofs] & 0xE0);
+ uint8_t a = wb[src_ofs + 1];
+
+ wb[dst_ofs] = r;
+ wb[dst_ofs + 1] = g;
+ wb[dst_ofs + 2] = b;
+ wb[dst_ofs + 3] = a;
+ }
+
+ } break;
case DDS_RGB10A2: {
// To RGBA8.
int colcount = size / 4;
@@ -549,6 +661,8 @@ Ref<Resource> ResourceFormatDDS::load(const String &p_path, const String &p_orig
}
} break;
+
+ // Channel-swapped.
case DDS_BGRA8: {
// To RGBA8.
int colcount = size / 4;
@@ -568,6 +682,24 @@ Ref<Resource> ResourceFormatDDS::load(const String &p_path, const String &p_orig
} break;
+ // Grayscale.
+ case DDS_LUMINANCE_ALPHA_4: {
+ // To LA8.
+ int colcount = size / 2;
+
+ for (int i = colcount - 1; i >= 0; i--) {
+ int src_ofs = i;
+ int dst_ofs = i * 2;
+
+ uint8_t l = wb[src_ofs] & 0x0F;
+ uint8_t a = wb[src_ofs] & 0xF0;
+
+ wb[dst_ofs] = (l << 4) | l;
+ wb[dst_ofs + 1] = a | (a >> 4);
+ }
+
+ } break;
+
default: {
}
}
diff --git a/modules/etcpak/image_compress_etcpak.cpp b/modules/etcpak/image_compress_etcpak.cpp
index 95ed462070..14887ce469 100644
--- a/modules/etcpak/image_compress_etcpak.cpp
+++ b/modules/etcpak/image_compress_etcpak.cpp
@@ -50,6 +50,7 @@ EtcpakType _determine_etc_type(Image::UsedChannels p_channels) {
return EtcpakType::ETCPAK_TYPE_ETC2;
case Image::USED_CHANNELS_RGBA:
return EtcpakType::ETCPAK_TYPE_ETC2_ALPHA;
+
default:
return EtcpakType::ETCPAK_TYPE_ETC2_ALPHA;
}
@@ -69,6 +70,7 @@ EtcpakType _determine_dxt_type(Image::UsedChannels p_channels) {
return EtcpakType::ETCPAK_TYPE_DXT1;
case Image::USED_CHANNELS_RGBA:
return EtcpakType::ETCPAK_TYPE_DXT5;
+
default:
return EtcpakType::ETCPAK_TYPE_DXT5;
}
@@ -79,71 +81,86 @@ void _compress_etc1(Image *r_img) {
}
void _compress_etc2(Image *r_img, Image::UsedChannels p_channels) {
- EtcpakType type = _determine_etc_type(p_channels);
- _compress_etcpak(type, r_img);
+ _compress_etcpak(_determine_etc_type(p_channels), r_img);
}
void _compress_bc(Image *r_img, Image::UsedChannels p_channels) {
- EtcpakType type = _determine_dxt_type(p_channels);
- _compress_etcpak(type, r_img);
+ _compress_etcpak(_determine_dxt_type(p_channels), r_img);
}
-void _compress_etcpak(EtcpakType p_compresstype, Image *r_img) {
+void _compress_etcpak(EtcpakType p_compress_type, Image *r_img) {
uint64_t start_time = OS::get_singleton()->get_ticks_msec();
- Image::Format img_format = r_img->get_format();
- if (Image::is_format_compressed(img_format)) {
- return; // Do not compress, already compressed.
- }
- if (img_format > Image::FORMAT_RGBA8) {
- // TODO: we should be able to handle FORMAT_RGBA4444 and FORMAT_RGBA5551 eventually
+ // The image is already compressed, return.
+ if (r_img->is_compressed()) {
return;
}
- // Use RGBA8 to convert.
- if (img_format != Image::FORMAT_RGBA8) {
- r_img->convert(Image::FORMAT_RGBA8);
- }
+ // Convert to RGBA8 for compression.
+ r_img->convert(Image::FORMAT_RGBA8);
// Determine output format based on Etcpak type.
Image::Format target_format = Image::FORMAT_RGBA8;
- if (p_compresstype == EtcpakType::ETCPAK_TYPE_ETC1) {
- target_format = Image::FORMAT_ETC;
- r_img->convert_rgba8_to_bgra8(); // It's badly documented but ETCPAK seems to be expected BGRA8 for ETC.
- } else if (p_compresstype == EtcpakType::ETCPAK_TYPE_ETC2) {
- target_format = Image::FORMAT_ETC2_RGB8;
- r_img->convert_rgba8_to_bgra8(); // It's badly documented but ETCPAK seems to be expected BGRA8 for ETC.
- } else if (p_compresstype == EtcpakType::ETCPAK_TYPE_ETC2_R) {
- target_format = Image::FORMAT_ETC2_R11;
- r_img->convert_rgba8_to_bgra8(); // It's badly documented but ETCPAK seems to be expected BGRA8 for ETC.
- } else if (p_compresstype == EtcpakType::ETCPAK_TYPE_ETC2_RG) {
- target_format = Image::FORMAT_ETC2_RG11;
- r_img->convert_rgba8_to_bgra8(); // It's badly documented but ETCPAK seems to be expected BGRA8 for ETC.
- } else if (p_compresstype == EtcpakType::ETCPAK_TYPE_ETC2_RA_AS_RG) {
- target_format = Image::FORMAT_ETC2_RA_AS_RG;
- r_img->convert_rg_to_ra_rgba8();
- r_img->convert_rgba8_to_bgra8(); // It's badly documented but ETCPAK seems to be expected BGRA8 for ETC.
- } else if (p_compresstype == EtcpakType::ETCPAK_TYPE_ETC2_ALPHA) {
- target_format = Image::FORMAT_ETC2_RGBA8;
- r_img->convert_rgba8_to_bgra8(); // It's badly documented but ETCPAK seems to be expected BGRA8 for ETC.
- } else if (p_compresstype == EtcpakType::ETCPAK_TYPE_DXT1) {
- target_format = Image::FORMAT_DXT1;
- } else if (p_compresstype == EtcpakType::ETCPAK_TYPE_DXT5_RA_AS_RG) {
- target_format = Image::FORMAT_DXT5_RA_AS_RG;
- r_img->convert_rg_to_ra_rgba8();
- } else if (p_compresstype == EtcpakType::ETCPAK_TYPE_DXT5) {
- target_format = Image::FORMAT_DXT5;
- } else if (p_compresstype == EtcpakType::ETCPAK_TYPE_RGTC_R) {
- target_format = Image::FORMAT_RGTC_R;
- } else if (p_compresstype == EtcpakType::ETCPAK_TYPE_RGTC_RG) {
- target_format = Image::FORMAT_RGTC_RG;
- } else {
- ERR_FAIL_MSG("Invalid or unsupported etcpak compression format, not ETC or DXT.");
+
+ switch (p_compress_type) {
+ case EtcpakType::ETCPAK_TYPE_ETC1:
+ target_format = Image::FORMAT_ETC;
+ break;
+
+ case EtcpakType::ETCPAK_TYPE_ETC2:
+ target_format = Image::FORMAT_ETC2_RGB8;
+ break;
+
+ case EtcpakType::ETCPAK_TYPE_ETC2_ALPHA:
+ target_format = Image::FORMAT_ETC2_RGBA8;
+ break;
+
+ case EtcpakType::ETCPAK_TYPE_ETC2_R:
+ target_format = Image::FORMAT_ETC2_R11;
+ break;
+
+ case EtcpakType::ETCPAK_TYPE_ETC2_RG:
+ target_format = Image::FORMAT_ETC2_RG11;
+ break;
+
+ case EtcpakType::ETCPAK_TYPE_ETC2_RA_AS_RG:
+ target_format = Image::FORMAT_ETC2_RA_AS_RG;
+ r_img->convert_rg_to_ra_rgba8();
+ break;
+
+ case EtcpakType::ETCPAK_TYPE_DXT1:
+ target_format = Image::FORMAT_DXT1;
+ break;
+
+ case EtcpakType::ETCPAK_TYPE_DXT5:
+ target_format = Image::FORMAT_DXT5;
+ break;
+
+ case EtcpakType::ETCPAK_TYPE_RGTC_R:
+ target_format = Image::FORMAT_RGTC_R;
+ break;
+
+ case EtcpakType::ETCPAK_TYPE_RGTC_RG:
+ target_format = Image::FORMAT_RGTC_RG;
+ break;
+
+ case EtcpakType::ETCPAK_TYPE_DXT5_RA_AS_RG:
+ target_format = Image::FORMAT_DXT5_RA_AS_RG;
+ r_img->convert_rg_to_ra_rgba8();
+ break;
+
+ default:
+ ERR_FAIL_MSG("Invalid or unsupported etcpak compression format, not ETC or DXT.");
+ break;
}
- // Compress image data and (if required) mipmaps.
+ // It's badly documented but ETCPAK seems to expect BGRA8 for ETC formats.
+ if (p_compress_type < EtcpakType::ETCPAK_TYPE_DXT1) {
+ r_img->convert_rgba8_to_bgra8();
+ }
- const bool mipmaps = r_img->has_mipmaps();
+ // Compress image data and (if required) mipmaps.
+ const bool has_mipmaps = r_img->has_mipmaps();
int width = r_img->get_width();
int height = r_img->get_height();
@@ -164,109 +181,115 @@ void _compress_etcpak(EtcpakType p_compresstype, Image *r_img) {
are used for a 2x2 map, and texel 'a' is used for 1x1. Note that this is similar to, but distinct from,
the surface pitch, which can encompass additional padding beyond the physical surface size.
*/
- int next_width = width <= 2 ? width : (width + 3) & ~3;
- int next_height = height <= 2 ? height : (height + 3) & ~3;
- if (next_width != width || next_height != height) {
- r_img->resize(next_width, next_height, Image::INTERPOLATE_LANCZOS);
- width = r_img->get_width();
- height = r_img->get_height();
+
+ if (width % 4 != 0 || height % 4 != 0) {
+ width = width <= 2 ? width : (width + 3) & ~3;
+ height = height <= 2 ? height : (height + 3) & ~3;
}
- // ERR_FAIL_COND(width % 4 != 0 || height % 4 != 0); // FIXME: No longer guaranteed.
+
// Multiple-of-4 should be guaranteed by above.
// However, power-of-two 3d textures will create Nx2 and Nx1 mipmap levels,
// which are individually compressed Image objects that violate the above rule.
// Hence, we allow Nx1 and Nx2 images through without forcing to multiple-of-4.
- const uint8_t *src_read = r_img->get_data().ptr();
-
- print_verbose(vformat("etcpak: Encoding image size %dx%d to format %s%s.", width, height, Image::get_format_name(target_format), mipmaps ? ", with mipmaps" : ""));
-
- int64_t dest_size = Image::get_image_data_size(width, height, target_format, mipmaps);
+ // Create the buffer for compressed image data.
Vector<uint8_t> dest_data;
- dest_data.resize(dest_size);
+ dest_data.resize(Image::get_image_data_size(width, height, target_format, has_mipmaps));
uint8_t *dest_write = dest_data.ptrw();
- int mip_count = mipmaps ? Image::get_image_required_mipmaps(width, height, target_format) : 0;
+ const uint8_t *src_read = r_img->get_data().ptr();
+
+ const int mip_count = has_mipmaps ? Image::get_image_required_mipmaps(width, height, target_format) : 0;
Vector<uint32_t> padded_src;
for (int i = 0; i < mip_count + 1; i++) {
// Get write mip metrics for target image.
- int orig_mip_w, orig_mip_h;
- int64_t mip_ofs = Image::get_image_mipmap_offset_and_dimensions(width, height, target_format, i, orig_mip_w, orig_mip_h);
+ int dest_mip_w, dest_mip_h;
+ int64_t dest_mip_ofs = Image::get_image_mipmap_offset_and_dimensions(width, height, target_format, i, dest_mip_w, dest_mip_h);
+
// Ensure that mip offset is a multiple of 8 (etcpak expects uint64_t pointer).
- ERR_FAIL_COND(mip_ofs % 8 != 0);
- uint64_t *dest_mip_write = (uint64_t *)&dest_write[mip_ofs];
+ ERR_FAIL_COND(dest_mip_ofs % 8 != 0);
+ uint64_t *dest_mip_write = reinterpret_cast<uint64_t *>(dest_write + dest_mip_ofs);
- // Block size. Align stride to multiple of 4 (RGBA8).
- int mip_w = (orig_mip_w + 3) & ~3;
- int mip_h = (orig_mip_h + 3) & ~3;
- const uint32_t blocks = mip_w * mip_h / 16;
+ // Block size.
+ dest_mip_w = (dest_mip_w + 3) & ~3;
+ dest_mip_h = (dest_mip_h + 3) & ~3;
+ const uint32_t blocks = dest_mip_w * dest_mip_h / 16;
// Get mip data from source image for reading.
- int64_t src_mip_ofs = r_img->get_mipmap_offset(i);
- const uint32_t *src_mip_read = (const uint32_t *)&src_read[src_mip_ofs];
+ int64_t src_mip_ofs, src_mip_size;
+ int src_mip_w, src_mip_h;
+
+ r_img->get_mipmap_offset_size_and_dimensions(i, src_mip_ofs, src_mip_size, src_mip_w, src_mip_h);
+
+ const uint32_t *src_mip_read = reinterpret_cast<const uint32_t *>(src_read + src_mip_ofs);
// Pad textures to nearest block by smearing.
- if (mip_w != orig_mip_w || mip_h != orig_mip_h) {
- padded_src.resize(mip_w * mip_h);
+ if (dest_mip_w != src_mip_w || dest_mip_h != src_mip_h) {
+ // Reserve the buffer for padded image data.
+ padded_src.resize(dest_mip_w * dest_mip_h);
uint32_t *ptrw = padded_src.ptrw();
+
int x = 0, y = 0;
- for (y = 0; y < orig_mip_h; y++) {
- for (x = 0; x < orig_mip_w; x++) {
- ptrw[mip_w * y + x] = src_mip_read[orig_mip_w * y + x];
+ for (y = 0; y < src_mip_h; y++) {
+ for (x = 0; x < src_mip_w; x++) {
+ ptrw[dest_mip_w * y + x] = src_mip_read[src_mip_w * y + x];
}
+
// First, smear in x.
- for (; x < mip_w; x++) {
- ptrw[mip_w * y + x] = ptrw[mip_w * y + x - 1];
+ for (; x < dest_mip_w; x++) {
+ ptrw[dest_mip_w * y + x] = ptrw[dest_mip_w * y + x - 1];
}
}
+
// Then, smear in y.
- for (; y < mip_h; y++) {
- for (x = 0; x < mip_w; x++) {
- ptrw[mip_w * y + x] = ptrw[mip_w * y + x - mip_w];
+ for (; y < dest_mip_h; y++) {
+ for (x = 0; x < dest_mip_w; x++) {
+ ptrw[dest_mip_w * y + x] = ptrw[dest_mip_w * y + x - dest_mip_w];
}
}
+
// Override the src_mip_read pointer to our temporary Vector.
src_mip_read = padded_src.ptr();
}
- switch (p_compresstype) {
+ switch (p_compress_type) {
case EtcpakType::ETCPAK_TYPE_ETC1:
- CompressEtc1RgbDither(src_mip_read, dest_mip_write, blocks, mip_w);
+ CompressEtc1RgbDither(src_mip_read, dest_mip_write, blocks, dest_mip_w);
break;
case EtcpakType::ETCPAK_TYPE_ETC2:
- CompressEtc2Rgb(src_mip_read, dest_mip_write, blocks, mip_w, true);
+ CompressEtc2Rgb(src_mip_read, dest_mip_write, blocks, dest_mip_w, true);
break;
case EtcpakType::ETCPAK_TYPE_ETC2_ALPHA:
case EtcpakType::ETCPAK_TYPE_ETC2_RA_AS_RG:
- CompressEtc2Rgba(src_mip_read, dest_mip_write, blocks, mip_w, true);
+ CompressEtc2Rgba(src_mip_read, dest_mip_write, blocks, dest_mip_w, true);
break;
case EtcpakType::ETCPAK_TYPE_ETC2_R:
- CompressEacR(src_mip_read, dest_mip_write, blocks, mip_w);
+ CompressEacR(src_mip_read, dest_mip_write, blocks, dest_mip_w);
break;
case EtcpakType::ETCPAK_TYPE_ETC2_RG:
- CompressEacRg(src_mip_read, dest_mip_write, blocks, mip_w);
+ CompressEacRg(src_mip_read, dest_mip_write, blocks, dest_mip_w);
break;
case EtcpakType::ETCPAK_TYPE_DXT1:
- CompressDxt1Dither(src_mip_read, dest_mip_write, blocks, mip_w);
+ CompressDxt1Dither(src_mip_read, dest_mip_write, blocks, dest_mip_w);
break;
case EtcpakType::ETCPAK_TYPE_DXT5:
case EtcpakType::ETCPAK_TYPE_DXT5_RA_AS_RG:
- CompressDxt5(src_mip_read, dest_mip_write, blocks, mip_w);
+ CompressDxt5(src_mip_read, dest_mip_write, blocks, dest_mip_w);
break;
case EtcpakType::ETCPAK_TYPE_RGTC_R:
- CompressBc4(src_mip_read, dest_mip_write, blocks, mip_w);
+ CompressBc4(src_mip_read, dest_mip_write, blocks, dest_mip_w);
break;
case EtcpakType::ETCPAK_TYPE_RGTC_RG:
- CompressBc5(src_mip_read, dest_mip_write, blocks, mip_w);
+ CompressBc5(src_mip_read, dest_mip_write, blocks, dest_mip_w);
break;
default:
@@ -276,7 +299,7 @@ void _compress_etcpak(EtcpakType p_compresstype, Image *r_img) {
}
// Replace original image with compressed one.
- r_img->set_data(width, height, mipmaps, target_format, dest_data);
+ r_img->set_data(width, height, has_mipmaps, target_format, dest_data);
print_verbose(vformat("etcpak: Encoding took %d ms.", OS::get_singleton()->get_ticks_msec() - start_time));
}
diff --git a/modules/etcpak/image_compress_etcpak.h b/modules/etcpak/image_compress_etcpak.h
index 9d5343740b..d50b322fe4 100644
--- a/modules/etcpak/image_compress_etcpak.h
+++ b/modules/etcpak/image_compress_etcpak.h
@@ -51,6 +51,6 @@ void _compress_etc1(Image *r_img);
void _compress_etc2(Image *r_img, Image::UsedChannels p_channels);
void _compress_bc(Image *r_img, Image::UsedChannels p_channels);
-void _compress_etcpak(EtcpakType p_compresstype, Image *r_img);
+void _compress_etcpak(EtcpakType p_compress_type, Image *r_img);
#endif // IMAGE_COMPRESS_ETCPAK_H
diff --git a/modules/fbx/fbx_document.cpp b/modules/fbx/fbx_document.cpp
index b9d9ec7b6c..4d3f7554c0 100644
--- a/modules/fbx/fbx_document.cpp
+++ b/modules/fbx/fbx_document.cpp
@@ -2114,9 +2114,6 @@ Error FBXDocument::_parse(Ref<FBXState> p_state, String p_path, Ref<FileAccess>
return OK;
}
-void FBXDocument::_bind_methods() {
-}
-
Node *FBXDocument::generate_scene(Ref<GLTFState> p_state, float p_bake_fps, bool p_trimming, bool p_remove_immutable_tracks) {
Ref<FBXState> state = p_state;
ERR_FAIL_COND_V(state.is_null(), nullptr);
diff --git a/modules/fbx/fbx_document.h b/modules/fbx/fbx_document.h
index 4a3bb176c2..96f1905881 100644
--- a/modules/fbx/fbx_document.h
+++ b/modules/fbx/fbx_document.h
@@ -61,9 +61,6 @@ public:
PackedByteArray generate_buffer(Ref<GLTFState> p_state) override;
Error write_to_filesystem(Ref<GLTFState> p_state, const String &p_path) override;
-protected:
- static void _bind_methods();
-
private:
String _get_texture_path(const String &p_base_directory, const String &p_source_file_path) const;
void _process_uv_set(PackedVector2Array &uv_array);
diff --git a/modules/gdscript/doc_classes/@GDScript.xml b/modules/gdscript/doc_classes/@GDScript.xml
index 6e7ac0dec9..104fc15a8c 100644
--- a/modules/gdscript/doc_classes/@GDScript.xml
+++ b/modules/gdscript/doc_classes/@GDScript.xml
@@ -52,9 +52,9 @@
<description>
Returns a single character (as a [String]) of the given Unicode code point (which is compatible with ASCII code).
[codeblock]
- a = char(65) # a is "A"
- a = char(65 + 32) # a is "a"
- a = char(8364) # a is "€"
+ var upper = char(65) # upper is "A"
+ var lower = char(65 + 32) # lower is "a"
+ var euro = char(8364) # euro is "€"
[/codeblock]
</description>
</method>
@@ -150,10 +150,10 @@
<description>
Returns the length of the given Variant [param var]. The length can be the character count of a [String] or [StringName], the element count of any array type, or the size of a [Dictionary]. For every other Variant type, a run-time error is generated and execution is stopped.
[codeblock]
- a = [1, 2, 3, 4]
+ var a = [1, 2, 3, 4]
len(a) # Returns 4
- b = "Hello!"
+ var b = "Hello!"
len(b) # Returns 6
[/codeblock]
</description>
diff --git a/modules/gdscript/gdscript.cpp b/modules/gdscript/gdscript.cpp
index eaf2565e69..7bf5e946fb 100644
--- a/modules/gdscript/gdscript.cpp
+++ b/modules/gdscript/gdscript.cpp
@@ -2857,8 +2857,11 @@ GDScriptLanguage::GDScriptLanguage() {
_debug_parse_err_line = -1;
_debug_parse_err_file = "";
+#ifdef DEBUG_ENABLED
profiling = false;
+ profile_native_calls = false;
script_frame_time = 0;
+#endif
int dmcs = GLOBAL_DEF(PropertyInfo(Variant::INT, "debug/settings/gdscript/max_call_stack", PROPERTY_HINT_RANGE, "512," + itos(GDScriptFunction::MAX_CALL_DEPTH - 1) + ",1"), 1024);
diff --git a/modules/gdscript/gdscript.h b/modules/gdscript/gdscript.h
index 4e78fbe302..6527a0ea4d 100644
--- a/modules/gdscript/gdscript.h
+++ b/modules/gdscript/gdscript.h
@@ -459,9 +459,11 @@ class GDScriptLanguage : public ScriptLanguage {
friend class GDScriptFunction;
SelfList<GDScriptFunction>::List function_list;
+#ifdef DEBUG_ENABLED
bool profiling;
bool profile_native_calls;
uint64_t script_frame_time;
+#endif
HashMap<String, ObjectID> orphan_subclasses;
diff --git a/modules/gltf/README.md b/modules/gltf/README.md
index 5d8966b201..2d1e92e070 100644
--- a/modules/gltf/README.md
+++ b/modules/gltf/README.md
@@ -1,11 +1,11 @@
-# Godot GLTF import and export module
+# Godot glTF import and export module
-In a nutshell, the GLTF module works like this:
+In a nutshell, the glTF module works like this:
-* The [`structures/`](structures/) folder contains GLTF structures, the
- small pieces that make up a GLTF file, represented as C++ classes.
-* The [`extensions/`](extensions/) folder contains GLTF extensions, which
- are optional features that build on top of the base GLTF spec.
+* The [`structures/`](structures/) folder contains glTF structures, the
+ small pieces that make up a glTF file, represented as C++ classes.
+* The [`extensions/`](extensions/) folder contains glTF extensions, which
+ are optional features that build on top of the base glTF spec.
* [`GLTFState`](gltf_state.h) holds collections of structures and extensions.
* [`GLTFDocument`](gltf_document.h) operates on GLTFState and its elements.
* The [`editor/`](editor/) folder uses GLTFDocument to import and export 3D models.
diff --git a/modules/gltf/doc_classes/GLTFAccessor.xml b/modules/gltf/doc_classes/GLTFAccessor.xml
index dd059e6b79..bc142797a3 100644
--- a/modules/gltf/doc_classes/GLTFAccessor.xml
+++ b/modules/gltf/doc_classes/GLTFAccessor.xml
@@ -1,11 +1,11 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="GLTFAccessor" inherits="Resource" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
<brief_description>
- Represents a GLTF accessor.
+ Represents a glTF accessor.
</brief_description>
<description>
- GLTFAccessor is a data structure representing GLTF a [code]accessor[/code] that would be found in the [code]"accessors"[/code] array. A buffer is a blob of binary data. A buffer view is a slice of a buffer. An accessor is a typed interpretation of the data in a buffer view.
- Most custom data stored in GLTF does not need accessors, only buffer views (see [GLTFBufferView]). Accessors are for more advanced use cases such as interleaved mesh data encoded for the GPU.
+ GLTFAccessor is a data structure representing a glTF [code]accessor[/code] that would be found in the [code]"accessors"[/code] array. A buffer is a blob of binary data. A buffer view is a slice of a buffer. An accessor is a typed interpretation of the data in a buffer view.
+ Most custom data stored in glTF does not need accessors, only buffer views (see [GLTFBufferView]). Accessors are for more advanced use cases such as interleaved mesh data encoded for the GPU.
</description>
<tutorials>
<link title="Buffers, BufferViews, and Accessors in Khronos glTF specification">https://github.com/KhronosGroup/glTF-Tutorials/blob/master/gltfTutorial/gltfTutorial_005_BuffersBufferViewsAccessors.md</link>
@@ -13,7 +13,7 @@
</tutorials>
<members>
<member name="accessor_type" type="int" setter="set_accessor_type" getter="get_accessor_type" enum="GLTFAccessor.GLTFAccessorType" default="0">
- The GLTF accessor type as an enum. Possible values are 0 for "SCALAR", 1 for "VEC2", 2 for "VEC3", 3 for "VEC4", 4 for "MAT2", 5 for "MAT3", and 6 for "MAT4".
+ The glTF accessor type as an enum. Possible values are 0 for "SCALAR", 1 for "VEC2", 2 for "VEC3", 3 for "VEC4", 4 for "MAT2", 5 for "MAT3", and 6 for "MAT4".
</member>
<member name="buffer_view" type="int" setter="set_buffer_view" getter="get_buffer_view" default="-1">
The index of the buffer view this accessor is referencing. If [code]-1[/code], this accessor is not referencing any buffer view.
@@ -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. 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.
</member>
<member name="count" type="int" setter="set_count" getter="get_count" default="0">
The number of elements referenced by this accessor.
@@ -55,7 +55,7 @@
The offset relative to the start of the bufferView in bytes.
</member>
<member name="type" type="int" setter="set_type" getter="get_type" deprecated="Use [member accessor_type] instead.">
- The GLTF accessor type as an enum. Use [member accessor_type] instead.
+ The glTF accessor type as an enum. Use [member accessor_type] instead.
</member>
</members>
<constants>
diff --git a/modules/gltf/doc_classes/GLTFAnimation.xml b/modules/gltf/doc_classes/GLTFAnimation.xml
index 2c70f4461f..d269145bbd 100644
--- a/modules/gltf/doc_classes/GLTFAnimation.xml
+++ b/modules/gltf/doc_classes/GLTFAnimation.xml
@@ -13,7 +13,7 @@
<param index="0" name="extension_name" type="StringName" />
<description>
Gets additional arbitrary data in this [GLTFAnimation] instance. This can be used to keep per-node state data in [GLTFDocumentExtension] classes, which is important because they are stateless.
- 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.
+ 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="set_additional_data">
@@ -22,7 +22,7 @@
<param index="1" name="additional_data" type="Variant" />
<description>
Sets additional arbitrary data in this [GLTFAnimation] instance. This can be used to keep per-node state data in [GLTFDocumentExtension] classes, which is important because they are stateless.
- The first argument should be the [GLTFDocumentExtension] name (does not have to match the extension name in the GLTF file), and the second argument can be anything you want.
+ The first argument should be the [GLTFDocumentExtension] name (does not have to match the extension name in the glTF file), and the second argument can be anything you want.
</description>
</method>
</methods>
diff --git a/modules/gltf/doc_classes/GLTFBufferView.xml b/modules/gltf/doc_classes/GLTFBufferView.xml
index e191935fc9..b7f499ad72 100644
--- a/modules/gltf/doc_classes/GLTFBufferView.xml
+++ b/modules/gltf/doc_classes/GLTFBufferView.xml
@@ -1,10 +1,10 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="GLTFBufferView" inherits="Resource" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
<brief_description>
- Represents a GLTF buffer view.
+ Represents a glTF buffer view.
</brief_description>
<description>
- GLTFBufferView is a data structure representing GLTF a [code]bufferView[/code] that would be found in the [code]"bufferViews"[/code] array. A buffer is a blob of binary data. A buffer view is a slice of a buffer that can be used to identify and extract data from the buffer.
+ GLTFBufferView is a data structure representing a glTF [code]bufferView[/code] that would be found in the [code]"bufferViews"[/code] array. A buffer is a blob of binary data. A buffer view is a slice of a buffer that can be used to identify and extract data from the buffer.
Most custom uses of buffers only need to use the [member buffer], [member byte_length], and [member byte_offset]. The [member byte_stride] and [member indices] properties are for more advanced use cases such as interleaved mesh data encoded for the GPU.
</description>
<tutorials>
diff --git a/modules/gltf/doc_classes/GLTFCamera.xml b/modules/gltf/doc_classes/GLTFCamera.xml
index b334bf2867..9fce21659c 100644
--- a/modules/gltf/doc_classes/GLTFCamera.xml
+++ b/modules/gltf/doc_classes/GLTFCamera.xml
@@ -1,15 +1,15 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="GLTFCamera" inherits="Resource" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
<brief_description>
- Represents a GLTF camera.
+ Represents a glTF camera.
</brief_description>
<description>
- Represents a camera as defined by the base GLTF spec.
+ Represents a camera as defined by the base glTF spec.
</description>
<tutorials>
<link title="Runtime file loading and saving">$DOCS_URL/tutorials/io/runtime_file_loading_and_saving.html</link>
- <link title="GLTF camera detailed specification">https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html#reference-camera</link>
- <link title="GLTF camera spec and example file">https://github.com/KhronosGroup/glTF-Tutorials/blob/master/gltfTutorial/gltfTutorial_015_SimpleCameras.md</link>
+ <link title="glTF camera detailed specification">https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html#reference-camera</link>
+ <link title="glTF camera spec and example file">https://github.com/KhronosGroup/glTF-Tutorials/blob/master/gltfTutorial/gltfTutorial_015_SimpleCameras.md</link>
</tutorials>
<methods>
<method name="from_dictionary" qualifiers="static">
@@ -41,19 +41,19 @@
</methods>
<members>
<member name="depth_far" type="float" setter="set_depth_far" getter="get_depth_far" default="4000.0">
- The distance to the far culling boundary for this camera relative to its local Z axis, in meters. This maps to GLTF's [code]zfar[/code] property.
+ The distance to the far culling boundary for this camera relative to its local Z axis, in meters. This maps to glTF's [code]zfar[/code] property.
</member>
<member name="depth_near" type="float" setter="set_depth_near" getter="get_depth_near" default="0.05">
- The distance to the near culling boundary for this camera relative to its local Z axis, in meters. This maps to GLTF's [code]znear[/code] property.
+ The distance to the near culling boundary for this camera relative to its local Z axis, in meters. This maps to glTF's [code]znear[/code] property.
</member>
<member name="fov" type="float" setter="set_fov" getter="get_fov" default="1.309">
- The FOV of the camera. This class and GLTF define the camera FOV in radians, while Godot uses degrees. This maps to GLTF's [code]yfov[/code] property. This value is only used for perspective cameras, when [member perspective] is true.
+ The FOV of the camera. This class and glTF define the camera FOV in radians, while Godot uses degrees. This maps to glTF's [code]yfov[/code] property. This value is only used for perspective cameras, when [member perspective] is true.
</member>
<member name="perspective" type="bool" setter="set_perspective" getter="get_perspective" default="true">
- Whether or not the camera is in perspective mode. If false, the camera is in orthographic/orthogonal mode. This maps to GLTF's camera [code]type[/code] property. See [member Camera3D.projection] and the GLTF spec for more information.
+ Whether or not the camera is in perspective mode. If false, the camera is in orthographic/orthogonal mode. This maps to glTF's camera [code]type[/code] property. See [member Camera3D.projection] and the glTF spec for more information.
</member>
<member name="size_mag" type="float" setter="set_size_mag" getter="get_size_mag" default="0.5">
- The size of the camera. This class and GLTF define the camera size magnitude as a radius in meters, while Godot defines it as a diameter in meters. This maps to GLTF's [code]ymag[/code] property. This value is only used for orthographic/orthogonal cameras, when [member perspective] is false.
+ The size of the camera. This class and glTF define the camera size magnitude as a radius in meters, while Godot defines it as a diameter in meters. This maps to glTF's [code]ymag[/code] property. This value is only used for orthographic/orthogonal cameras, when [member perspective] is false.
</member>
</members>
</class>
diff --git a/modules/gltf/doc_classes/GLTFDocument.xml b/modules/gltf/doc_classes/GLTFDocument.xml
index 1b51def28e..ebeed015e9 100644
--- a/modules/gltf/doc_classes/GLTFDocument.xml
+++ b/modules/gltf/doc_classes/GLTFDocument.xml
@@ -5,7 +5,7 @@
</brief_description>
<description>
GLTFDocument supports reading data from a glTF file, buffer, or Godot scene. This data can then be written to the filesystem, buffer, or used to create a Godot scene.
- All of the data in a GLTF scene is stored in the [GLTFState] class. GLTFDocument processes state objects, but does not contain any scene data itself. GLTFDocument has member variables to store export configuration settings such as the image format, but is otherwise stateless. Multiple scenes can be processed with the same settings using the same GLTFDocument object and different [GLTFState] objects.
+ All of the data in a glTF scene is stored in the [GLTFState] class. GLTFDocument processes state objects, but does not contain any scene data itself. GLTFDocument has member variables to store export configuration settings such as the image format, but is otherwise stateless. Multiple scenes can be processed with the same settings using the same GLTFDocument object and different [GLTFState] objects.
GLTFDocument can be extended with arbitrary functionality by extending the [GLTFDocumentExtension] class and registering it with GLTFDocument via [method register_gltf_document_extension]. This allows for custom data to be imported and exported.
</description>
<tutorials>
@@ -21,7 +21,7 @@
<param index="2" name="state" type="GLTFState" />
<param index="3" name="flags" type="int" default="0" />
<description>
- Takes a [PackedByteArray] defining a GLTF and imports the data to the given [GLTFState] object through the [param state] parameter.
+ Takes a [PackedByteArray] defining a glTF and imports the data to the given [GLTFState] object through the [param state] parameter.
[b]Note:[/b] The [param base_path] tells [method append_from_buffer] where to find dependencies and can be empty.
</description>
</method>
@@ -32,7 +32,7 @@
<param index="2" name="flags" type="int" default="0" />
<param index="3" name="base_path" type="String" default="&quot;&quot;" />
<description>
- Takes a path to a GLTF file and imports the data at that file path to the given [GLTFState] object through the [param state] parameter.
+ Takes a path to a glTF file and imports the data at that file path to the given [GLTFState] object through the [param state] parameter.
[b]Note:[/b] The [param base_path] tells [method append_from_file] where to find dependencies and can be empty.
</description>
</method>
@@ -49,7 +49,7 @@
<return type="PackedByteArray" />
<param index="0" name="state" type="GLTFState" />
<description>
- Takes a [GLTFState] object through the [param state] parameter and returns a GLTF [PackedByteArray].
+ Takes a [GLTFState] object through the [param state] parameter and returns a glTF [PackedByteArray].
</description>
</method>
<method name="generate_scene">
@@ -91,7 +91,7 @@
</methods>
<members>
<member name="image_format" type="String" setter="set_image_format" getter="get_image_format" default="&quot;PNG&quot;">
- The user-friendly name of the export image format. This is used when exporting the GLTF file, including writing to a file and writing to a byte array.
+ The user-friendly name of the export image format. This is used when exporting the glTF file, including writing to a file and writing to a byte array.
By default, Godot allows the following options: "None", "PNG", "JPEG", "Lossless WebP", and "Lossy WebP". Support for more image formats can be added in [GLTFDocumentExtension] classes.
</member>
<member name="lossy_quality" type="float" setter="set_lossy_quality" getter="get_lossy_quality" default="0.75">
diff --git a/modules/gltf/doc_classes/GLTFDocumentExtension.xml b/modules/gltf/doc_classes/GLTFDocumentExtension.xml
index 0eabcb5022..5c548c472f 100644
--- a/modules/gltf/doc_classes/GLTFDocumentExtension.xml
+++ b/modules/gltf/doc_classes/GLTFDocumentExtension.xml
@@ -4,7 +4,7 @@
[GLTFDocument] extension class.
</brief_description>
<description>
- Extends the functionality of the [GLTFDocument] class by allowing you to run arbitrary code at various stages of GLTF import or export.
+ Extends the functionality of the [GLTFDocument] class by allowing you to run arbitrary code at various stages of glTF import or export.
To use, make a new class extending GLTFDocumentExtension, override any methods you need, make an instance of your class, and register it using [method GLTFDocument.register_gltf_document_extension].
[b]Note:[/b] Like GLTFDocument itself, all GLTFDocumentExtension classes must be stateless in order to function properly. If you need to store data, use the [code]set_additional_data[/code] and [code]get_additional_data[/code] methods in [GLTFState] or [GLTFNode].
</description>
@@ -30,7 +30,7 @@
<param index="3" name="node" type="Node" />
<description>
Part of the export process. This method is run after [method _get_saveable_image_formats] and before [method _export_post]. If this [GLTFDocumentExtension] is used for exporting images, this runs after [method _serialize_texture_json].
- 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.
+ 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_post" qualifiers="virtual">
@@ -38,7 +38,7 @@
<param index="0" name="state" type="GLTFState" />
<description>
Part of the export process. This method is run last, after all other parts of the export process.
- This method can be used to modify the final JSON of the generated GLTF file.
+ This method can be used to modify the final JSON of the generated glTF file.
</description>
</method>
<method name="_export_preflight" qualifiers="virtual">
@@ -47,7 +47,7 @@
<param index="1" name="root" type="Node" />
<description>
Part of the export process. This method is run first, before all other parts of the export process.
- The return value is used to determine if this [GLTFDocumentExtension] instance should be used for exporting a given GLTF file. If [constant OK], the export will use this [GLTFDocumentExtension] instance. If not overridden, [constant OK] is returned.
+ The return value is used to determine if this [GLTFDocumentExtension] instance should be used for exporting a given glTF file. If [constant OK], the export will use this [GLTFDocumentExtension] instance. If not overridden, [constant OK] is returned.
</description>
</method>
<method name="_export_preserialize" qualifiers="virtual">
@@ -86,7 +86,7 @@
<return type="PackedStringArray" />
<description>
Part of the import process. This method is run after [method _import_preflight] and before [method _parse_node_extensions].
- Returns an array of the GLTF extensions supported by this GLTFDocumentExtension class. This is used to validate if a GLTF file with required extensions can be loaded.
+ Returns an array of the glTF extensions supported by this GLTFDocumentExtension class. This is used to validate if a glTF file with required extensions can be loaded.
</description>
</method>
<method name="_import_node" qualifiers="virtual">
@@ -123,7 +123,7 @@
<param index="1" name="extensions" type="PackedStringArray" />
<description>
Part of the import process. This method is run first, before all other parts of the import process.
- The return value is used to determine if this [GLTFDocumentExtension] instance should be used for importing a given GLTF file. If [constant OK], the import will use this [GLTFDocumentExtension] instance. If not overridden, [constant OK] is returned.
+ The return value is used to determine if this [GLTFDocumentExtension] instance should be used for importing a given glTF file. If [constant OK], the import will use this [GLTFDocumentExtension] instance. If not overridden, [constant OK] is returned.
</description>
</method>
<method name="_parse_image_data" qualifiers="virtual">
@@ -134,7 +134,7 @@
<param index="3" name="ret_image" type="Image" />
<description>
Part of the import process. This method is run after [method _parse_node_extensions] and before [method _parse_texture_json].
- Runs when parsing image data from a GLTF file. The data could be sourced from a separate file, a URI, or a buffer, and then is passed as a byte array.
+ Runs when parsing image data from a glTF file. The data could be sourced from a separate file, a URI, or a buffer, and then is passed as a byte array.
</description>
</method>
<method name="_parse_node_extensions" qualifiers="virtual">
@@ -154,7 +154,7 @@
<param index="2" name="ret_gltf_texture" type="GLTFTexture" />
<description>
Part of the import process. This method is run after [method _parse_image_data] and before [method _generate_scene_node].
- Runs when parsing the texture JSON from the GLTF textures array. This can be used to set the source image index to use as the texture.
+ Runs when parsing the texture JSON from the glTF textures array. This can be used to set the source image index to use as the texture.
</description>
</method>
<method name="_save_image_at_path" qualifiers="virtual">
@@ -166,7 +166,7 @@
<param index="4" name="lossy_quality" type="float" />
<description>
Part of the export process. This method is run after [method _get_saveable_image_formats] and before [method _serialize_texture_json].
- This method is run when saving images separately from the GLTF file. When images are embedded, [method _serialize_image_to_bytes] runs instead. Note that these methods only run when this [GLTFDocumentExtension] is selected as the image exporter.
+ This method is run when saving images separately from the glTF file. When images are embedded, [method _serialize_image_to_bytes] runs instead. Note that these methods only run when this [GLTFDocumentExtension] is selected as the image exporter.
</description>
</method>
<method name="_serialize_image_to_bytes" qualifiers="virtual">
@@ -178,7 +178,7 @@
<param index="4" name="lossy_quality" type="float" />
<description>
Part of the export process. This method is run after [method _get_saveable_image_formats] and before [method _serialize_texture_json].
- This method is run when embedding images in the GLTF file. When images are saved separately, [method _save_image_at_path] runs instead. Note that these methods only run when this [GLTFDocumentExtension] is selected as the image exporter.
+ This method is run when embedding images in the glTF file. When images are saved separately, [method _save_image_at_path] runs instead. Note that these methods only run when this [GLTFDocumentExtension] is selected as the image exporter.
This method must set the image MIME type in the [param image_dict] with the [code]"mimeType"[/code] key. For example, for a PNG image, it would be set to [code]"image/png"[/code]. The return value must be a [PackedByteArray] containing the image data.
</description>
</method>
diff --git a/modules/gltf/doc_classes/GLTFLight.xml b/modules/gltf/doc_classes/GLTFLight.xml
index 87ea159e7c..e07d24a144 100644
--- a/modules/gltf/doc_classes/GLTFLight.xml
+++ b/modules/gltf/doc_classes/GLTFLight.xml
@@ -1,14 +1,14 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="GLTFLight" inherits="Resource" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
<brief_description>
- Represents a GLTF light.
+ Represents a glTF light.
</brief_description>
<description>
- Represents a light as defined by the [code]KHR_lights_punctual[/code] GLTF extension.
+ Represents a light as defined by the [code]KHR_lights_punctual[/code] glTF extension.
</description>
<tutorials>
<link title="Runtime file loading and saving">$DOCS_URL/tutorials/io/runtime_file_loading_and_saving.html</link>
- <link title="KHR_lights_punctual GLTF extension spec">https://github.com/KhronosGroup/glTF/blob/main/extensions/2.0/Khronos/KHR_lights_punctual</link>
+ <link title="KHR_lights_punctual glTF extension spec">https://github.com/KhronosGroup/glTF/blob/main/extensions/2.0/Khronos/KHR_lights_punctual</link>
</tutorials>
<methods>
<method name="from_dictionary" qualifiers="static">
@@ -70,7 +70,7 @@
At this angle, the light drops off to zero brightness. Between the inner and outer cone angles, there is a transition from full brightness to zero brightness. If this angle is a half turn, then the spotlight emits in all directions. When creating a Godot [SpotLight3D], the outer cone angle is used as the angle of the spotlight.
</member>
<member name="range" type="float" setter="set_range" getter="get_range" default="inf">
- The range of the light, beyond which the light has no effect. GLTF lights with no range defined behave like physical lights (which have infinite range). When creating a Godot light, the range is clamped to 4096.
+ The range of the light, beyond which the light has no effect. glTF lights with no range defined behave like physical lights (which have infinite range). When creating a Godot light, the range is clamped to 4096.
</member>
</members>
</class>
diff --git a/modules/gltf/doc_classes/GLTFMesh.xml b/modules/gltf/doc_classes/GLTFMesh.xml
index b4c3db7618..da73c20c1d 100644
--- a/modules/gltf/doc_classes/GLTFMesh.xml
+++ b/modules/gltf/doc_classes/GLTFMesh.xml
@@ -1,10 +1,10 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="GLTFMesh" inherits="Resource" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
<brief_description>
- GLTFMesh represents a GLTF mesh.
+ GLTFMesh represents a glTF mesh.
</brief_description>
<description>
- GLTFMesh handles 3D mesh data imported from GLTF files. It includes properties for blend channels, blend weights, instance materials, and the mesh itself.
+ GLTFMesh handles 3D mesh data imported from glTF files. It includes properties for blend channels, blend weights, instance materials, and the mesh itself.
</description>
<tutorials>
<link title="Runtime file loading and saving">$DOCS_URL/tutorials/io/runtime_file_loading_and_saving.html</link>
@@ -15,7 +15,7 @@
<param index="0" name="extension_name" type="StringName" />
<description>
Gets additional arbitrary data in this [GLTFMesh] instance. This can be used to keep per-node state data in [GLTFDocumentExtension] classes, which is important because they are stateless.
- 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.
+ 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="set_additional_data">
@@ -24,7 +24,7 @@
<param index="1" name="additional_data" type="Variant" />
<description>
Sets additional arbitrary data in this [GLTFMesh] instance. This can be used to keep per-node state data in [GLTFDocumentExtension] classes, which is important because they are stateless.
- The first argument should be the [GLTFDocumentExtension] name (does not have to match the extension name in the GLTF file), and the second argument can be anything you want.
+ The first argument should be the [GLTFDocumentExtension] name (does not have to match the extension name in the glTF file), and the second argument can be anything you want.
</description>
</method>
</methods>
diff --git a/modules/gltf/doc_classes/GLTFNode.xml b/modules/gltf/doc_classes/GLTFNode.xml
index 4a7570e4bc..2786c25e9a 100644
--- a/modules/gltf/doc_classes/GLTFNode.xml
+++ b/modules/gltf/doc_classes/GLTFNode.xml
@@ -1,15 +1,15 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="GLTFNode" inherits="Resource" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
<brief_description>
- GLTF node class.
+ glTF node class.
</brief_description>
<description>
- Represents a GLTF node. GLTF nodes may have names, transforms, children (other GLTF nodes), and more specialized properties (represented by their own classes).
- GLTF nodes generally exist inside of [GLTFState] which represents all data of a GLTF file. Most of GLTFNode's properties are indices of other data in the GLTF file. You can extend a GLTF node with additional properties by using [method get_additional_data] and [method set_additional_data].
+ Represents a glTF node. glTF nodes may have names, transforms, children (other glTF nodes), and more specialized properties (represented by their own classes).
+ glTF nodes generally exist inside of [GLTFState] which represents all data of a glTF file. Most of GLTFNode's properties are indices of other data in the glTF file. You can extend a glTF node with additional properties by using [method get_additional_data] and [method set_additional_data].
</description>
<tutorials>
<link title="Runtime file loading and saving">$DOCS_URL/tutorials/io/runtime_file_loading_and_saving.html</link>
- <link title="GLTF scene and node spec">https://github.com/KhronosGroup/glTF-Tutorials/blob/master/gltfTutorial/gltfTutorial_004_ScenesNodes.md"</link>
+ <link title="glTF scene and node spec">https://github.com/KhronosGroup/glTF-Tutorials/blob/master/gltfTutorial/gltfTutorial_004_ScenesNodes.md"</link>
</tutorials>
<methods>
<method name="get_additional_data">
@@ -17,7 +17,7 @@
<param index="0" name="extension_name" type="StringName" />
<description>
Gets additional arbitrary data in this [GLTFNode] instance. This can be used to keep per-node state data in [GLTFDocumentExtension] classes, which is important because they are stateless.
- 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.
+ 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="set_additional_data">
@@ -26,25 +26,25 @@
<param index="1" name="additional_data" type="Variant" />
<description>
Sets additional arbitrary data in this [GLTFNode] instance. This can be used to keep per-node state data in [GLTFDocumentExtension] classes, which is important because they are stateless.
- The first argument should be the [GLTFDocumentExtension] name (does not have to match the extension name in the GLTF file), and the second argument can be anything you want.
+ The first argument should be the [GLTFDocumentExtension] name (does not have to match the extension name in the glTF file), and the second argument can be anything you want.
</description>
</method>
</methods>
<members>
<member name="camera" type="int" setter="set_camera" getter="get_camera" default="-1">
- If this GLTF node is a camera, the index of the [GLTFCamera] in the [GLTFState] that describes the camera's properties. If -1, this node is not a camera.
+ If this glTF node is a camera, the index of the [GLTFCamera] in the [GLTFState] that describes the camera's properties. If -1, this node is not a camera.
</member>
<member name="children" type="PackedInt32Array" setter="set_children" getter="get_children" default="PackedInt32Array()">
- The indices of the child nodes in the [GLTFState]. If this GLTF node has no children, this will be an empty array.
+ The indices of the child nodes in the [GLTFState]. If this glTF node has no children, this will be an empty array.
</member>
<member name="height" type="int" setter="set_height" getter="get_height" default="-1">
How deep into the node hierarchy this node is. A root node will have a height of 0, its children will have a height of 1, and so on. If -1, the height has not been calculated.
</member>
<member name="light" type="int" setter="set_light" getter="get_light" default="-1">
- If this GLTF node is a light, the index of the [GLTFLight] in the [GLTFState] that describes the light's properties. If -1, this node is not a light.
+ If this glTF node is a light, the index of the [GLTFLight] in the [GLTFState] that describes the light's properties. If -1, this node is not a light.
</member>
<member name="mesh" type="int" setter="set_mesh" getter="get_mesh" default="-1">
- If this GLTF node is a mesh, the index of the [GLTFMesh] in the [GLTFState] that describes the mesh's properties. If -1, this node is not a mesh.
+ If this glTF node is a mesh, the index of the [GLTFMesh] in the [GLTFState] that describes the mesh's properties. If -1, this node is not a mesh.
</member>
<member name="original_name" type="String" setter="set_original_name" getter="get_original_name" default="&quot;&quot;">
The original name of the node.
@@ -53,22 +53,22 @@
The index of the parent node in the [GLTFState]. If -1, this node is a root node.
</member>
<member name="position" type="Vector3" setter="set_position" getter="get_position" default="Vector3(0, 0, 0)">
- The position of the GLTF node relative to its parent.
+ The position of the glTF node relative to its parent.
</member>
<member name="rotation" type="Quaternion" setter="set_rotation" getter="get_rotation" default="Quaternion(0, 0, 0, 1)">
- The rotation of the GLTF node relative to its parent.
+ The rotation of the glTF node relative to its parent.
</member>
<member name="scale" type="Vector3" setter="set_scale" getter="get_scale" default="Vector3(1, 1, 1)">
- The scale of the GLTF node relative to its parent.
+ The scale of the glTF node relative to its parent.
</member>
<member name="skeleton" type="int" setter="set_skeleton" getter="get_skeleton" default="-1">
- If this GLTF node has a skeleton, the index of the [GLTFSkeleton] in the [GLTFState] that describes the skeleton's properties. If -1, this node does not have a skeleton.
+ If this glTF node has a skeleton, the index of the [GLTFSkeleton] in the [GLTFState] that describes the skeleton's properties. If -1, this node does not have a skeleton.
</member>
<member name="skin" type="int" setter="set_skin" getter="get_skin" default="-1">
- If this GLTF node has a skin, the index of the [GLTFSkin] in the [GLTFState] that describes the skin's properties. If -1, this node does not have a skin.
+ If this glTF node has a skin, the index of the [GLTFSkin] in the [GLTFState] that describes the skin's properties. If -1, this node does not have a skin.
</member>
<member name="xform" type="Transform3D" setter="set_xform" getter="get_xform" default="Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0)">
- The transform of the GLTF node relative to its parent. This property is usually unused since the position, rotation, and scale properties are preferred.
+ The transform of the glTF node relative to its parent. This property is usually unused since the position, rotation, and scale properties are preferred.
</member>
</members>
</class>
diff --git a/modules/gltf/doc_classes/GLTFPhysicsBody.xml b/modules/gltf/doc_classes/GLTFPhysicsBody.xml
index cd701e2f2f..1a76b190ba 100644
--- a/modules/gltf/doc_classes/GLTFPhysicsBody.xml
+++ b/modules/gltf/doc_classes/GLTFPhysicsBody.xml
@@ -1,21 +1,21 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="GLTFPhysicsBody" inherits="Resource" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
<brief_description>
- Represents a GLTF physics body.
+ Represents a glTF physics body.
</brief_description>
<description>
- Represents a physics body as an intermediary between the [code]OMI_physics_body[/code] GLTF data and Godot's nodes, and it's abstracted in a way that allows adding support for different GLTF physics extensions in the future.
+ Represents a physics body as an intermediary between the [code]OMI_physics_body[/code] glTF data and Godot's nodes, and it's abstracted in a way that allows adding support for different glTF physics extensions in the future.
</description>
<tutorials>
<link title="Runtime file loading and saving">$DOCS_URL/tutorials/io/runtime_file_loading_and_saving.html</link>
- <link title="OMI_physics_body GLTF extension">https://github.com/omigroup/gltf-extensions/tree/main/extensions/2.0/OMI_physics_body</link>
+ <link title="OMI_physics_body glTF extension">https://github.com/omigroup/gltf-extensions/tree/main/extensions/2.0/OMI_physics_body</link>
</tutorials>
<methods>
<method name="from_dictionary" qualifiers="static">
<return type="GLTFPhysicsBody" />
<param index="0" name="dictionary" type="Dictionary" />
<description>
- Creates a new GLTFPhysicsBody instance by parsing the given [Dictionary] in the [code]OMI_physics_body[/code] GLTF extension format.
+ Creates a new GLTFPhysicsBody instance by parsing the given [Dictionary] in the [code]OMI_physics_body[/code] glTF extension format.
</description>
</method>
<method name="from_node" qualifiers="static">
@@ -28,7 +28,7 @@
<method name="to_dictionary" qualifiers="const">
<return type="Dictionary" />
<description>
- Serializes this GLTFPhysicsBody instance into a [Dictionary]. It will be in the format expected by the [code]OMI_physics_body[/code] GLTF extension.
+ Serializes this GLTFPhysicsBody instance into a [Dictionary]. It will be in the format expected by the [code]OMI_physics_body[/code] glTF extension.
</description>
</method>
<method name="to_node" qualifiers="const">
diff --git a/modules/gltf/doc_classes/GLTFPhysicsShape.xml b/modules/gltf/doc_classes/GLTFPhysicsShape.xml
index a4aaf9415c..53872a942f 100644
--- a/modules/gltf/doc_classes/GLTFPhysicsShape.xml
+++ b/modules/gltf/doc_classes/GLTFPhysicsShape.xml
@@ -1,15 +1,15 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="GLTFPhysicsShape" inherits="Resource" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
<brief_description>
- Represents a GLTF physics shape.
+ Represents a glTF physics shape.
</brief_description>
<description>
- Represents a physics shape as defined by the [code]OMI_physics_shape[/code] or [code]OMI_collider[/code] GLTF extensions. This class is an intermediary between the GLTF data and Godot's nodes, and it's abstracted in a way that allows adding support for different GLTF physics extensions in the future.
+ Represents a physics shape as defined by the [code]OMI_physics_shape[/code] or [code]OMI_collider[/code] glTF extensions. This class is an intermediary between the glTF data and Godot's nodes, and it's abstracted in a way that allows adding support for different glTF physics extensions in the future.
</description>
<tutorials>
<link title="Runtime file loading and saving">$DOCS_URL/tutorials/io/runtime_file_loading_and_saving.html</link>
- <link title="OMI_physics_shape GLTF extension">https://github.com/omigroup/gltf-extensions/tree/main/extensions/2.0/OMI_physics_shape</link>
- <link title="OMI_collider GLTF extension">https://github.com/omigroup/gltf-extensions/tree/main/extensions/2.0/Archived/OMI_collider</link>
+ <link title="OMI_physics_shape glTF extension">https://github.com/omigroup/gltf-extensions/tree/main/extensions/2.0/OMI_physics_shape</link>
+ <link title="OMI_collider glTF extension">https://github.com/omigroup/gltf-extensions/tree/main/extensions/2.0/Archived/OMI_collider</link>
</tutorials>
<methods>
<method name="from_dictionary" qualifiers="static">
@@ -66,7 +66,7 @@
This is the only variable not used in the [method to_node] method, it's intended to be used alongside when deciding where to add the generated node as a child.
</member>
<member name="mesh_index" type="int" setter="set_mesh_index" getter="get_mesh_index" default="-1">
- The index of the shape's mesh in the GLTF file. This is only used when the shape type is "hull" (convex hull) or "trimesh" (concave trimesh).
+ The index of the shape's mesh in the glTF file. This is only used when the shape type is "hull" (convex hull) or "trimesh" (concave trimesh).
</member>
<member name="radius" type="float" setter="set_radius" getter="get_radius" default="0.5">
The radius of the shape, in meters. This is only used when the shape type is "capsule", "cylinder", or "sphere". This value should not be negative.
diff --git a/modules/gltf/doc_classes/GLTFSkeleton.xml b/modules/gltf/doc_classes/GLTFSkeleton.xml
index ac03a6ee9e..2dd3a37413 100644
--- a/modules/gltf/doc_classes/GLTFSkeleton.xml
+++ b/modules/gltf/doc_classes/GLTFSkeleton.xml
@@ -22,7 +22,7 @@
<method name="get_godot_bone_node">
<return type="Dictionary" />
<description>
- Returns a [Dictionary] that maps skeleton bone indices to the indices of GLTF nodes. This property is unused during import, and only set during export. In a GLTF file, a bone is a node, so Godot converts skeleton bones to GLTF nodes.
+ Returns a [Dictionary] that maps skeleton bone indices to the indices of glTF nodes. This property is unused during import, and only set during export. In a glTF file, a bone is a node, so Godot converts skeleton bones to glTF nodes.
</description>
</method>
<method name="get_godot_skeleton">
@@ -39,7 +39,7 @@
<return type="void" />
<param index="0" name="godot_bone_node" type="Dictionary" />
<description>
- Sets a [Dictionary] that maps skeleton bone indices to the indices of GLTF nodes. This property is unused during import, and only set during export. In a GLTF file, a bone is a node, so Godot converts skeleton bones to GLTF nodes.
+ Sets a [Dictionary] that maps skeleton bone indices to the indices of glTF nodes. This property is unused during import, and only set during export. In a glTF file, a bone is a node, so Godot converts skeleton bones to glTF nodes.
</description>
</method>
<method name="set_unique_names">
diff --git a/modules/gltf/doc_classes/GLTFSpecGloss.xml b/modules/gltf/doc_classes/GLTFSpecGloss.xml
index 722fa5e9ae..11151f53d0 100644
--- a/modules/gltf/doc_classes/GLTFSpecGloss.xml
+++ b/modules/gltf/doc_classes/GLTFSpecGloss.xml
@@ -1,14 +1,14 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="GLTFSpecGloss" inherits="Resource" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
<brief_description>
- Archived GLTF extension for specular/glossy materials.
+ Archived glTF extension for specular/glossy materials.
</brief_description>
<description>
- KHR_materials_pbrSpecularGlossiness is an archived GLTF extension. This means that it is deprecated and not recommended for new files. However, it is still supported for loading old files.
+ KHR_materials_pbrSpecularGlossiness is an archived glTF extension. This means that it is deprecated and not recommended for new files. However, it is still supported for loading old files.
</description>
<tutorials>
<link title="Runtime file loading and saving">$DOCS_URL/tutorials/io/runtime_file_loading_and_saving.html</link>
- <link title="KHR_materials_pbrSpecularGlossiness GLTF extension spec">https://github.com/KhronosGroup/glTF/blob/main/extensions/2.0/Archived/KHR_materials_pbrSpecularGlossiness</link>
+ <link title="KHR_materials_pbrSpecularGlossiness glTF extension spec">https://github.com/KhronosGroup/glTF/blob/main/extensions/2.0/Archived/KHR_materials_pbrSpecularGlossiness</link>
</tutorials>
<members>
<member name="diffuse_factor" type="Color" setter="set_diffuse_factor" getter="get_diffuse_factor" default="Color(1, 1, 1, 1)">
diff --git a/modules/gltf/doc_classes/GLTFState.xml b/modules/gltf/doc_classes/GLTFState.xml
index 21a0527813..c049acf557 100644
--- a/modules/gltf/doc_classes/GLTFState.xml
+++ b/modules/gltf/doc_classes/GLTFState.xml
@@ -1,15 +1,15 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="GLTFState" inherits="Resource" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
<brief_description>
- Represents all data of a GLTF file.
+ Represents all data of a glTF file.
</brief_description>
<description>
- Contains all nodes and resources of a GLTF file. This is used by [GLTFDocument] as data storage, which allows [GLTFDocument] and all [GLTFDocumentExtension] classes to remain stateless.
- GLTFState can be populated by [GLTFDocument] reading a file or by converting a Godot scene. Then the data can either be used to create a Godot scene or save to a GLTF file. The code that converts to/from a Godot scene can be intercepted at arbitrary points by [GLTFDocumentExtension] classes. This allows for custom data to be stored in the GLTF file or for custom data to be converted to/from Godot nodes.
+ Contains all nodes and resources of a glTF file. This is used by [GLTFDocument] as data storage, which allows [GLTFDocument] and all [GLTFDocumentExtension] classes to remain stateless.
+ GLTFState can be populated by [GLTFDocument] reading a file or by converting a Godot scene. Then the data can either be used to create a Godot scene or save to a glTF file. The code that converts to/from a Godot scene can be intercepted at arbitrary points by [GLTFDocumentExtension] classes. This allows for custom data to be stored in the glTF file or for custom data to be converted to/from Godot nodes.
</description>
<tutorials>
<link title="Runtime file loading and saving">$DOCS_URL/tutorials/io/runtime_file_loading_and_saving.html</link>
- <link title="GLTF asset header schema">https://github.com/KhronosGroup/glTF/blob/main/specification/2.0/schema/asset.schema.json"</link>
+ <link title="glTF asset header schema">https://github.com/KhronosGroup/glTF/blob/main/specification/2.0/schema/asset.schema.json"</link>
</tutorials>
<methods>
<method name="add_used_extension">
@@ -17,7 +17,7 @@
<param index="0" name="extension_name" type="String" />
<param index="1" name="required" type="bool" />
<description>
- Appends an extension to the list of extensions used by this GLTF file during serialization. If [param required] is true, the extension will also be added to the list of required extensions. Do not run this in [method GLTFDocumentExtension._export_post], as that stage is too late to add extensions. The final list is sorted alphabetically.
+ Appends an extension to the list of extensions used by this glTF file during serialization. If [param required] is true, the extension will also be added to the list of required extensions. Do not run this in [method GLTFDocumentExtension._export_post], as that stage is too late to add extensions. The final list is sorted alphabetically.
</description>
</method>
<method name="append_data_to_buffers">
@@ -38,27 +38,27 @@
<param index="0" name="extension_name" type="StringName" />
<description>
Gets additional arbitrary data in this [GLTFState] instance. This can be used to keep per-file state data in [GLTFDocumentExtension] classes, which is important because they are stateless.
- 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.
+ 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_animation_player">
<return type="AnimationPlayer" />
<param index="0" name="idx" type="int" />
<description>
- Returns the [AnimationPlayer] node with the given index. These nodes are only used during the export process when converting Godot [AnimationPlayer] nodes to GLTF animations.
+ Returns the [AnimationPlayer] node with the given index. These nodes are only used during the export process when converting Godot [AnimationPlayer] nodes to glTF animations.
</description>
</method>
<method name="get_animation_players_count">
<return type="int" />
<param index="0" name="idx" type="int" />
<description>
- Returns the number of [AnimationPlayer] nodes in this [GLTFState]. These nodes are only used during the export process when converting Godot [AnimationPlayer] nodes to GLTF animations.
+ Returns the number of [AnimationPlayer] nodes in this [GLTFState]. These nodes are only used during the export process when converting Godot [AnimationPlayer] nodes to glTF animations.
</description>
</method>
<method name="get_animations">
<return type="GLTFAnimation[]" />
<description>
- Returns an array of all [GLTFAnimation]s in the GLTF file. When importing, these will be generated as animations in an [AnimationPlayer] node. When exporting, these will be generated from Godot [AnimationPlayer] nodes.
+ Returns an array of all [GLTFAnimation]s in the glTF file. When importing, these will be generated as animations in an [AnimationPlayer] node. When exporting, these will be generated from Godot [AnimationPlayer] nodes.
</description>
</method>
<method name="get_buffer_views">
@@ -69,7 +69,7 @@
<method name="get_cameras">
<return type="GLTFCamera[]" />
<description>
- Returns an array of all [GLTFCamera]s in the GLTF file. These are the cameras that the [member GLTFNode.camera] index refers to.
+ Returns an array of all [GLTFCamera]s in the glTF file. These are the cameras that the [member GLTFNode.camera] index refers to.
</description>
</method>
<method name="get_handle_binary_image">
@@ -80,13 +80,13 @@
<method name="get_images">
<return type="Texture2D[]" />
<description>
- Gets the images of the GLTF file as an array of [Texture2D]s. These are the images that the [member GLTFTexture.src_image] index refers to.
+ Gets the images of the glTF file as an array of [Texture2D]s. These are the images that the [member GLTFTexture.src_image] index refers to.
</description>
</method>
<method name="get_lights">
<return type="GLTFLight[]" />
<description>
- Returns an array of all [GLTFLight]s in the GLTF file. These are the lights that the [member GLTFNode.light] index refers to.
+ Returns an array of all [GLTFLight]s in the glTF file. These are the lights that the [member GLTFNode.light] index refers to.
</description>
</method>
<method name="get_materials">
@@ -97,7 +97,7 @@
<method name="get_meshes">
<return type="GLTFMesh[]" />
<description>
- Returns an array of all [GLTFMesh]es in the GLTF file. These are the meshes that the [member GLTFNode.mesh] index refers to.
+ Returns an array of all [GLTFMesh]es in the glTF file. These are the meshes that the [member GLTFNode.mesh] index refers to.
</description>
</method>
<method name="get_node_index">
@@ -111,7 +111,7 @@
<method name="get_nodes">
<return type="GLTFNode[]" />
<description>
- Returns an array of all [GLTFNode]s in the GLTF file. These are the nodes that [member GLTFNode.children] and [member root_nodes] refer to. This includes nodes that may not be generated in the Godot scene, or nodes that may generate multiple Godot scene nodes.
+ Returns an array of all [GLTFNode]s in the glTF file. These are the nodes that [member GLTFNode.children] and [member root_nodes] refer to. This includes nodes that may not be generated in the Godot scene, or nodes that may generate multiple Godot scene nodes.
</description>
</method>
<method name="get_scene_node">
@@ -125,19 +125,19 @@
<method name="get_skeletons">
<return type="GLTFSkeleton[]" />
<description>
- Returns an array of all [GLTFSkeleton]s in the GLTF file. These are the skeletons that the [member GLTFNode.skeleton] index refers to.
+ Returns an array of all [GLTFSkeleton]s in the glTF file. These are the skeletons that the [member GLTFNode.skeleton] index refers to.
</description>
</method>
<method name="get_skins">
<return type="GLTFSkin[]" />
<description>
- Returns an array of all [GLTFSkin]s in the GLTF file. These are the skins that the [member GLTFNode.skin] index refers to.
+ Returns an array of all [GLTFSkin]s in the glTF file. These are the skins that the [member GLTFNode.skin] index refers to.
</description>
</method>
<method name="get_texture_samplers">
<return type="GLTFTextureSampler[]" />
<description>
- Retrieves the array of texture samplers that are used by the textures contained in the GLTF.
+ Retrieves the array of texture samplers that are used by the textures contained in the glTF.
</description>
</method>
<method name="get_textures">
@@ -169,7 +169,7 @@
<param index="1" name="additional_data" type="Variant" />
<description>
Sets additional arbitrary data in this [GLTFState] instance. This can be used to keep per-file state data in [GLTFDocumentExtension] classes, which is important because they are stateless.
- The first argument should be the [GLTFDocumentExtension] name (does not have to match the extension name in the GLTF file), and the second argument can be anything you want.
+ The first argument should be the [GLTFDocumentExtension] name (does not have to match the extension name in the glTF file), and the second argument can be anything you want.
</description>
</method>
<method name="set_animations">
@@ -250,7 +250,7 @@
<return type="void" />
<param index="0" name="texture_samplers" type="GLTFTextureSampler[]" />
<description>
- Sets the array of texture samplers that are used by the textures contained in the GLTF.
+ Sets the array of texture samplers that are used by the textures contained in the glTF.
</description>
</method>
<method name="set_textures">
@@ -279,17 +279,17 @@
The baking fps of the animation for either import or export.
</member>
<member name="base_path" type="String" setter="set_base_path" getter="get_base_path" default="&quot;&quot;">
- The folder path associated with this GLTF data. This is used to find other files the GLTF file references, like images or binary buffers. This will be set during import when appending from a file, and will be set during export when writing to a file.
+ The folder path associated with this glTF data. This is used to find other files the glTF file references, like images or binary buffers. This will be set during import when appending from a file, and will be set during export when writing to a file.
</member>
<member name="buffers" type="PackedByteArray[]" setter="set_buffers" getter="get_buffers" default="[]">
</member>
<member name="copyright" type="String" setter="set_copyright" getter="get_copyright" default="&quot;&quot;">
- The copyright string in the asset header of the GLTF file. This is set during import if present and export if non-empty. See the GLTF asset header documentation for more information.
+ The copyright string in the asset header of the glTF file. This is set during import if present and export if non-empty. See the glTF asset header documentation for more information.
</member>
<member name="create_animations" type="bool" setter="set_create_animations" getter="get_create_animations" default="true">
</member>
<member name="filename" type="String" setter="set_filename" getter="get_filename" default="&quot;&quot;">
- The file name associated with this GLTF data. If it ends with [code].gltf[/code], this is text-based GLTF, otherwise this is binary GLB. This will be set during import when appending from a file, and will be set during export when writing to a file. If writing to a buffer, this will be an empty string.
+ The file name associated with this glTF data. If it ends with [code].gltf[/code], this is text-based glTF, otherwise this is binary GLB. This will be set during import when appending from a file, and will be set during export when writing to a file. If writing to a buffer, this will be an empty string.
</member>
<member name="glb_data" type="PackedByteArray" setter="set_glb_data" getter="get_glb_data" default="PackedByteArray()">
The binary buffer attached to a .glb file.
@@ -305,10 +305,10 @@
<member name="minor_version" type="int" setter="set_minor_version" getter="get_minor_version" default="0">
</member>
<member name="root_nodes" type="PackedInt32Array" setter="set_root_nodes" getter="get_root_nodes" default="PackedInt32Array()">
- The root nodes of the GLTF file. Typically, a GLTF file will only have one scene, and therefore one root node. However, a GLTF file may have multiple scenes and therefore multiple root nodes, which will be generated as siblings of each other and as children of the root node of the generated Godot scene.
+ The root nodes of the glTF file. Typically, a glTF file will only have one scene, and therefore one root node. However, a glTF file may have multiple scenes and therefore multiple root nodes, which will be generated as siblings of each other and as children of the root node of the generated Godot scene.
</member>
<member name="scene_name" type="String" setter="set_scene_name" getter="get_scene_name" default="&quot;&quot;">
- The name of the scene. When importing, if not specified, this will be the file name. When exporting, if specified, the scene name will be saved to the GLTF file.
+ The name of the scene. When importing, if not specified, this will be the file name. When exporting, if specified, the scene name will be saved to the glTF file.
</member>
<member name="use_named_skin_binds" type="bool" setter="set_use_named_skin_binds" getter="get_use_named_skin_binds" default="false">
</member>
diff --git a/modules/gltf/doc_classes/GLTFTexture.xml b/modules/gltf/doc_classes/GLTFTexture.xml
index 9ad7c0f4c6..2a868a8ba3 100644
--- a/modules/gltf/doc_classes/GLTFTexture.xml
+++ b/modules/gltf/doc_classes/GLTFTexture.xml
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="GLTFTexture" inherits="Resource" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
<brief_description>
- GLTFTexture represents a texture in a GLTF file.
+ GLTFTexture represents a texture in a glTF file.
</brief_description>
<description>
</description>
diff --git a/modules/gltf/doc_classes/GLTFTextureSampler.xml b/modules/gltf/doc_classes/GLTFTextureSampler.xml
index 2b5bad6724..d00ab463c2 100644
--- a/modules/gltf/doc_classes/GLTFTextureSampler.xml
+++ b/modules/gltf/doc_classes/GLTFTextureSampler.xml
@@ -1,10 +1,10 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="GLTFTextureSampler" inherits="Resource" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
<brief_description>
- Represents a GLTF texture sampler
+ Represents a glTF texture sampler
</brief_description>
<description>
- Represents a texture sampler as defined by the base GLTF spec. Texture samplers in GLTF specify how to sample data from the texture's base image, when rendering the texture on an object.
+ Represents a texture sampler as defined by the base glTF spec. Texture samplers in glTF specify how to sample data from the texture's base image, when rendering the texture on an object.
</description>
<tutorials>
<link title="Runtime file loading and saving">$DOCS_URL/tutorials/io/runtime_file_loading_and_saving.html</link>
diff --git a/modules/gltf/editor/editor_scene_importer_blend.cpp b/modules/gltf/editor/editor_scene_importer_blend.cpp
index ec3ea9bcae..0aab6e84b4 100644
--- a/modules/gltf/editor/editor_scene_importer_blend.cpp
+++ b/modules/gltf/editor/editor_scene_importer_blend.cpp
@@ -115,8 +115,15 @@ Node *EditorSceneFormatImporterBlend::import_scene(const String &p_path, uint32_
List<String> *r_missing_deps, Error *r_err) {
String blender_path = EDITOR_GET("filesystem/import/blender/blender_path");
- if (blender_major_version == -1 || blender_minor_version == -1) {
- _get_blender_version(blender_path, blender_major_version, blender_minor_version, nullptr);
+ ERR_FAIL_COND_V_MSG(blender_path.is_empty(), nullptr, "Blender path is empty, check your Editor Settings.");
+ ERR_FAIL_COND_V_MSG(!FileAccess::exists(blender_path), nullptr, vformat("Invalid Blender path: %s, check your Editor Settings.", blender_path));
+
+ if (blender_major_version == -1 || blender_minor_version == -1 || last_tested_blender_path != blender_path) {
+ String error;
+ if (!_get_blender_version(blender_path, blender_major_version, blender_minor_version, &error)) {
+ ERR_FAIL_V_MSG(nullptr, error);
+ }
+ last_tested_blender_path = blender_path;
}
// Get global paths for source and sink.
@@ -227,6 +234,18 @@ Node *EditorSceneFormatImporterBlend::import_scene(const String &p_path, uint32_
} else {
parameters_map["export_normals"] = false;
}
+
+ if (blender_major_version > 4 || (blender_major_version == 4 && blender_minor_version >= 1)) {
+ if (p_options.has(SNAME("blender/meshes/export_geometry_nodes_instances")) && p_options[SNAME("blender/meshes/export_geometry_nodes_instances")]) {
+ parameters_map["export_gn_mesh"] = true;
+ if (blender_major_version == 4 && blender_minor_version == 1) {
+ // There is a bug in Blender 4.1 where it can't export lights and geometry nodes at the same time, one must be disabled.
+ parameters_map["export_lights"] = false;
+ }
+ } else {
+ parameters_map["export_gn_mesh"] = false;
+ }
+ }
if (p_options.has(SNAME("blender/meshes/tangents")) && p_options[SNAME("blender/meshes/tangents")]) {
parameters_map["export_tangents"] = true;
} else {
@@ -350,6 +369,7 @@ void EditorSceneFormatImporterBlend::get_import_options(const String &p_path, Li
ADD_OPTION_BOOL("blender/meshes/colors", false);
ADD_OPTION_BOOL("blender/meshes/uvs", true);
ADD_OPTION_BOOL("blender/meshes/normals", true);
+ ADD_OPTION_BOOL("blender/meshes/export_geometry_nodes_instances", false);
ADD_OPTION_BOOL("blender/meshes/tangents", true);
ADD_OPTION_ENUM("blender/meshes/skins", "None,4 Influences (Compatible),All Influences", BLEND_BONE_INFLUENCES_ALL);
ADD_OPTION_BOOL("blender/meshes/export_bones_deforming_mesh_only", false);
diff --git a/modules/gltf/editor/editor_scene_importer_blend.h b/modules/gltf/editor/editor_scene_importer_blend.h
index 8a6c65a624..6adace9276 100644
--- a/modules/gltf/editor/editor_scene_importer_blend.h
+++ b/modules/gltf/editor/editor_scene_importer_blend.h
@@ -45,6 +45,7 @@ class EditorSceneFormatImporterBlend : public EditorSceneFormatImporter {
int blender_major_version = -1;
int blender_minor_version = -1;
+ String last_tested_blender_path;
public:
enum {
diff --git a/modules/gltf/extensions/gltf_document_extension_convert_importer_mesh.cpp b/modules/gltf/extensions/gltf_document_extension_convert_importer_mesh.cpp
index 07faee3dfc..676f764c11 100644
--- a/modules/gltf/extensions/gltf_document_extension_convert_importer_mesh.cpp
+++ b/modules/gltf/extensions/gltf_document_extension_convert_importer_mesh.cpp
@@ -34,9 +34,6 @@
#include "scene/3d/mesh_instance_3d.h"
#include "scene/resources/3d/importer_mesh.h"
-void GLTFDocumentExtensionConvertImporterMesh::_bind_methods() {
-}
-
void GLTFDocumentExtensionConvertImporterMesh::_copy_meta(Object *p_src_object, Object *p_dst_object) {
List<StringName> meta_list;
p_src_object->get_meta_list(&meta_list);
diff --git a/modules/gltf/extensions/gltf_document_extension_convert_importer_mesh.h b/modules/gltf/extensions/gltf_document_extension_convert_importer_mesh.h
index ca10444eb5..b216a47a7f 100644
--- a/modules/gltf/extensions/gltf_document_extension_convert_importer_mesh.h
+++ b/modules/gltf/extensions/gltf_document_extension_convert_importer_mesh.h
@@ -37,7 +37,6 @@ class GLTFDocumentExtensionConvertImporterMesh : public GLTFDocumentExtension {
GDCLASS(GLTFDocumentExtensionConvertImporterMesh, GLTFDocumentExtension);
protected:
- static void _bind_methods();
static void _copy_meta(Object *p_src_object, Object *p_dst_object);
public:
diff --git a/modules/gltf/extensions/gltf_light.cpp b/modules/gltf/extensions/gltf_light.cpp
index c1d2fea98b..f6e91c1635 100644
--- a/modules/gltf/extensions/gltf_light.cpp
+++ b/modules/gltf/extensions/gltf_light.cpp
@@ -170,7 +170,7 @@ Light3D *GLTFLight::to_node() const {
}
Ref<GLTFLight> GLTFLight::from_dictionary(const Dictionary p_dictionary) {
- ERR_FAIL_COND_V_MSG(!p_dictionary.has("type"), Ref<GLTFLight>(), "Failed to parse GLTF light, missing required field 'type'.");
+ ERR_FAIL_COND_V_MSG(!p_dictionary.has("type"), Ref<GLTFLight>(), "Failed to parse glTF light, missing required field 'type'.");
Ref<GLTFLight> light;
light.instantiate();
const String &type = p_dictionary["type"];
@@ -181,7 +181,7 @@ Ref<GLTFLight> GLTFLight::from_dictionary(const Dictionary p_dictionary) {
if (arr.size() == 3) {
light->color = Color(arr[0], arr[1], arr[2]).linear_to_srgb();
} else {
- ERR_PRINT("Error parsing GLTF light: The color must have exactly 3 numbers.");
+ ERR_PRINT("Error parsing glTF light: The color must have exactly 3 numbers.");
}
}
if (p_dictionary.has("intensity")) {
@@ -195,10 +195,10 @@ Ref<GLTFLight> GLTFLight::from_dictionary(const Dictionary p_dictionary) {
light->inner_cone_angle = spot["innerConeAngle"];
light->outer_cone_angle = spot["outerConeAngle"];
if (light->inner_cone_angle >= light->outer_cone_angle) {
- ERR_PRINT("Error parsing GLTF light: The inner angle must be smaller than the outer angle.");
+ ERR_PRINT("Error parsing glTF light: The inner angle must be smaller than the outer angle.");
}
} else if (type != "point" && type != "directional") {
- ERR_PRINT("Error parsing GLTF light: Light type '" + type + "' is unknown.");
+ ERR_PRINT("Error parsing glTF light: Light type '" + type + "' is unknown.");
}
return light;
}
diff --git a/modules/gltf/extensions/physics/gltf_document_extension_physics.cpp b/modules/gltf/extensions/physics/gltf_document_extension_physics.cpp
index 7e52cde059..5c26a1686b 100644
--- a/modules/gltf/extensions/physics/gltf_document_extension_physics.cpp
+++ b/modules/gltf/extensions/physics/gltf_document_extension_physics.cpp
@@ -88,7 +88,7 @@ Error GLTFDocumentExtensionPhysics::parse_node_extensions(Ref<GLTFState> p_state
// "collider" is the index of the collider in the state colliders array.
int node_collider_index = node_collider_ext["collider"];
Array state_colliders = p_state->get_additional_data(StringName("GLTFPhysicsShapes"));
- ERR_FAIL_INDEX_V_MSG(node_collider_index, state_colliders.size(), Error::ERR_FILE_CORRUPT, "GLTF Physics: On node " + p_gltf_node->get_name() + ", the collider index " + itos(node_collider_index) + " is not in the state colliders (size: " + itos(state_colliders.size()) + ").");
+ ERR_FAIL_INDEX_V_MSG(node_collider_index, state_colliders.size(), Error::ERR_FILE_CORRUPT, "glTF Physics: On node " + p_gltf_node->get_name() + ", the collider index " + itos(node_collider_index) + " is not in the state colliders (size: " + itos(state_colliders.size()) + ").");
p_gltf_node->set_additional_data(StringName("GLTFPhysicsShape"), state_colliders[node_collider_index]);
} else {
p_gltf_node->set_additional_data(StringName("GLTFPhysicsShape"), GLTFPhysicsShape::from_dictionary(node_collider_ext));
@@ -103,7 +103,7 @@ Error GLTFDocumentExtensionPhysics::parse_node_extensions(Ref<GLTFState> p_state
int node_shape_index = node_collider.get("shape", -1);
if (node_shape_index != -1) {
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()) + ").");
+ 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]);
} else {
// If this node is a collider but does not have a collider
@@ -117,7 +117,7 @@ Error GLTFDocumentExtensionPhysics::parse_node_extensions(Ref<GLTFState> p_state
int node_shape_index = node_trigger.get("shape", -1);
if (node_shape_index != -1) {
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()) + ").");
+ 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]);
} else {
// If this node is a trigger but does not have a trigger shape,
@@ -150,7 +150,7 @@ void _setup_shape_mesh_resource_from_index_if_needed(Ref<GLTFState> p_state, Ref
return; // The mesh resource is already set up.
}
TypedArray<GLTFMesh> state_meshes = p_state->get_meshes();
- ERR_FAIL_INDEX_MSG(shape_mesh_index, state_meshes.size(), "GLTF Physics: When importing '" + p_state->get_scene_name() + "', the shape mesh index " + itos(shape_mesh_index) + " is not in the state meshes (size: " + itos(state_meshes.size()) + ").");
+ ERR_FAIL_INDEX_MSG(shape_mesh_index, state_meshes.size(), "glTF Physics: When importing '" + p_state->get_scene_name() + "', the shape mesh index " + itos(shape_mesh_index) + " is not in the state meshes (size: " + itos(state_meshes.size()) + ").");
Ref<GLTFMesh> gltf_mesh = state_meshes[shape_mesh_index];
ERR_FAIL_COND(gltf_mesh.is_null());
importer_mesh = gltf_mesh->get_mesh();
@@ -164,12 +164,12 @@ CollisionObject3D *_generate_shape_with_body(Ref<GLTFState> p_state, Ref<GLTFNod
bool is_trigger = p_physics_shape->get_is_trigger();
// This method is used for the case where we must generate a parent body.
// This is can happen for multiple reasons. One possibility is that this
- // GLTF file is using OMI_collider but not OMI_physics_body, or at least
+ // glTF file is using OMI_collider but not OMI_physics_body, or at least
// this particular node is not using it. Another possibility is that the
- // physics body information is set up on the same GLTF node, not a parent.
+ // physics body information is set up on the same glTF node, not a parent.
CollisionObject3D *body;
if (p_physics_body.is_valid()) {
- // This code is run when the physics body is on the same GLTF node.
+ // This code is run when the physics body is on the same glTF node.
body = p_physics_body->to_node();
if (is_trigger && (p_physics_body->get_body_type() != "trigger")) {
// Edge case: If the body's trigger and the collider's trigger
@@ -266,7 +266,7 @@ Node3D *GLTFDocumentExtensionPhysics::generate_scene_node(Ref<GLTFState> p_state
Ref<GLTFPhysicsShape> gltf_physics_shape = p_gltf_node->get_additional_data(StringName("GLTFPhysicsShape"));
if (gltf_physics_shape.is_valid()) {
_setup_shape_mesh_resource_from_index_if_needed(p_state, gltf_physics_shape);
- // If this GLTF node specifies both a shape and a body, generate both.
+ // If this glTF node specifies both a shape and a body, generate both.
if (gltf_physics_body.is_valid()) {
return _generate_shape_with_body(p_state, p_gltf_node, gltf_physics_shape, gltf_physics_body);
}
@@ -309,7 +309,7 @@ Node3D *GLTFDocumentExtensionPhysics::generate_scene_node(Ref<GLTFState> p_state
}
} else if (!Object::cast_to<PhysicsBody3D>(ancestor_col_obj)) {
if (p_gltf_node->get_additional_data(StringName("GLTFPhysicsCompoundCollider"))) {
- // If the GLTF file wants this node to group solid shapes together,
+ // If the glTF file wants this node to group solid shapes together,
// and there is no parent body, we need to create a static body.
ancestor_col_obj = memnew(StaticBody3D);
ret = ancestor_col_obj;
@@ -386,7 +386,7 @@ void GLTFDocumentExtensionPhysics::convert_scene_node(Ref<GLTFState> p_state, Re
if (cast_to<CollisionShape3D>(p_scene_node)) {
CollisionShape3D *godot_shape = Object::cast_to<CollisionShape3D>(p_scene_node);
Ref<GLTFPhysicsShape> gltf_shape = GLTFPhysicsShape::from_node(godot_shape);
- ERR_FAIL_COND_MSG(gltf_shape.is_null(), "GLTF Physics: Could not convert CollisionShape3D to GLTFPhysicsShape. Does it have a valid Shape3D?");
+ ERR_FAIL_COND_MSG(gltf_shape.is_null(), "glTF Physics: Could not convert CollisionShape3D to GLTFPhysicsShape. Does it have a valid Shape3D?");
{
Ref<ImporterMesh> importer_mesh = gltf_shape->get_importer_mesh();
if (importer_mesh.is_valid()) {
diff --git a/modules/gltf/extensions/physics/gltf_physics_body.cpp b/modules/gltf/extensions/physics/gltf_physics_body.cpp
index 7929b46542..c11aa5d2ff 100644
--- a/modules/gltf/extensions/physics/gltf_physics_body.cpp
+++ b/modules/gltf/extensions/physics/gltf_physics_body.cpp
@@ -108,7 +108,7 @@ void GLTFPhysicsBody::set_body_type(String p_body_type) {
} else if (p_body_type == "trigger") {
body_type = PhysicsBodyType::TRIGGER;
} else {
- ERR_PRINT("Error setting GLTF physics body type: The body type must be one of \"static\", \"animatable\", \"character\", \"rigid\", \"vehicle\", or \"trigger\".");
+ ERR_PRINT("Error setting glTF physics body type: The body type must be one of \"static\", \"animatable\", \"character\", \"rigid\", \"vehicle\", or \"trigger\".");
}
}
@@ -194,7 +194,7 @@ Ref<GLTFPhysicsBody> GLTFPhysicsBody::from_node(const CollisionObject3D *p_body_
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.");
+ 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;
@@ -289,7 +289,7 @@ Ref<GLTFPhysicsBody> GLTFPhysicsBody::from_dictionary(const Dictionary p_diction
physics_body->body_type = PhysicsBodyType::TRIGGER;
#endif // DISABLE_DEPRECATED
} else {
- ERR_PRINT("Error parsing GLTF physics body: The body type in the GLTF file \"" + body_type_string + "\" was not recognized.");
+ ERR_PRINT("Error parsing glTF physics body: The body type in the glTF file \"" + body_type_string + "\" was not recognized.");
}
}
if (motion.has("mass")) {
@@ -300,7 +300,7 @@ Ref<GLTFPhysicsBody> GLTFPhysicsBody::from_dictionary(const Dictionary p_diction
if (arr.size() == 3) {
physics_body->set_linear_velocity(Vector3(arr[0], arr[1], arr[2]));
} else {
- ERR_PRINT("Error parsing GLTF physics body: The linear velocity vector must have exactly 3 numbers.");
+ ERR_PRINT("Error parsing glTF physics body: The linear velocity vector must have exactly 3 numbers.");
}
}
if (motion.has("angularVelocity")) {
@@ -308,7 +308,7 @@ Ref<GLTFPhysicsBody> GLTFPhysicsBody::from_dictionary(const Dictionary p_diction
if (arr.size() == 3) {
physics_body->set_angular_velocity(Vector3(arr[0], arr[1], arr[2]));
} else {
- ERR_PRINT("Error parsing GLTF physics body: The angular velocity vector must have exactly 3 numbers.");
+ ERR_PRINT("Error parsing glTF physics body: The angular velocity vector must have exactly 3 numbers.");
}
}
if (motion.has("centerOfMass")) {
@@ -316,7 +316,7 @@ Ref<GLTFPhysicsBody> GLTFPhysicsBody::from_dictionary(const Dictionary p_diction
if (arr.size() == 3) {
physics_body->set_center_of_mass(Vector3(arr[0], arr[1], arr[2]));
} else {
- ERR_PRINT("Error parsing GLTF physics body: The center of mass vector must have exactly 3 numbers.");
+ ERR_PRINT("Error parsing glTF physics body: The center of mass vector must have exactly 3 numbers.");
}
}
if (motion.has("inertiaDiagonal")) {
@@ -324,7 +324,7 @@ Ref<GLTFPhysicsBody> GLTFPhysicsBody::from_dictionary(const Dictionary p_diction
if (arr.size() == 3) {
physics_body->set_inertia_diagonal(Vector3(arr[0], arr[1], arr[2]));
} else {
- ERR_PRINT("Error parsing GLTF physics body: The inertia diagonal vector must have exactly 3 numbers.");
+ ERR_PRINT("Error parsing glTF physics body: The inertia diagonal vector must have exactly 3 numbers.");
}
}
if (motion.has("inertiaOrientation")) {
@@ -332,7 +332,7 @@ Ref<GLTFPhysicsBody> GLTFPhysicsBody::from_dictionary(const Dictionary p_diction
if (arr.size() == 4) {
physics_body->set_inertia_orientation(Quaternion(arr[0], arr[1], arr[2], arr[3]));
} else {
- ERR_PRINT("Error parsing GLTF physics body: The inertia orientation quaternion must have exactly 4 numbers.");
+ ERR_PRINT("Error parsing glTF physics body: The inertia orientation quaternion must have exactly 4 numbers.");
}
}
return physics_body;
diff --git a/modules/gltf/extensions/physics/gltf_physics_shape.cpp b/modules/gltf/extensions/physics/gltf_physics_shape.cpp
index 6897bdbd3a..0340eb11b5 100644
--- a/modules/gltf/extensions/physics/gltf_physics_shape.cpp
+++ b/modules/gltf/extensions/physics/gltf_physics_shape.cpp
@@ -134,7 +134,7 @@ void GLTFPhysicsShape::set_importer_mesh(Ref<ImporterMesh> p_importer_mesh) {
Ref<ImporterMesh> _convert_hull_points_to_mesh(const Vector<Vector3> &p_hull_points) {
Ref<ImporterMesh> importer_mesh;
- ERR_FAIL_COND_V_MSG(p_hull_points.size() < 3, importer_mesh, "GLTFPhysicsShape: Convex hull has fewer points (" + itos(p_hull_points.size()) + ") than the minimum of 3. At least 3 points are required in order to save to GLTF, since it uses a mesh to represent convex hulls.");
+ ERR_FAIL_COND_V_MSG(p_hull_points.size() < 3, importer_mesh, "GLTFPhysicsShape: Convex hull has fewer points (" + itos(p_hull_points.size()) + ") than the minimum of 3. At least 3 points are required in order to save to glTF, since it uses a mesh to represent convex hulls.");
if (p_hull_points.size() > 255) {
WARN_PRINT("GLTFPhysicsShape: Convex hull has more points (" + itos(p_hull_points.size()) + ") than the recommended maximum of 255. This may not load correctly in other engines.");
}
diff --git a/modules/gltf/gltf_document.cpp b/modules/gltf/gltf_document.cpp
index dff1e62e82..cd25b93e6c 100644
--- a/modules/gltf/gltf_document.cpp
+++ b/modules/gltf/gltf_document.cpp
@@ -620,7 +620,7 @@ Error GLTFDocument::_parse_nodes(Ref<GLTFState> p_state) {
for (Ref<GLTFDocumentExtension> ext : document_extensions) {
ERR_CONTINUE(ext.is_null());
Error err = ext->parse_node_extensions(p_state, node, extensions);
- ERR_CONTINUE_MSG(err != OK, "GLTF: Encountered error " + itos(err) + " when parsing node extensions for node " + node->get_name() + " in file " + p_state->filename + ". Continuing.");
+ ERR_CONTINUE_MSG(err != OK, "glTF: Encountered error " + itos(err) + " when parsing node extensions for node " + node->get_name() + " in file " + p_state->filename + ". Continuing.");
}
}
@@ -3353,7 +3353,7 @@ Error GLTFDocument::_serialize_images(Ref<GLTFState> p_state) {
ERR_CONTINUE(image.is_null());
if (image->is_compressed()) {
image->decompress();
- ERR_FAIL_COND_V_MSG(image->is_compressed(), ERR_INVALID_DATA, "GLTF: Image was compressed, but could not be decompressed.");
+ ERR_FAIL_COND_V_MSG(image->is_compressed(), ERR_INVALID_DATA, "glTF: Image was compressed, but could not be decompressed.");
}
if (p_state->filename.to_lower().ends_with("gltf")) {
@@ -3374,7 +3374,7 @@ Error GLTFDocument::_serialize_images(Ref<GLTFState> p_state) {
if (_image_save_extension.is_valid()) {
img_name = img_name + _image_save_extension->get_image_file_extension();
Error err = _image_save_extension->save_image_at_path(p_state, image, full_texture_dir.path_join(img_name), _image_format, _lossy_quality);
- ERR_FAIL_COND_V_MSG(err != OK, err, "GLTF: Failed to save image in '" + _image_format + "' format as a separate file.");
+ ERR_FAIL_COND_V_MSG(err != OK, err, "glTF: Failed to save image in '" + _image_format + "' format as a separate file.");
} else if (_image_format == "PNG") {
img_name = img_name + ".png";
image->save_png(full_texture_dir.path_join(img_name));
@@ -3382,7 +3382,7 @@ Error GLTFDocument::_serialize_images(Ref<GLTFState> p_state) {
img_name = img_name + ".jpg";
image->save_jpg(full_texture_dir.path_join(img_name), _lossy_quality);
} else {
- ERR_FAIL_V_MSG(ERR_UNAVAILABLE, "GLTF: Unknown image format '" + _image_format + "'.");
+ ERR_FAIL_V_MSG(ERR_UNAVAILABLE, "glTF: Unknown image format '" + _image_format + "'.");
}
image_dict["uri"] = relative_texture_dir.path_join(img_name).uri_encode();
} else {
@@ -3412,9 +3412,9 @@ Error GLTFDocument::_serialize_images(Ref<GLTFState> p_state) {
buffer = image->save_jpg_to_buffer(_lossy_quality);
image_dict["mimeType"] = "image/jpeg";
} else {
- ERR_FAIL_V_MSG(ERR_UNAVAILABLE, "GLTF: Unknown image format '" + _image_format + "'.");
+ ERR_FAIL_V_MSG(ERR_UNAVAILABLE, "glTF: Unknown image format '" + _image_format + "'.");
}
- ERR_FAIL_COND_V_MSG(buffer.is_empty(), ERR_INVALID_DATA, "GLTF: Failed to save image in '" + _image_format + "' format.");
+ ERR_FAIL_COND_V_MSG(buffer.is_empty(), ERR_INVALID_DATA, "glTF: Failed to save image in '" + _image_format + "' format.");
bv->byte_length = buffer.size();
p_state->buffers.write[bi].resize(p_state->buffers[bi].size() + bv->byte_length);
@@ -3445,7 +3445,7 @@ Ref<Image> GLTFDocument::_parse_image_bytes_into_image(Ref<GLTFState> p_state, c
for (Ref<GLTFDocumentExtension> ext : document_extensions) {
ERR_CONTINUE(ext.is_null());
Error err = ext->parse_image_data(p_state, p_bytes, p_mime_type, r_image);
- ERR_CONTINUE_MSG(err != OK, "GLTF: Encountered error " + itos(err) + " when parsing image " + itos(p_index) + " in file " + p_state->filename + ". Continuing.");
+ ERR_CONTINUE_MSG(err != OK, "glTF: Encountered error " + itos(err) + " when parsing image " + itos(p_index) + " in file " + p_state->filename + ". Continuing.");
if (!r_image->is_empty()) {
r_file_extension = ext->get_image_file_extension();
return r_image;
@@ -3736,13 +3736,13 @@ Error GLTFDocument::_parse_textures(Ref<GLTFState> p_state) {
for (Ref<GLTFDocumentExtension> ext : document_extensions) {
ERR_CONTINUE(ext.is_null());
Error err = ext->parse_texture_json(p_state, texture_dict, gltf_texture);
- ERR_CONTINUE_MSG(err != OK, "GLTF: Encountered error " + itos(err) + " when parsing texture JSON " + String(Variant(texture_dict)) + " in file " + p_state->filename + ". Continuing.");
+ ERR_CONTINUE_MSG(err != OK, "glTF: Encountered error " + itos(err) + " when parsing texture JSON " + String(Variant(texture_dict)) + " in file " + p_state->filename + ". Continuing.");
if (gltf_texture->get_src_image() != -1) {
break;
}
}
if (gltf_texture->get_src_image() == -1) {
- // No extensions handled it, so use the base GLTF source.
+ // No extensions handled it, so use the base glTF source.
// This may be the fallback, or the only option anyway.
ERR_FAIL_COND_V(!texture_dict.has("source"), ERR_PARSE_ERROR);
gltf_texture->set_src_image(texture_dict["source"]);
@@ -5631,7 +5631,7 @@ void GLTFDocument::_generate_scene_node(Ref<GLTFState> p_state, const GLTFNodeIn
// If none of our GLTFDocumentExtension classes generated us a node, we generate one.
if (!current_node) {
if (gltf_node->skin >= 0 && gltf_node->mesh >= 0 && !gltf_node->children.is_empty()) {
- // GLTF specifies that skinned meshes should ignore their node transforms,
+ // glTF specifies that skinned meshes should ignore their node transforms,
// only being controlled by the skeleton, so Godot will reparent a skinned
// mesh to its skeleton. However, we still need to ensure any child nodes
// keep their place in the tree, so if there are any child nodes, the skinned
@@ -5658,6 +5658,15 @@ void GLTFDocument::_generate_scene_node(Ref<GLTFState> p_state, const GLTFNodeIn
if (p_scene_root == nullptr) {
// If the root node argument is null, this is the root node.
p_scene_root = current_node;
+ // If multiple nodes were generated under the root node, ensure they have the owner set.
+ if (unlikely(current_node->get_child_count() > 0)) {
+ Array args;
+ args.append(p_scene_root);
+ for (int i = 0; i < current_node->get_child_count(); i++) {
+ Node *child = current_node->get_child(i);
+ child->propagate_call(StringName("set_owner"), args);
+ }
+ }
} else {
// Add the node we generated and set the owner to the scene root.
p_scene_parent->add_child(current_node, true);
@@ -7138,9 +7147,9 @@ Node *GLTFDocument::_generate_scene_node_tree(Ref<GLTFState> p_state) {
HashMap<ObjectID, SkinSkeletonIndex> skeleton_map;
Error err = SkinTool::_create_skeletons(p_state->unique_names, p_state->skins, p_state->nodes,
skeleton_map, p_state->skeletons, p_state->scene_nodes);
- ERR_FAIL_COND_V_MSG(err != OK, nullptr, "GLTF: Failed to create skeletons.");
+ ERR_FAIL_COND_V_MSG(err != OK, nullptr, "glTF: Failed to create skeletons.");
err = _create_skins(p_state);
- ERR_FAIL_COND_V_MSG(err != OK, nullptr, "GLTF: Failed to create skins.");
+ ERR_FAIL_COND_V_MSG(err != OK, nullptr, "glTF: Failed to create skins.");
// Generate the node tree.
Node *single_root;
if (p_state->extensions_used.has("GODOT_single_root")) {
@@ -7459,7 +7468,7 @@ Error GLTFDocument::_parse_gltf_extensions(Ref<GLTFState> p_state) {
Error ret = OK;
for (int i = 0; i < p_state->extensions_required.size(); i++) {
if (!supported_extensions.has(p_state->extensions_required[i])) {
- ERR_PRINT("GLTF: Can't import file '" + p_state->filename + "', required extension '" + String(p_state->extensions_required[i]) + "' is not supported. Are you missing a GLTFDocumentExtension plugin?");
+ ERR_PRINT("glTF: Can't import file '" + p_state->filename + "', required extension '" + String(p_state->extensions_required[i]) + "' is not supported. Are you missing a GLTFDocumentExtension plugin?");
ret = ERR_UNAVAILABLE;
}
}
diff --git a/modules/gltf/structures/gltf_camera.cpp b/modules/gltf/structures/gltf_camera.cpp
index d56f67a092..863e1df967 100644
--- a/modules/gltf/structures/gltf_camera.cpp
+++ b/modules/gltf/structures/gltf_camera.cpp
@@ -62,9 +62,9 @@ Ref<GLTFCamera> GLTFCamera::from_node(const Camera3D *p_camera) {
c.instantiate();
ERR_FAIL_NULL_V_MSG(p_camera, c, "Tried to create a GLTFCamera from a Camera3D node, but the given node was null.");
c->set_perspective(p_camera->get_projection() == Camera3D::ProjectionType::PROJECTION_PERSPECTIVE);
- // GLTF spec (yfov) is in radians, Godot's camera (fov) is in degrees.
+ // glTF spec (yfov) is in radians, Godot's camera (fov) is in degrees.
c->set_fov(Math::deg_to_rad(p_camera->get_fov()));
- // GLTF spec (xmag and ymag) is a radius in meters, Godot's camera (size) is a diameter in meters.
+ // glTF spec (xmag and ymag) is a radius in meters, Godot's camera (size) is a diameter in meters.
c->set_size_mag(p_camera->get_size() * 0.5f);
c->set_depth_far(p_camera->get_far());
c->set_depth_near(p_camera->get_near());
@@ -74,9 +74,9 @@ Ref<GLTFCamera> GLTFCamera::from_node(const Camera3D *p_camera) {
Camera3D *GLTFCamera::to_node() const {
Camera3D *camera = memnew(Camera3D);
camera->set_projection(perspective ? Camera3D::PROJECTION_PERSPECTIVE : Camera3D::PROJECTION_ORTHOGONAL);
- // GLTF spec (yfov) is in radians, Godot's camera (fov) is in degrees.
+ // glTF spec (yfov) is in radians, Godot's camera (fov) is in degrees.
camera->set_fov(Math::rad_to_deg(fov));
- // GLTF spec (xmag and ymag) is a radius in meters, Godot's camera (size) is a diameter in meters.
+ // glTF spec (xmag and ymag) is a radius in meters, Godot's camera (size) is a diameter in meters.
camera->set_size(size_mag * 2.0f);
camera->set_near(depth_near);
camera->set_far(depth_far);
@@ -84,7 +84,7 @@ Camera3D *GLTFCamera::to_node() const {
}
Ref<GLTFCamera> GLTFCamera::from_dictionary(const Dictionary p_dictionary) {
- ERR_FAIL_COND_V_MSG(!p_dictionary.has("type"), Ref<GLTFCamera>(), "Failed to parse GLTF camera, missing required field 'type'.");
+ ERR_FAIL_COND_V_MSG(!p_dictionary.has("type"), Ref<GLTFCamera>(), "Failed to parse glTF camera, missing required field 'type'.");
Ref<GLTFCamera> camera;
camera.instantiate();
const String &type = p_dictionary["type"];
@@ -107,7 +107,7 @@ Ref<GLTFCamera> GLTFCamera::from_dictionary(const Dictionary p_dictionary) {
camera->set_depth_near(ortho["znear"]);
}
} else {
- ERR_PRINT("Error parsing GLTF camera: Camera type '" + type + "' is unknown, should be perspective or orthographic.");
+ ERR_PRINT("Error parsing glTF camera: Camera type '" + type + "' is unknown, should be perspective or orthographic.");
}
return camera;
}
diff --git a/modules/gltf/structures/gltf_camera.h b/modules/gltf/structures/gltf_camera.h
index ef55b06a76..1a583c82cc 100644
--- a/modules/gltf/structures/gltf_camera.h
+++ b/modules/gltf/structures/gltf_camera.h
@@ -42,8 +42,8 @@ class GLTFCamera : public Resource {
GDCLASS(GLTFCamera, Resource);
private:
- // GLTF has no default camera values, they should always be specified in
- // the GLTF file. Here we default to Godot's default camera settings.
+ // glTF has no default camera values, they should always be specified in
+ // the glTF file. Here we default to Godot's default camera settings.
bool perspective = true;
real_t fov = Math::deg_to_rad(75.0);
real_t size_mag = 0.5;
diff --git a/modules/gridmap/editor/grid_map_editor_plugin.cpp b/modules/gridmap/editor/grid_map_editor_plugin.cpp
index f402e2a583..ea63e07104 100644
--- a/modules/gridmap/editor/grid_map_editor_plugin.cpp
+++ b/modules/gridmap/editor/grid_map_editor_plugin.cpp
@@ -643,6 +643,7 @@ EditorPlugin::AfterGUIInput GridMapEditor::forward_spatial_input_event(Camera3D
_do_paste();
input_action = INPUT_NONE;
_update_paste_indicator();
+ return EditorPlugin::AFTER_GUI_INPUT_STOP;
} else if (mb->is_shift_pressed() && can_edit) {
input_action = INPUT_SELECT;
last_selection = selection;
diff --git a/modules/mono/csharp_script.cpp b/modules/mono/csharp_script.cpp
index 36c8a40ed9..6d561c1566 100644
--- a/modules/mono/csharp_script.cpp
+++ b/modules/mono/csharp_script.cpp
@@ -148,7 +148,7 @@ void CSharpLanguage::finalize() {
finalizing = true;
- // Make sure all script binding gchandles are released before finalizing GDMono
+ // Make sure all script binding gchandles are released before finalizing GDMono.
for (KeyValue<Object *, CSharpScriptBinding> &E : script_bindings) {
CSharpScriptBinding &script_binding = E.value;
@@ -156,6 +156,10 @@ void CSharpLanguage::finalize() {
script_binding.gchandle.release();
script_binding.inited = false;
}
+
+ // Make sure we clear all the instance binding callbacks so they don't get called
+ // after finalizing the C# language.
+ script_binding.owner->free_instance_binding(this);
}
if (gdmono) {
@@ -1227,6 +1231,11 @@ void CSharpLanguage::_instance_binding_free_callback(void *, void *, void *p_bin
}
GDExtensionBool CSharpLanguage::_instance_binding_reference_callback(void *p_token, void *p_binding, GDExtensionBool p_reference) {
+ // Instance bindings callbacks can only be called if the C# language is available.
+ // Failing this assert usually means that we didn't clear the instance binding in some Object
+ // and the C# language has already been finalized.
+ DEV_ASSERT(CSharpLanguage::get_singleton() != nullptr);
+
CRASH_COND(!p_binding);
CSharpScriptBinding &script_binding = ((RBMap<Object *, CSharpScriptBinding>::Element *)p_binding)->get();
@@ -1662,7 +1671,7 @@ bool CSharpInstance::_reference_owner_unsafe() {
// but the managed instance is alive, the refcount will be 1 instead of 0.
// See: _unreference_owner_unsafe()
- // May not me referenced yet, so we must use init_ref() instead of reference()
+ // May not be referenced yet, so we must use init_ref() instead of reference()
if (static_cast<RefCounted *>(owner)->init_ref()) {
CSharpLanguage::get_singleton()->post_unsafe_reference(owner);
unsafe_referenced = true;
@@ -2351,8 +2360,8 @@ CSharpInstance *CSharpScript::_create_instance(const Variant **p_args, int p_arg
if (!ok) {
// Important to clear this before destroying the script instance here
instance->script = Ref<CSharpScript>();
- instance->owner = nullptr;
p_owner->set_script_instance(nullptr);
+ instance->owner = nullptr;
return nullptr;
}
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/GeneratedSources/ExportDiagnostics_GD0107_OK_ScriptPropertyDefVal.generated.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/GeneratedSources/ExportDiagnostics_GD0107_OK_ScriptPropertyDefVal.generated.cs
index 217f467637..9a8b3ea846 100644
--- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/GeneratedSources/ExportDiagnostics_GD0107_OK_ScriptPropertyDefVal.generated.cs
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/GeneratedSources/ExportDiagnostics_GD0107_OK_ScriptPropertyDefVal.generated.cs
@@ -11,11 +11,27 @@ partial class ExportDiagnostics_GD0107_OK
[global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
internal new static global::System.Collections.Generic.Dictionary<global::Godot.StringName, global::Godot.Variant> GetGodotPropertyDefaultValues()
{
- var values = new global::System.Collections.Generic.Dictionary<global::Godot.StringName, global::Godot.Variant>(2);
+ var values = new global::System.Collections.Generic.Dictionary<global::Godot.StringName, global::Godot.Variant>(10);
global::Godot.Node __NodeProperty_default_value = default;
values.Add(PropertyName.@NodeProperty, global::Godot.Variant.From<global::Godot.Node>(__NodeProperty_default_value));
+ global::Godot.Node[] __SystemArrayOfNodesProperty_default_value = default;
+ values.Add(PropertyName.@SystemArrayOfNodesProperty, global::Godot.Variant.CreateFrom(__SystemArrayOfNodesProperty_default_value));
+ global::Godot.Collections.Array<global::Godot.Node> __GodotArrayOfNodesProperty_default_value = default;
+ values.Add(PropertyName.@GodotArrayOfNodesProperty, global::Godot.Variant.CreateFrom(__GodotArrayOfNodesProperty_default_value));
+ global::Godot.Collections.Dictionary<global::Godot.Node, string> __GodotDictionaryWithNodeAsKeyProperty_default_value = default;
+ values.Add(PropertyName.@GodotDictionaryWithNodeAsKeyProperty, global::Godot.Variant.CreateFrom(__GodotDictionaryWithNodeAsKeyProperty_default_value));
+ global::Godot.Collections.Dictionary<string, global::Godot.Node> __GodotDictionaryWithNodeAsValueProperty_default_value = default;
+ values.Add(PropertyName.@GodotDictionaryWithNodeAsValueProperty, global::Godot.Variant.CreateFrom(__GodotDictionaryWithNodeAsValueProperty_default_value));
global::Godot.Node __NodeField_default_value = default;
values.Add(PropertyName.@NodeField, global::Godot.Variant.From<global::Godot.Node>(__NodeField_default_value));
+ global::Godot.Node[] __SystemArrayOfNodesField_default_value = default;
+ values.Add(PropertyName.@SystemArrayOfNodesField, global::Godot.Variant.CreateFrom(__SystemArrayOfNodesField_default_value));
+ global::Godot.Collections.Array<global::Godot.Node> __GodotArrayOfNodesField_default_value = default;
+ values.Add(PropertyName.@GodotArrayOfNodesField, global::Godot.Variant.CreateFrom(__GodotArrayOfNodesField_default_value));
+ global::Godot.Collections.Dictionary<global::Godot.Node, string> __GodotDictionaryWithNodeAsKeyField_default_value = default;
+ values.Add(PropertyName.@GodotDictionaryWithNodeAsKeyField, global::Godot.Variant.CreateFrom(__GodotDictionaryWithNodeAsKeyField_default_value));
+ global::Godot.Collections.Dictionary<string, global::Godot.Node> __GodotDictionaryWithNodeAsValueField_default_value = default;
+ values.Add(PropertyName.@GodotDictionaryWithNodeAsValueField, global::Godot.Variant.CreateFrom(__GodotDictionaryWithNodeAsValueField_default_value));
return values;
}
#endif // TOOLS
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/Sources/ExportDiagnostics_GD0107.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/Sources/ExportDiagnostics_GD0107.cs
index 067783ea66..4613d883c2 100644
--- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/Sources/ExportDiagnostics_GD0107.cs
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Tests/TestData/Sources/ExportDiagnostics_GD0107.cs
@@ -1,4 +1,5 @@
using Godot;
+using Godot.Collections;
public partial class ExportDiagnostics_GD0107_OK : Node
{
@@ -6,7 +7,31 @@ public partial class ExportDiagnostics_GD0107_OK : Node
public Node NodeField;
[Export]
+ public Node[] SystemArrayOfNodesField;
+
+ [Export]
+ public Array<Node> GodotArrayOfNodesField;
+
+ [Export]
+ public Dictionary<Node, string> GodotDictionaryWithNodeAsKeyField;
+
+ [Export]
+ public Dictionary<string, Node> GodotDictionaryWithNodeAsValueField;
+
+ [Export]
public Node NodeProperty { get; set; }
+
+ [Export]
+ public Node[] SystemArrayOfNodesProperty { get; set; }
+
+ [Export]
+ public Array<Node> GodotArrayOfNodesProperty { get; set; }
+
+ [Export]
+ public Dictionary<Node, string> GodotDictionaryWithNodeAsKeyProperty { get; set; }
+
+ [Export]
+ public Dictionary<string, Node> GodotDictionaryWithNodeAsValueProperty { get; set; }
}
public partial class ExportDiagnostics_GD0107_KO : Resource
@@ -15,5 +40,29 @@ public partial class ExportDiagnostics_GD0107_KO : Resource
public Node {|GD0107:NodeField|};
[Export]
+ public Node[] {|GD0107:SystemArrayOfNodesField|};
+
+ [Export]
+ public Array<Node> {|GD0107:GodotArrayOfNodesField|};
+
+ [Export]
+ public Dictionary<Node, string> {|GD0107:GodotDictionaryWithNodeAsKeyField|};
+
+ [Export]
+ public Dictionary<string, Node> {|GD0107:GodotDictionaryWithNodeAsValueField|};
+
+ [Export]
public Node {|GD0107:NodeProperty|} { get; set; }
+
+ [Export]
+ public Node[] {|GD0107:SystemArrayOfNodesProperty|} { get; set; }
+
+ [Export]
+ public Array<Node> {|GD0107:GodotArrayOfNodesProperty|} { get; set; }
+
+ [Export]
+ public Dictionary<Node, string> {|GD0107:GodotDictionaryWithNodeAsKeyProperty|} { get; set; }
+
+ [Export]
+ public Dictionary<string, Node> {|GD0107:GodotDictionaryWithNodeAsValueProperty|} { get; set; }
}
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertyDefValGenerator.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertyDefValGenerator.cs
index efe88d8468..626f51ecae 100644
--- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertyDefValGenerator.cs
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertyDefValGenerator.cs
@@ -196,16 +196,13 @@ namespace Godot.SourceGenerators
continue;
}
- if (marshalType == MarshalType.GodotObjectOrDerived)
+ if (!isNode && MemberHasNodeType(propertyType, marshalType.Value))
{
- if (!isNode && propertyType.InheritsFrom("GodotSharp", GodotClasses.Node))
- {
- context.ReportDiagnostic(Diagnostic.Create(
- Common.OnlyNodesShouldExportNodesRule,
- property.Locations.FirstLocationWithSourceTreeOrDefault()
- ));
- continue;
- }
+ context.ReportDiagnostic(Diagnostic.Create(
+ Common.OnlyNodesShouldExportNodesRule,
+ property.Locations.FirstLocationWithSourceTreeOrDefault()
+ ));
+ continue;
}
var propertyDeclarationSyntax = property.DeclaringSyntaxReferences
@@ -315,16 +312,13 @@ namespace Godot.SourceGenerators
continue;
}
- if (marshalType == MarshalType.GodotObjectOrDerived)
+ if (!isNode && MemberHasNodeType(fieldType, marshalType.Value))
{
- if (!isNode && fieldType.InheritsFrom("GodotSharp", GodotClasses.Node))
- {
- context.ReportDiagnostic(Diagnostic.Create(
- Common.OnlyNodesShouldExportNodesRule,
- field.Locations.FirstLocationWithSourceTreeOrDefault()
- ));
- continue;
- }
+ context.ReportDiagnostic(Diagnostic.Create(
+ Common.OnlyNodesShouldExportNodesRule,
+ field.Locations.FirstLocationWithSourceTreeOrDefault()
+ ));
+ continue;
}
EqualsValueClauseSyntax? initializer = field.DeclaringSyntaxReferences
@@ -424,6 +418,27 @@ namespace Godot.SourceGenerators
context.AddSource(uniqueHint, SourceText.From(source.ToString(), Encoding.UTF8));
}
+ private static bool MemberHasNodeType(ITypeSymbol memberType, MarshalType marshalType)
+ {
+ if (marshalType == MarshalType.GodotObjectOrDerived)
+ {
+ return memberType.InheritsFrom("GodotSharp", GodotClasses.Node);
+ }
+ if (marshalType == MarshalType.GodotObjectOrDerivedArray)
+ {
+ var elementType = ((IArrayTypeSymbol)memberType).ElementType;
+ return elementType.InheritsFrom("GodotSharp", GodotClasses.Node);
+ }
+ if (memberType is INamedTypeSymbol { IsGenericType: true } genericType)
+ {
+ return genericType.TypeArguments
+ .Any(static typeArgument
+ => typeArgument.InheritsFrom("GodotSharp", GodotClasses.Node));
+ }
+
+ return false;
+ }
+
private struct ExportedPropertyMetadata
{
public ExportedPropertyMetadata(string name, MarshalType type, ITypeSymbol typeSymbol, string? value)
diff --git a/modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs b/modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs
index d3720dcb72..ede0600ac1 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs
@@ -355,24 +355,23 @@ namespace GodotTools.Export
if (outputPaths.Count > 2)
{
// lipo the simulator binaries together
- // TODO: Move this to the native lipo implementation we have in the macos export plugin.
- var lipoArgs = new List<string>();
- lipoArgs.Add("-create");
- lipoArgs.AddRange(outputPaths.Skip(1).Select(x => Path.Combine(x, $"{GodotSharpDirs.ProjectAssemblyName}.dylib")));
- lipoArgs.Add("-output");
- lipoArgs.Add(Path.Combine(outputPaths[1], $"{GodotSharpDirs.ProjectAssemblyName}.dylib"));
- int lipoExitCode = OS.ExecuteCommand(XcodeHelper.FindXcodeTool("lipo"), lipoArgs);
- if (lipoExitCode != 0)
- throw new InvalidOperationException($"Command 'lipo' exited with code: {lipoExitCode}.");
+ string outputPath = Path.Combine(outputPaths[1], $"{GodotSharpDirs.ProjectAssemblyName}.dylib");
+ string[] files = outputPaths
+ .Skip(1)
+ .Select(path => Path.Combine(path, $"{GodotSharpDirs.ProjectAssemblyName}.dylib"))
+ .ToArray();
+
+ if (!Internal.LipOCreateFile(outputPath, files))
+ {
+ throw new InvalidOperationException($"Failed to 'lipo' simulator binaries.");
+ }
outputPaths.RemoveRange(2, outputPaths.Count - 2);
}
- var xcFrameworkPath = Path.Combine(GodotSharpDirs.ProjectBaseOutputPath, publishConfig.BuildConfig,
- $"{GodotSharpDirs.ProjectAssemblyName}_aot.xcframework");
- if (!BuildManager.GenerateXCFrameworkBlocking(outputPaths,
- Path.Combine(GodotSharpDirs.ProjectBaseOutputPath, publishConfig.BuildConfig, xcFrameworkPath)))
+ string xcFrameworkPath = Path.Combine(GodotSharpDirs.ProjectBaseOutputPath, publishConfig.BuildConfig, $"{GodotSharpDirs.ProjectAssemblyName}_aot.xcframework");
+ if (!BuildManager.GenerateXCFrameworkBlocking(outputPaths, xcFrameworkPath))
{
throw new InvalidOperationException("Failed to generate xcframework.");
}
diff --git a/modules/mono/editor/GodotTools/GodotTools/Export/XcodeHelper.cs b/modules/mono/editor/GodotTools/GodotTools/Export/XcodeHelper.cs
deleted file mode 100644
index 023f46b685..0000000000
--- a/modules/mono/editor/GodotTools/GodotTools/Export/XcodeHelper.cs
+++ /dev/null
@@ -1,93 +0,0 @@
-using System;
-using System.IO;
-
-namespace GodotTools.Export
-{
- public static class XcodeHelper
- {
- private static string? _XcodePath = null;
-
- public static string XcodePath
- {
- get
- {
- if (_XcodePath == null)
- {
- _XcodePath = FindXcode();
-
- if (_XcodePath == null)
- throw new FileNotFoundException("Could not find Xcode.");
- }
-
- return _XcodePath;
- }
- }
-
- private static string? FindSelectedXcode()
- {
- var outputWrapper = new Godot.Collections.Array();
-
- int exitCode = Godot.OS.Execute("xcode-select", new string[] { "--print-path" }, output: outputWrapper);
-
- if (exitCode == 0)
- {
- string output = (string)outputWrapper[0];
- return output.Trim();
- }
-
- Console.Error.WriteLine($"'xcode-select --print-path' exited with code: {exitCode}");
-
- return null;
- }
-
- public static string? FindXcode()
- {
- string? selectedXcode = FindSelectedXcode();
- if (selectedXcode != null)
- {
- if (Directory.Exists(Path.Combine(selectedXcode, "Contents", "Developer")))
- return selectedXcode;
-
- // The path already pointed to Contents/Developer
- var dirInfo = new DirectoryInfo(selectedXcode);
- if (dirInfo is not { Parent.Name: "Contents", Name: "Developer" })
- {
- Console.WriteLine(Path.GetDirectoryName(selectedXcode));
- Console.WriteLine(System.IO.Directory.GetParent(selectedXcode)?.Name);
- Console.Error.WriteLine("Unrecognized path for selected Xcode");
- }
- else
- {
- return System.IO.Path.GetFullPath($"{selectedXcode}/../..");
- }
- }
- else
- {
- Console.Error.WriteLine("Could not find the selected Xcode; trying with a hint path");
- }
-
- const string XcodeHintPath = "/Applications/Xcode.app";
-
- if (Directory.Exists(XcodeHintPath))
- {
- if (Directory.Exists(Path.Combine(XcodeHintPath, "Contents", "Developer")))
- return XcodeHintPath;
-
- Console.Error.WriteLine($"Found Xcode at '{XcodeHintPath}' but it's missing the 'Contents/Developer' sub-directory");
- }
-
- return null;
- }
-
- public static string FindXcodeTool(string toolName)
- {
- string XcodeDefaultToolchain = Path.Combine(XcodePath, "Contents", "Developer", "Toolchains", "XcodeDefault.xctoolchain");
-
- string path = Path.Combine(XcodeDefaultToolchain, "usr", "bin", toolName);
- if (File.Exists(path))
- return path;
-
- throw new FileNotFoundException($"Cannot find Xcode tool: {toolName}");
- }
- }
-}
diff --git a/modules/mono/editor/GodotTools/GodotTools/Internals/Internal.cs b/modules/mono/editor/GodotTools/GodotTools/Internals/Internal.cs
index 175bb78051..225ac4073b 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Internals/Internal.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Internals/Internal.cs
@@ -35,6 +35,13 @@ namespace GodotTools.Internals
return godot_icall_Internal_IsMacOSAppBundleInstalled(bundleIdIn);
}
+ public static bool LipOCreateFile(string outputPath, string[] files)
+ {
+ using godot_string outputPathIn = Marshaling.ConvertStringToNative(outputPath);
+ using godot_packed_string_array filesIn = Marshaling.ConvertSystemArrayToNativePackedStringArray(files);
+ return godot_icall_Internal_LipOCreateFile(outputPathIn, filesIn);
+ }
+
public static bool GodotIs32Bits() => godot_icall_Internal_GodotIs32Bits();
public static bool GodotIsRealTDouble() => godot_icall_Internal_GodotIsRealTDouble();
@@ -121,6 +128,8 @@ namespace GodotTools.Internals
private static partial bool godot_icall_Internal_IsMacOSAppBundleInstalled(in godot_string bundleId);
+ private static partial bool godot_icall_Internal_LipOCreateFile(in godot_string outputPath, in godot_packed_string_array files);
+
private static partial bool godot_icall_Internal_GodotIs32Bits();
private static partial bool godot_icall_Internal_GodotIsRealTDouble();
diff --git a/modules/mono/editor/editor_internal_calls.cpp b/modules/mono/editor/editor_internal_calls.cpp
index 03d8b4eab6..7322a47630 100644
--- a/modules/mono/editor/editor_internal_calls.cpp
+++ b/modules/mono/editor/editor_internal_calls.cpp
@@ -44,6 +44,7 @@
#include "editor/editor_node.h"
#include "editor/editor_paths.h"
#include "editor/editor_settings.h"
+#include "editor/export/lipo.h"
#include "editor/gui/editor_run_bar.h"
#include "editor/plugins/script_editor_plugin.h"
#include "editor/themes/editor_scale.h"
@@ -117,6 +118,13 @@ bool godot_icall_Internal_IsMacOSAppBundleInstalled(const godot_string *p_bundle
#endif
}
+bool godot_icall_Internal_LipOCreateFile(const godot_string *p_output_path, const godot_packed_array *p_files) {
+ String output_path = *reinterpret_cast<const String *>(p_output_path);
+ PackedStringArray files = *reinterpret_cast<const PackedStringArray *>(p_files);
+ LipO lip;
+ return lip.create_file(output_path, files);
+}
+
bool godot_icall_Internal_GodotIs32Bits() {
return sizeof(void *) == 4;
}
@@ -258,6 +266,7 @@ static const void *unmanaged_callbacks[]{
(void *)godot_icall_EditorProgress_Step,
(void *)godot_icall_Internal_FullExportTemplatesDir,
(void *)godot_icall_Internal_IsMacOSAppBundleInstalled,
+ (void *)godot_icall_Internal_LipOCreateFile,
(void *)godot_icall_Internal_GodotIs32Bits,
(void *)godot_icall_Internal_GodotIsRealTDouble,
(void *)godot_icall_Internal_GodotMainIteration,
diff --git a/modules/mono/editor/script_templates/VisualShaderNodeCustom/basic.cs b/modules/mono/editor/script_templates/VisualShaderNodeCustom/basic.cs
index cd335934db..ece1ab44a2 100644
--- a/modules/mono/editor/script_templates/VisualShaderNodeCustom/basic.cs
+++ b/modules/mono/editor/script_templates/VisualShaderNodeCustom/basic.cs
@@ -3,6 +3,8 @@
using _BINDINGS_NAMESPACE_;
using System;
+[Tool]
+[GlobalClass]
public partial class VisualShaderNode_CLASS_ : _BASE_
{
public override string _GetName()
@@ -20,37 +22,37 @@ public partial class VisualShaderNode_CLASS_ : _BASE_
return "";
}
- public override long _GetReturnIconType()
+ public override VisualShaderNode.PortType _GetReturnIconType()
{
return 0;
}
- public override long _GetInputPortCount()
+ public override int _GetInputPortCount()
{
return 0;
}
- public override string _GetInputPortName(long port)
+ public override string _GetInputPortName(int port)
{
return "";
}
- public override long _GetInputPortType(long port)
+ public override VisualShaderNode.PortType _GetInputPortType(int port)
{
return 0;
}
- public override long _GetOutputPortCount()
+ public override int _GetOutputPortCount()
{
return 1;
}
- public override string _GetOutputPortName(long port)
+ public override string _GetOutputPortName(int port)
{
return "result";
}
- public override long _GetOutputPortType(long port)
+ public override VisualShaderNode.PortType _GetOutputPortType(int port)
{
return 0;
}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/GodotObjectExtensions.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/GodotObjectExtensions.cs
index 563a6abe9b..1fc6e54e09 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/GodotObjectExtensions.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Extensions/GodotObjectExtensions.cs
@@ -1,4 +1,5 @@
using System;
+using System.Diagnostics.CodeAnalysis;
using Godot.NativeInterop;
#nullable enable
@@ -51,7 +52,7 @@ namespace Godot
/// </summary>
/// <param name="instance">The instance to check.</param>
/// <returns>If the instance is a valid object.</returns>
- public static bool IsInstanceValid(GodotObject? instance)
+ public static bool IsInstanceValid([NotNullWhen(true)] GodotObject? instance)
{
return instance != null && instance.NativeInstance != IntPtr.Zero;
}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs
index 27f2713efa..ef66d5bbe0 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs
@@ -692,10 +692,18 @@ namespace Godot
// Zero length vectors have no angle, so the best we can do is either lerp or throw an error.
return Lerp(to, weight);
}
+ Vector3 axis = Cross(to);
+ real_t axisLengthSquared = axis.LengthSquared();
+ if (axisLengthSquared == 0.0)
+ {
+ // Colinear vectors have no rotation axis or angle between them, so the best we can do is lerp.
+ return Lerp(to, weight);
+ }
+ axis /= Mathf.Sqrt(axisLengthSquared);
real_t startLength = Mathf.Sqrt(startLengthSquared);
real_t resultLength = Mathf.Lerp(startLength, Mathf.Sqrt(endLengthSquared), weight);
real_t angle = AngleTo(to);
- return Rotated(Cross(to).Normalized(), angle * weight) * (resultLength / startLength);
+ return Rotated(axis, angle * weight) * (resultLength / startLength);
}
/// <summary>
diff --git a/modules/multiplayer/doc_classes/MultiplayerSpawner.xml b/modules/multiplayer/doc_classes/MultiplayerSpawner.xml
index 3da245f806..b620292519 100644
--- a/modules/multiplayer/doc_classes/MultiplayerSpawner.xml
+++ b/modules/multiplayer/doc_classes/MultiplayerSpawner.xml
@@ -48,11 +48,11 @@
</methods>
<members>
<member name="spawn_function" type="Callable" setter="set_spawn_function" getter="get_spawn_function">
- Method called on all peers when for every custom [method spawn] requested by the authority. Will receive the [code]data[/code] parameter, and should return a [Node] that is not in the scene tree.
+ Method called on all peers when a custom [method spawn] is requested by the authority. Will receive the [code]data[/code] parameter, and should return a [Node] that is not in the scene tree.
[b]Note:[/b] The returned node should [b]not[/b] be added to the scene with [method Node.add_child]. This is done automatically.
</member>
<member name="spawn_limit" type="int" setter="set_spawn_limit" getter="get_spawn_limit" default="0">
- Maximum nodes that is allowed to be spawned by this spawner. Includes both spawnable scenes and custom spawns.
+ Maximum number of nodes allowed to be spawned by this spawner. Includes both spawnable scenes and custom spawns.
When set to [code]0[/code] (the default), there is no limit.
</member>
<member name="spawn_path" type="NodePath" setter="set_spawn_path" getter="get_spawn_path" default="NodePath(&quot;&quot;)">
diff --git a/modules/navigation/2d/nav_mesh_generator_2d.cpp b/modules/navigation/2d/nav_mesh_generator_2d.cpp
index 8c2fb42463..33b92f6266 100644
--- a/modules/navigation/2d/nav_mesh_generator_2d.cpp
+++ b/modules/navigation/2d/nav_mesh_generator_2d.cpp
@@ -1042,10 +1042,32 @@ void NavMeshGenerator2D::generator_bake_from_source_geometry_data(Ref<Navigation
}
TPPLPartition tpart;
- if (tpart.ConvexPartition_HM(&tppl_in_polygon, &tppl_out_polygon) == 0) { //failed!
- ERR_PRINT("NavigationPolygon Convex partition failed. Unable to create a valid NavigationMesh from defined polygon outline paths.");
- p_navigation_mesh->clear();
- return;
+
+ NavigationPolygon::SamplePartitionType sample_partition_type = p_navigation_mesh->get_sample_partition_type();
+
+ switch (sample_partition_type) {
+ case NavigationPolygon::SamplePartitionType::SAMPLE_PARTITION_CONVEX_PARTITION:
+ if (tpart.ConvexPartition_HM(&tppl_in_polygon, &tppl_out_polygon) == 0) {
+ ERR_PRINT("NavigationPolygon polygon convex partition failed. Unable to create a valid navigation mesh polygon layout from provided source geometry.");
+ p_navigation_mesh->set_vertices(Vector<Vector2>());
+ p_navigation_mesh->clear_polygons();
+ return;
+ }
+ break;
+ case NavigationPolygon::SamplePartitionType::SAMPLE_PARTITION_TRIANGULATE:
+ if (tpart.Triangulate_EC(&tppl_in_polygon, &tppl_out_polygon) == 0) {
+ ERR_PRINT("NavigationPolygon polygon triangulation failed. Unable to create a valid navigation mesh polygon layout from provided source geometry.");
+ p_navigation_mesh->set_vertices(Vector<Vector2>());
+ p_navigation_mesh->clear_polygons();
+ return;
+ }
+ break;
+ default: {
+ ERR_PRINT("NavigationPolygon polygon partitioning failed. Unrecognized partition type.");
+ p_navigation_mesh->set_vertices(Vector<Vector2>());
+ p_navigation_mesh->clear_polygons();
+ return;
+ }
}
Vector<Vector2> new_vertices;
diff --git a/modules/navigation/3d/godot_navigation_server_3d.cpp b/modules/navigation/3d/godot_navigation_server_3d.cpp
index 430d527844..11a5de608b 100644
--- a/modules/navigation/3d/godot_navigation_server_3d.cpp
+++ b/modules/navigation/3d/godot_navigation_server_3d.cpp
@@ -509,22 +509,31 @@ void GodotNavigationServer3D::region_bake_navigation_mesh(Ref<NavigationMesh> p_
int GodotNavigationServer3D::region_get_connections_count(RID p_region) const {
NavRegion *region = region_owner.get_or_null(p_region);
ERR_FAIL_NULL_V(region, 0);
-
- return region->get_connections_count();
+ NavMap *map = region->get_map();
+ if (map) {
+ return map->get_region_connections_count(region);
+ }
+ return 0;
}
Vector3 GodotNavigationServer3D::region_get_connection_pathway_start(RID p_region, int p_connection_id) const {
NavRegion *region = region_owner.get_or_null(p_region);
ERR_FAIL_NULL_V(region, Vector3());
-
- return region->get_connection_pathway_start(p_connection_id);
+ NavMap *map = region->get_map();
+ if (map) {
+ return map->get_region_connection_pathway_start(region, p_connection_id);
+ }
+ return Vector3();
}
Vector3 GodotNavigationServer3D::region_get_connection_pathway_end(RID p_region, int p_connection_id) const {
NavRegion *region = region_owner.get_or_null(p_region);
ERR_FAIL_NULL_V(region, Vector3());
-
- return region->get_connection_pathway_end(p_connection_id);
+ NavMap *map = region->get_map();
+ if (map) {
+ return map->get_region_connection_pathway_end(region, p_connection_id);
+ }
+ return Vector3();
}
Vector3 GodotNavigationServer3D::region_get_random_point(RID p_region, uint32_t p_navigation_layers, bool p_uniformly) const {
@@ -1298,6 +1307,7 @@ void GodotNavigationServer3D::process(real_t p_delta_time) {
int _new_pm_edge_merge_count = 0;
int _new_pm_edge_connection_count = 0;
int _new_pm_edge_free_count = 0;
+ int _new_pm_obstacle_count = 0;
// In c++ we can't be sure that this is performed in the main thread
// even with mutable functions.
@@ -1315,6 +1325,7 @@ void GodotNavigationServer3D::process(real_t p_delta_time) {
_new_pm_edge_merge_count += active_maps[i]->get_pm_edge_merge_count();
_new_pm_edge_connection_count += active_maps[i]->get_pm_edge_connection_count();
_new_pm_edge_free_count += active_maps[i]->get_pm_edge_free_count();
+ _new_pm_obstacle_count += active_maps[i]->get_pm_obstacle_count();
// Emit a signal if a map changed.
const uint32_t new_map_iteration_id = active_maps[i]->get_iteration_id();
@@ -1332,6 +1343,7 @@ void GodotNavigationServer3D::process(real_t p_delta_time) {
pm_edge_merge_count = _new_pm_edge_merge_count;
pm_edge_connection_count = _new_pm_edge_connection_count;
pm_edge_free_count = _new_pm_edge_free_count;
+ pm_obstacle_count = _new_pm_obstacle_count;
}
void GodotNavigationServer3D::init() {
@@ -1566,6 +1578,9 @@ int GodotNavigationServer3D::get_process_info(ProcessInfo p_info) const {
case INFO_EDGE_FREE_COUNT: {
return pm_edge_free_count;
} break;
+ case INFO_OBSTACLE_COUNT: {
+ return pm_obstacle_count;
+ } break;
}
return 0;
diff --git a/modules/navigation/3d/godot_navigation_server_3d.h b/modules/navigation/3d/godot_navigation_server_3d.h
index 5ba7ed1088..12a1132f07 100644
--- a/modules/navigation/3d/godot_navigation_server_3d.h
+++ b/modules/navigation/3d/godot_navigation_server_3d.h
@@ -95,6 +95,7 @@ class GodotNavigationServer3D : public NavigationServer3D {
int pm_edge_merge_count = 0;
int pm_edge_connection_count = 0;
int pm_edge_free_count = 0;
+ int pm_obstacle_count = 0;
public:
GodotNavigationServer3D();
diff --git a/modules/navigation/editor/navigation_mesh_editor_plugin.cpp b/modules/navigation/editor/navigation_mesh_editor_plugin.cpp
index d07d3cdff5..f37ed9b168 100644
--- a/modules/navigation/editor/navigation_mesh_editor_plugin.cpp
+++ b/modules/navigation/editor/navigation_mesh_editor_plugin.cpp
@@ -126,9 +126,6 @@ void NavigationMeshEditor::edit(NavigationRegion3D *p_nav_region) {
node = p_nav_region;
}
-void NavigationMeshEditor::_bind_methods() {
-}
-
NavigationMeshEditor::NavigationMeshEditor() {
bake_hbox = memnew(HBoxContainer);
diff --git a/modules/navigation/editor/navigation_mesh_editor_plugin.h b/modules/navigation/editor/navigation_mesh_editor_plugin.h
index 6114c62ebf..f5a471d531 100644
--- a/modules/navigation/editor/navigation_mesh_editor_plugin.h
+++ b/modules/navigation/editor/navigation_mesh_editor_plugin.h
@@ -60,7 +60,6 @@ class NavigationMeshEditor : public Control {
protected:
void _node_removed(Node *p_node);
- static void _bind_methods();
void _notification(int p_what);
public:
diff --git a/modules/navigation/nav_map.cpp b/modules/navigation/nav_map.cpp
index f917c988ea..0c91e8dea3 100644
--- a/modules/navigation/nav_map.cpp
+++ b/modules/navigation/nav_map.cpp
@@ -907,6 +907,7 @@ void NavMap::sync() {
int _new_pm_edge_merge_count = pm_edge_merge_count;
int _new_pm_edge_connection_count = pm_edge_connection_count;
int _new_pm_edge_free_count = pm_edge_free_count;
+ int _new_pm_obstacle_count = obstacles.size();
// Check if we need to update the links.
if (regenerate_polygons) {
@@ -936,8 +937,9 @@ void NavMap::sync() {
_new_pm_edge_free_count = 0;
// Remove regions connections.
+ region_external_connections.clear();
for (NavRegion *region : regions) {
- region->get_connections().clear();
+ region_external_connections[region] = LocalVector<gd::Edge::Connection>();
}
// Resize the polygon count.
@@ -1071,7 +1073,7 @@ void NavMap::sync() {
free_edge.polygon->edges[free_edge.edge].connections.push_back(new_connection);
// Add the connection to the region_connection map.
- ((NavRegion *)free_edge.polygon->owner)->get_connections().push_back(new_connection);
+ region_external_connections[(NavRegion *)free_edge.polygon->owner].push_back(new_connection);
_new_pm_edge_connection_count += 1;
}
}
@@ -1219,6 +1221,7 @@ void NavMap::sync() {
pm_edge_merge_count = _new_pm_edge_merge_count;
pm_edge_connection_count = _new_pm_edge_connection_count;
pm_edge_free_count = _new_pm_edge_free_count;
+ pm_obstacle_count = _new_pm_obstacle_count;
}
void NavMap::_update_rvo_obstacles_tree_2d() {
@@ -1426,6 +1429,40 @@ void NavMap::_update_merge_rasterizer_cell_dimensions() {
merge_rasterizer_cell_height = cell_height * merge_rasterizer_cell_scale;
}
+int NavMap::get_region_connections_count(NavRegion *p_region) const {
+ ERR_FAIL_NULL_V(p_region, 0);
+
+ HashMap<NavRegion *, LocalVector<gd::Edge::Connection>>::ConstIterator found_connections = region_external_connections.find(p_region);
+ if (found_connections) {
+ return found_connections->value.size();
+ }
+ return 0;
+}
+
+Vector3 NavMap::get_region_connection_pathway_start(NavRegion *p_region, int p_connection_id) const {
+ ERR_FAIL_NULL_V(p_region, Vector3());
+
+ HashMap<NavRegion *, LocalVector<gd::Edge::Connection>>::ConstIterator found_connections = region_external_connections.find(p_region);
+ if (found_connections) {
+ ERR_FAIL_INDEX_V(p_connection_id, int(found_connections->value.size()), Vector3());
+ return found_connections->value[p_connection_id].pathway_start;
+ }
+
+ return Vector3();
+}
+
+Vector3 NavMap::get_region_connection_pathway_end(NavRegion *p_region, int p_connection_id) const {
+ ERR_FAIL_NULL_V(p_region, Vector3());
+
+ HashMap<NavRegion *, LocalVector<gd::Edge::Connection>>::ConstIterator found_connections = region_external_connections.find(p_region);
+ if (found_connections) {
+ ERR_FAIL_INDEX_V(p_connection_id, int(found_connections->value.size()), Vector3());
+ return found_connections->value[p_connection_id].pathway_end;
+ }
+
+ return Vector3();
+}
+
NavMap::NavMap() {
avoidance_use_multiple_threads = GLOBAL_GET("navigation/avoidance/thread_model/avoidance_use_multiple_threads");
avoidance_use_high_priority_threads = GLOBAL_GET("navigation/avoidance/thread_model/avoidance_use_high_priority_threads");
diff --git a/modules/navigation/nav_map.h b/modules/navigation/nav_map.h
index d6215ea57f..82e8854b7a 100644
--- a/modules/navigation/nav_map.h
+++ b/modules/navigation/nav_map.h
@@ -123,6 +123,9 @@ class NavMap : public NavRid {
int pm_edge_merge_count = 0;
int pm_edge_connection_count = 0;
int pm_edge_free_count = 0;
+ int pm_obstacle_count = 0;
+
+ HashMap<NavRegion *, LocalVector<gd::Edge::Connection>> region_external_connections;
public:
NavMap();
@@ -216,6 +219,11 @@ public:
int get_pm_edge_merge_count() const { return pm_edge_merge_count; }
int get_pm_edge_connection_count() const { return pm_edge_connection_count; }
int get_pm_edge_free_count() const { return pm_edge_free_count; }
+ int get_pm_obstacle_count() const { return pm_obstacle_count; }
+
+ int get_region_connections_count(NavRegion *p_region) const;
+ Vector3 get_region_connection_pathway_start(NavRegion *p_region, int p_connection_id) const;
+ Vector3 get_region_connection_pathway_end(NavRegion *p_region, int p_connection_id) const;
private:
void compute_single_step(uint32_t index, NavAgent **agent);
diff --git a/modules/navigation/nav_region.cpp b/modules/navigation/nav_region.cpp
index f30855d697..85510bd416 100644
--- a/modules/navigation/nav_region.cpp
+++ b/modules/navigation/nav_region.cpp
@@ -44,8 +44,6 @@ void NavRegion::set_map(NavMap *p_map) {
map = p_map;
polygons_dirty = true;
- connections.clear();
-
if (map) {
map->add_region(this);
}
@@ -105,25 +103,6 @@ void NavRegion::set_navigation_mesh(Ref<NavigationMesh> p_navigation_mesh) {
polygons_dirty = true;
}
-int NavRegion::get_connections_count() const {
- if (!map) {
- return 0;
- }
- return connections.size();
-}
-
-Vector3 NavRegion::get_connection_pathway_start(int p_connection_id) const {
- ERR_FAIL_NULL_V(map, Vector3());
- ERR_FAIL_INDEX_V(p_connection_id, connections.size(), Vector3());
- return connections[p_connection_id].pathway_start;
-}
-
-Vector3 NavRegion::get_connection_pathway_end(int p_connection_id) const {
- ERR_FAIL_NULL_V(map, Vector3());
- ERR_FAIL_INDEX_V(p_connection_id, connections.size(), Vector3());
- return connections[p_connection_id].pathway_end;
-}
-
Vector3 NavRegion::get_random_point(uint32_t p_navigation_layers, bool p_uniformly) const {
if (!get_enabled()) {
return Vector3();
diff --git a/modules/navigation/nav_region.h b/modules/navigation/nav_region.h
index ebc082bd2f..662a32c47a 100644
--- a/modules/navigation/nav_region.h
+++ b/modules/navigation/nav_region.h
@@ -40,7 +40,6 @@
class NavRegion : public NavBase {
NavMap *map = nullptr;
Transform3D transform;
- Vector<gd::Edge::Connection> connections;
bool enabled = true;
bool use_edge_connections = true;
@@ -85,13 +84,6 @@ public:
void set_navigation_mesh(Ref<NavigationMesh> p_navigation_mesh);
- Vector<gd::Edge::Connection> &get_connections() {
- return connections;
- }
- int get_connections_count() const;
- Vector3 get_connection_pathway_start(int p_connection_id) const;
- Vector3 get_connection_pathway_end(int p_connection_id) const;
-
LocalVector<gd::Polygon> const &get_polygons() const {
return polygons;
}
diff --git a/modules/openxr/editor/openxr_select_runtime.cpp b/modules/openxr/editor/openxr_select_runtime.cpp
index 026797c6e0..4d95b079e2 100644
--- a/modules/openxr/editor/openxr_select_runtime.cpp
+++ b/modules/openxr/editor/openxr_select_runtime.cpp
@@ -35,9 +35,6 @@
#include "editor/editor_settings.h"
#include "editor/editor_string_names.h"
-void OpenXRSelectRuntime::_bind_methods() {
-}
-
void OpenXRSelectRuntime::_update_items() {
Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
OS *os = OS::get_singleton();
diff --git a/modules/openxr/editor/openxr_select_runtime.h b/modules/openxr/editor/openxr_select_runtime.h
index 60b5137f67..9a3487439c 100644
--- a/modules/openxr/editor/openxr_select_runtime.h
+++ b/modules/openxr/editor/openxr_select_runtime.h
@@ -40,7 +40,6 @@ public:
OpenXRSelectRuntime();
protected:
- static void _bind_methods();
void _notification(int p_notification);
private:
diff --git a/modules/openxr/extensions/openxr_mxink_extension.cpp b/modules/openxr/extensions/openxr_mxink_extension.cpp
new file mode 100644
index 0000000000..fe48583c27
--- /dev/null
+++ b/modules/openxr/extensions/openxr_mxink_extension.cpp
@@ -0,0 +1,83 @@
+/**************************************************************************/
+/* openxr_mxink_extension.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* 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 "openxr_mxink_extension.h"
+
+#include "../action_map/openxr_interaction_profile_metadata.h"
+
+// Not in base XR libs needs def
+#define XR_LOGITECH_MX_INK_STYLUS_INTERACTION_EXTENSION_NAME "XR_LOGITECH_mx_ink_stylus_interaction"
+
+HashMap<String, bool *> OpenXRMxInkExtension::get_requested_extensions() {
+ HashMap<String, bool *> request_extensions;
+
+ request_extensions[XR_LOGITECH_MX_INK_STYLUS_INTERACTION_EXTENSION_NAME] = &available;
+
+ return request_extensions;
+}
+
+bool OpenXRMxInkExtension::is_available() {
+ return available;
+}
+
+void OpenXRMxInkExtension::on_register_metadata() {
+ OpenXRInteractionProfileMetadata *metadata = OpenXRInteractionProfileMetadata::get_singleton();
+ ERR_FAIL_NULL(metadata);
+
+ // Logitech MX Ink Stylus
+ metadata->register_interaction_profile("Logitech MX Ink Stylus", "/interaction_profiles/logitech/mx_ink_stylus_logitech", XR_LOGITECH_MX_INK_STYLUS_INTERACTION_EXTENSION_NAME);
+
+ metadata->register_io_path("/interaction_profiles/logitech/mx_ink_stylus_logitech", "Tip Force", "/user/hand/left", "/user/hand/left/input/tip_logitech/force", "", OpenXRAction::OPENXR_ACTION_FLOAT);
+ metadata->register_io_path("/interaction_profiles/logitech/mx_ink_stylus_logitech", "Middle force", "/user/hand/left", "/user/hand/left/input/cluster_middle_logitech/force", "", OpenXRAction::OPENXR_ACTION_FLOAT);
+ metadata->register_io_path("/interaction_profiles/logitech/mx_ink_stylus_logitech", "Front click", "/user/hand/left", "/user/hand/left/input/cluster_front_logitech/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ metadata->register_io_path("/interaction_profiles/logitech/mx_ink_stylus_logitech", "Front double", "/user/hand/left", "/user/hand/left/input/cluster_front_logitech/double_tap_logitech", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ metadata->register_io_path("/interaction_profiles/logitech/mx_ink_stylus_logitech", "Back click", "/user/hand/left", "/user/hand/left/input/cluster_back_logitech/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ metadata->register_io_path("/interaction_profiles/logitech/mx_ink_stylus_logitech", "Back double", "/user/hand/left", "/user/hand/left/input/cluster_back_logitech/double_tap_logitech", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ metadata->register_io_path("/interaction_profiles/logitech/mx_ink_stylus_logitech", "System click", "/user/hand/left", "/user/hand/left/input/system/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ metadata->register_io_path("/interaction_profiles/logitech/mx_ink_stylus_logitech", "Docked", "/user/hand/left", "/user/hand/left/input/dock_logitech/docked_logitech", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ metadata->register_io_path("/interaction_profiles/logitech/mx_ink_stylus_logitech", "Grip pose", "/user/hand/left", "/user/hand/left/input/grip/pose", "", OpenXRAction::OPENXR_ACTION_POSE);
+ metadata->register_io_path("/interaction_profiles/logitech/mx_ink_stylus_logitech", "Aim pose", "/user/hand/left", "/user/hand/left/input/aim/pose", "", OpenXRAction::OPENXR_ACTION_POSE);
+ metadata->register_io_path("/interaction_profiles/logitech/mx_ink_stylus_logitech", "Tip pose", "/user/hand/left", "/user/hand/left/input/tip_logitech/pose", "", OpenXRAction::OPENXR_ACTION_POSE);
+
+ metadata->register_io_path("/interaction_profiles/logitech/mx_ink_stylus_logitech", "Tip Force", "/user/hand/right", "/user/hand/right/input/tip_logitech/force", "", OpenXRAction::OPENXR_ACTION_FLOAT);
+ metadata->register_io_path("/interaction_profiles/logitech/mx_ink_stylus_logitech", "Middle force", "/user/hand/right", "/user/hand/right/input/cluster_middle_logitech/force", "", OpenXRAction::OPENXR_ACTION_FLOAT);
+ metadata->register_io_path("/interaction_profiles/logitech/mx_ink_stylus_logitech", "Front click", "/user/hand/right", "/user/hand/right/input/cluster_front_logitech/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ metadata->register_io_path("/interaction_profiles/logitech/mx_ink_stylus_logitech", "Front double", "/user/hand/right", "/user/hand/right/input/cluster_front_logitech/double_tap_logitech", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ metadata->register_io_path("/interaction_profiles/logitech/mx_ink_stylus_logitech", "Back click", "/user/hand/right", "/user/hand/right/input/cluster_back_logitech/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ metadata->register_io_path("/interaction_profiles/logitech/mx_ink_stylus_logitech", "Back double", "/user/hand/right", "/user/hand/right/input/cluster_back_logitech/double_tap_logitech", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ metadata->register_io_path("/interaction_profiles/logitech/mx_ink_stylus_logitech", "System click", "/user/hand/right", "/user/hand/right/input/system/click", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ metadata->register_io_path("/interaction_profiles/logitech/mx_ink_stylus_logitech", "Docked", "/user/hand/right", "/user/hand/right/input/dock_logitech/docked_logitech", "", OpenXRAction::OPENXR_ACTION_BOOL);
+ metadata->register_io_path("/interaction_profiles/logitech/mx_ink_stylus_logitech", "Grip pose", "/user/hand/right", "/user/hand/right/input/grip/pose", "", OpenXRAction::OPENXR_ACTION_POSE);
+ metadata->register_io_path("/interaction_profiles/logitech/mx_ink_stylus_logitech", "Aim pose", "/user/hand/right", "/user/hand/right/input/aim/pose", "", OpenXRAction::OPENXR_ACTION_POSE);
+ metadata->register_io_path("/interaction_profiles/logitech/mx_ink_stylus_logitech", "Tip pose", "/user/hand/right", "/user/hand/right/input/tip_logitech/pose", "", OpenXRAction::OPENXR_ACTION_POSE);
+
+ metadata->register_io_path("/interaction_profiles/logitech/mx_ink_stylus_logitech", "Haptic output", "/user/hand/left", "/user/hand/left/output/haptic", "", OpenXRAction::OPENXR_ACTION_HAPTIC);
+ metadata->register_io_path("/interaction_profiles/logitech/mx_ink_stylus_logitech", "Haptic output", "/user/hand/right", "/user/hand/right/output/haptic", "", OpenXRAction::OPENXR_ACTION_HAPTIC);
+}
diff --git a/modules/openxr/extensions/openxr_mxink_extension.h b/modules/openxr/extensions/openxr_mxink_extension.h
new file mode 100644
index 0000000000..fe0cf866aa
--- /dev/null
+++ b/modules/openxr/extensions/openxr_mxink_extension.h
@@ -0,0 +1,48 @@
+/**************************************************************************/
+/* openxr_mxink_extension.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* 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 OPENXR_MXINK_EXTENSION_H
+#define OPENXR_MXINK_EXTENSION_H
+
+#include "openxr_extension_wrapper.h"
+
+class OpenXRMxInkExtension : public OpenXRExtensionWrapper {
+public:
+ virtual HashMap<String, bool *> get_requested_extensions() override;
+
+ bool is_available();
+
+ virtual void on_register_metadata() override;
+
+private:
+ bool available = false;
+};
+
+#endif // OPENXR_MXINK_EXTENSION_H
diff --git a/modules/openxr/openxr_api.cpp b/modules/openxr/openxr_api.cpp
index 98e5484157..c6fd20dac7 100644
--- a/modules/openxr/openxr_api.cpp
+++ b/modules/openxr/openxr_api.cpp
@@ -525,7 +525,7 @@ bool OpenXRAPI::create_instance() {
1, // applicationVersion, we don't currently have this
"Godot Game Engine", // engineName
VERSION_MAJOR * 10000 + VERSION_MINOR * 100 + VERSION_PATCH, // engineVersion 4.0 -> 40000, 4.0.1 -> 40001, 4.1 -> 40100, etc.
- XR_CURRENT_API_VERSION // apiVersion
+ XR_API_VERSION_1_0 // apiVersion
};
void *next_pointer = nullptr;
diff --git a/modules/openxr/openxr_interface.cpp b/modules/openxr/openxr_interface.cpp
index cce9c09361..73ac529537 100644
--- a/modules/openxr/openxr_interface.cpp
+++ b/modules/openxr/openxr_interface.cpp
@@ -36,7 +36,8 @@
#include "extensions/openxr_eye_gaze_interaction.h"
#include "extensions/openxr_hand_interaction_extension.h"
-#include "thirdparty/openxr/include/openxr/openxr.h"
+
+#include <openxr/openxr.h>
void OpenXRInterface::_bind_methods() {
// lifecycle signals
diff --git a/modules/openxr/register_types.cpp b/modules/openxr/register_types.cpp
index 85514737f2..64381ae1c7 100644
--- a/modules/openxr/register_types.cpp
+++ b/modules/openxr/register_types.cpp
@@ -57,6 +57,7 @@
#include "extensions/openxr_local_floor_extension.h"
#include "extensions/openxr_meta_controller_extension.h"
#include "extensions/openxr_ml2_controller_extension.h"
+#include "extensions/openxr_mxink_extension.h"
#include "extensions/openxr_palm_pose_extension.h"
#include "extensions/openxr_pico_controller_extension.h"
#include "extensions/openxr_wmr_controller_extension.h"
@@ -126,6 +127,7 @@ void initialize_openxr_module(ModuleInitializationLevel p_level) {
OpenXRAPI::register_extension_wrapper(memnew(OpenXRMetaControllerExtension));
OpenXRAPI::register_extension_wrapper(memnew(OpenXREyeGazeInteractionExtension));
OpenXRAPI::register_extension_wrapper(memnew(OpenXRHandInteractionExtension));
+ OpenXRAPI::register_extension_wrapper(memnew(OpenXRMxInkExtension));
// register gated extensions
if (GLOBAL_GET("xr/openxr/extensions/hand_tracking")) {
diff --git a/modules/text_server_adv/SCsub b/modules/text_server_adv/SCsub
index 68a5d499d4..4112b81622 100644
--- a/modules/text_server_adv/SCsub
+++ b/modules/text_server_adv/SCsub
@@ -142,7 +142,14 @@ if env["builtin_harfbuzz"]:
env_harfbuzz.Append(CCFLAGS=["-DHAVE_ICU"])
if env["builtin_icu4c"]:
env_harfbuzz.Prepend(CPPPATH=["#thirdparty/icu4c/common/", "#thirdparty/icu4c/i18n/"])
- env_harfbuzz.Append(CCFLAGS=["-DU_HAVE_LIB_SUFFIX=1", "-DU_LIB_SUFFIX_C_NAME=_godot", "-DHAVE_ICU_BUILTIN"])
+ env_harfbuzz.Append(
+ CCFLAGS=[
+ "-DU_STATIC_IMPLEMENTATION",
+ "-DU_HAVE_LIB_SUFFIX=1",
+ "-DU_LIB_SUFFIX_C_NAME=_godot",
+ "-DHAVE_ICU_BUILTIN",
+ ]
+ )
if freetype_enabled:
env_harfbuzz.Append(
@@ -499,6 +506,7 @@ if env["builtin_icu4c"]:
)
env_text_server_adv.Append(
CXXFLAGS=[
+ "-DU_STATIC_IMPLEMENTATION",
"-DU_HAVE_LIB_SUFFIX=1",
"-DU_LIB_SUFFIX_C_NAME=_godot",
"-DICU_DATA_NAME=" + icu_data_name,
diff --git a/modules/text_server_adv/gdextension_build/SConstruct b/modules/text_server_adv/gdextension_build/SConstruct
index d0d13fec3f..effed1e772 100644
--- a/modules/text_server_adv/gdextension_build/SConstruct
+++ b/modules/text_server_adv/gdextension_build/SConstruct
@@ -415,6 +415,7 @@ if env["platform"] == "android" or env["platform"] == "linuxbsd":
env_harfbuzz.Append(
CCFLAGS=[
+ "-DU_STATIC_IMPLEMENTATION",
"-DU_HAVE_LIB_SUFFIX=1",
"-DU_LIB_SUFFIX_C_NAME=_godot",
"-DHAVE_ICU_BUILTIN",
@@ -746,6 +747,7 @@ env_icu.Append(
)
env.Append(
CXXFLAGS=[
+ "-DU_STATIC_IMPLEMENTATION",
"-DU_HAVE_LIB_SUFFIX=1",
"-DU_LIB_SUFFIX_C_NAME=_godot",
"-DICU_DATA_NAME=" + icu_data_name,
diff --git a/modules/text_server_adv/text_server_adv.cpp b/modules/text_server_adv/text_server_adv.cpp
index 499ddb703b..d0c22e9e4d 100644
--- a/modules/text_server_adv/text_server_adv.cpp
+++ b/modules/text_server_adv/text_server_adv.cpp
@@ -51,7 +51,7 @@ using namespace godot;
#include "core/error/error_macros.h"
#include "core/object/worker_thread_pool.h"
#include "core/string/print_string.h"
-#include "core/string/translation.h"
+#include "core/string/translation_server.h"
#include "scene/resources/image_texture.h"
#include "modules/modules_enabled.gen.h" // For freetype, msdfgen, svg.
@@ -3528,6 +3528,37 @@ String TextServerAdvanced::_font_get_supported_chars(const RID &p_font_rid) cons
return chars;
}
+PackedInt32Array TextServerAdvanced::_font_get_supported_glyphs(const RID &p_font_rid) const {
+ FontAdvanced *fd = _get_font_data(p_font_rid);
+ ERR_FAIL_NULL_V(fd, PackedInt32Array());
+
+ MutexLock lock(fd->mutex);
+ if (fd->cache.is_empty()) {
+ ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, fd->msdf ? Vector2i(fd->msdf_source_size, 0) : Vector2i(16, 0)), PackedInt32Array());
+ }
+ FontForSizeAdvanced *at_size = fd->cache.begin()->value;
+
+ PackedInt32Array glyphs;
+#ifdef MODULE_FREETYPE_ENABLED
+ if (at_size && at_size->face) {
+ FT_UInt gindex;
+ FT_ULong charcode = FT_Get_First_Char(at_size->face, &gindex);
+ while (gindex != 0) {
+ glyphs.push_back(gindex);
+ charcode = FT_Get_Next_Char(at_size->face, charcode, &gindex);
+ }
+ return glyphs;
+ }
+#endif
+ if (at_size) {
+ const HashMap<int32_t, FontGlyph> &gl = at_size->glyph_map;
+ for (const KeyValue<int32_t, FontGlyph> &E : gl) {
+ glyphs.push_back(E.key);
+ }
+ }
+ return glyphs;
+}
+
void TextServerAdvanced::_font_render_range(const RID &p_font_rid, const Vector2i &p_size, int64_t p_start, int64_t p_end) {
FontAdvanced *fd = _get_font_data(p_font_rid);
ERR_FAIL_NULL(fd);
diff --git a/modules/text_server_adv/text_server_adv.h b/modules/text_server_adv/text_server_adv.h
index 92bdb93bcf..fdebb8e4cd 100644
--- a/modules/text_server_adv/text_server_adv.h
+++ b/modules/text_server_adv/text_server_adv.h
@@ -871,6 +871,7 @@ public:
MODBIND2RC(bool, font_has_char, const RID &, int64_t);
MODBIND1RC(String, font_get_supported_chars, const RID &);
+ MODBIND1RC(PackedInt32Array, font_get_supported_glyphs, const RID &);
MODBIND4(font_render_range, const RID &, const Vector2i &, int64_t, int64_t);
MODBIND3(font_render_glyph, const RID &, const Vector2i &, int64_t);
diff --git a/modules/text_server_fb/text_server_fb.cpp b/modules/text_server_fb/text_server_fb.cpp
index b45c004011..a7ddfc719e 100644
--- a/modules/text_server_fb/text_server_fb.cpp
+++ b/modules/text_server_fb/text_server_fb.cpp
@@ -52,7 +52,7 @@ using namespace godot;
#include "core/config/project_settings.h"
#include "core/error/error_macros.h"
#include "core/string/print_string.h"
-#include "core/string/translation.h"
+#include "core/string/translation_server.h"
#include "modules/modules_enabled.gen.h" // For freetype, msdfgen, svg.
@@ -2477,6 +2477,37 @@ String TextServerFallback::_font_get_supported_chars(const RID &p_font_rid) cons
return chars;
}
+PackedInt32Array TextServerFallback::_font_get_supported_glyphs(const RID &p_font_rid) const {
+ FontFallback *fd = _get_font_data(p_font_rid);
+ ERR_FAIL_NULL_V(fd, PackedInt32Array());
+
+ MutexLock lock(fd->mutex);
+ if (fd->cache.is_empty()) {
+ ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, fd->msdf ? Vector2i(fd->msdf_source_size, 0) : Vector2i(16, 0)), PackedInt32Array());
+ }
+ FontForSizeFallback *at_size = fd->cache.begin()->value;
+
+ PackedInt32Array glyphs;
+#ifdef MODULE_FREETYPE_ENABLED
+ if (at_size && at_size->face) {
+ FT_UInt gindex;
+ FT_ULong charcode = FT_Get_First_Char(at_size->face, &gindex);
+ while (gindex != 0) {
+ glyphs.push_back(gindex);
+ charcode = FT_Get_Next_Char(at_size->face, charcode, &gindex);
+ }
+ return glyphs;
+ }
+#endif
+ if (at_size) {
+ const HashMap<int32_t, FontGlyph> &gl = at_size->glyph_map;
+ for (const KeyValue<int32_t, FontGlyph> &E : gl) {
+ glyphs.push_back(E.key);
+ }
+ }
+ return glyphs;
+}
+
void TextServerFallback::_font_render_range(const RID &p_font_rid, const Vector2i &p_size, int64_t p_start, int64_t p_end) {
FontFallback *fd = _get_font_data(p_font_rid);
ERR_FAIL_NULL(fd);
diff --git a/modules/text_server_fb/text_server_fb.h b/modules/text_server_fb/text_server_fb.h
index 2235247b31..1b76c6fa0f 100644
--- a/modules/text_server_fb/text_server_fb.h
+++ b/modules/text_server_fb/text_server_fb.h
@@ -739,6 +739,7 @@ public:
MODBIND2RC(bool, font_has_char, const RID &, int64_t);
MODBIND1RC(String, font_get_supported_chars, const RID &);
+ MODBIND1RC(PackedInt32Array, font_get_supported_glyphs, const RID &);
MODBIND4(font_render_range, const RID &, const Vector2i &, int64_t, int64_t);
MODBIND3(font_render_glyph, const RID &, const Vector2i &, int64_t);
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 fa39ccb546..111cd48405 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/Godot.kt
+++ b/platform/android/java/lib/src/org/godotengine/godot/Godot.kt
@@ -39,8 +39,6 @@ import android.content.res.Configuration
import android.content.res.Resources
import android.graphics.Color
import android.hardware.Sensor
-import android.hardware.SensorEvent
-import android.hardware.SensorEventListener
import android.hardware.SensorManager
import android.os.*
import android.util.Log
@@ -53,6 +51,7 @@ import androidx.core.view.WindowInsetsAnimationCompat
import androidx.core.view.WindowInsetsCompat
import com.google.android.vending.expansion.downloader.*
import org.godotengine.godot.input.GodotEditText
+import org.godotengine.godot.input.GodotInputHandler
import org.godotengine.godot.io.directory.DirectoryAccessHandler
import org.godotengine.godot.io.file.FileAccessHandler
import org.godotengine.godot.plugin.GodotPluginRegistry
@@ -73,6 +72,7 @@ import java.io.InputStream
import java.lang.Exception
import java.security.MessageDigest
import java.util.*
+import java.util.concurrent.atomic.AtomicBoolean
import java.util.concurrent.atomic.AtomicReference
/**
@@ -81,7 +81,7 @@ import java.util.concurrent.atomic.AtomicReference
* Can be hosted by [Activity], [Fragment] or [Service] android components, so long as its
* lifecycle methods are properly invoked.
*/
-class Godot(private val context: Context) : SensorEventListener {
+class Godot(private val context: Context) {
private companion object {
private val TAG = Godot::class.java.simpleName
@@ -99,15 +99,23 @@ class Godot(private val context: Context) : SensorEventListener {
private val pluginRegistry: GodotPluginRegistry by lazy {
GodotPluginRegistry.getPluginRegistry()
}
+
+ private val accelerometer_enabled = AtomicBoolean(false)
private val mAccelerometer: Sensor? by lazy {
mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER)
}
+
+ private val gravity_enabled = AtomicBoolean(false)
private val mGravity: Sensor? by lazy {
mSensorManager.getDefaultSensor(Sensor.TYPE_GRAVITY)
}
+
+ private val magnetometer_enabled = AtomicBoolean(false)
private val mMagnetometer: Sensor? by lazy {
mSensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD)
}
+
+ private val gyroscope_enabled = AtomicBoolean(false)
private val mGyroscope: Sensor? by lazy {
mSensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE)
}
@@ -127,6 +135,7 @@ class Godot(private val context: Context) : SensorEventListener {
val fileAccessHandler = FileAccessHandler(context)
val netUtils = GodotNetUtils(context)
private val commandLineFileParser = CommandLineFileParser()
+ private val godotInputHandler = GodotInputHandler(context, this)
/**
* Task to run when the engine terminates.
@@ -154,6 +163,17 @@ class Godot(private val context: Context) : SensorEventListener {
private var renderViewInitialized = false
private var primaryHost: GodotHost? = null
+ /**
+ * Tracks whether we're in the RESUMED lifecycle state.
+ * See [onResume] and [onPause]
+ */
+ private var resumed = false
+
+ /**
+ * Tracks whether [onGodotSetupCompleted] fired.
+ */
+ private val godotMainLoopStarted = AtomicBoolean(false)
+
var io: GodotIO? = null
private var commandLine : MutableList<String> = ArrayList<String>()
@@ -416,10 +436,10 @@ class Godot(private val context: Context) : SensorEventListener {
if (!meetsVulkanRequirements(activity.packageManager)) {
throw IllegalStateException(activity.getString(R.string.error_missing_vulkan_requirements_message))
}
- GodotVulkanRenderView(host, this)
+ GodotVulkanRenderView(host, this, godotInputHandler)
} else {
// Fallback to openGl
- GodotGLRenderView(host, this, xrMode, useDebugOpengl)
+ GodotGLRenderView(host, this, godotInputHandler, xrMode, useDebugOpengl)
}
if (host == primaryHost) {
@@ -520,23 +540,13 @@ class Godot(private val context: Context) : SensorEventListener {
fun onResume(host: GodotHost) {
Log.v(TAG, "OnResume: $host")
+ resumed = true
if (host != primaryHost) {
return
}
renderView?.onActivityResumed()
- if (mAccelerometer != null) {
- mSensorManager.registerListener(this, mAccelerometer, SensorManager.SENSOR_DELAY_GAME)
- }
- if (mGravity != null) {
- mSensorManager.registerListener(this, mGravity, SensorManager.SENSOR_DELAY_GAME)
- }
- if (mMagnetometer != null) {
- mSensorManager.registerListener(this, mMagnetometer, SensorManager.SENSOR_DELAY_GAME)
- }
- if (mGyroscope != null) {
- mSensorManager.registerListener(this, mGyroscope, SensorManager.SENSOR_DELAY_GAME)
- }
+ registerSensorsIfNeeded()
if (useImmersive) {
val window = requireActivity().window
window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_LAYOUT_STABLE or
@@ -551,14 +561,34 @@ class Godot(private val context: Context) : SensorEventListener {
}
}
+ private fun registerSensorsIfNeeded() {
+ if (!resumed || !godotMainLoopStarted.get()) {
+ return
+ }
+
+ if (accelerometer_enabled.get() && mAccelerometer != null) {
+ mSensorManager.registerListener(godotInputHandler, mAccelerometer, SensorManager.SENSOR_DELAY_GAME)
+ }
+ if (gravity_enabled.get() && mGravity != null) {
+ mSensorManager.registerListener(godotInputHandler, mGravity, SensorManager.SENSOR_DELAY_GAME)
+ }
+ if (magnetometer_enabled.get() && mMagnetometer != null) {
+ mSensorManager.registerListener(godotInputHandler, mMagnetometer, SensorManager.SENSOR_DELAY_GAME)
+ }
+ if (gyroscope_enabled.get() && mGyroscope != null) {
+ mSensorManager.registerListener(godotInputHandler, mGyroscope, SensorManager.SENSOR_DELAY_GAME)
+ }
+ }
+
fun onPause(host: GodotHost) {
Log.v(TAG, "OnPause: $host")
+ resumed = false
if (host != primaryHost) {
return
}
renderView?.onActivityPaused()
- mSensorManager.unregisterListener(this)
+ mSensorManager.unregisterListener(godotInputHandler)
for (plugin in pluginRegistry.allPlugins) {
plugin.onMainPause()
}
@@ -659,6 +689,16 @@ class Godot(private val context: Context) : SensorEventListener {
*/
private fun onGodotMainLoopStarted() {
Log.v(TAG, "OnGodotMainLoopStarted")
+ godotMainLoopStarted.set(true)
+
+ accelerometer_enabled.set(java.lang.Boolean.parseBoolean(GodotLib.getGlobal("input_devices/sensors/enable_accelerometer")))
+ gravity_enabled.set(java.lang.Boolean.parseBoolean(GodotLib.getGlobal("input_devices/sensors/enable_gravity")))
+ gyroscope_enabled.set(java.lang.Boolean.parseBoolean(GodotLib.getGlobal("input_devices/sensors/enable_gyroscope")))
+ magnetometer_enabled.set(java.lang.Boolean.parseBoolean(GodotLib.getGlobal("input_devices/sensors/enable_magnetometer")))
+
+ runOnUiThread {
+ registerSensorsIfNeeded()
+ }
for (plugin in pluginRegistry.allPlugins) {
plugin.onGodotMainLoopStarted()
@@ -858,77 +898,6 @@ class Godot(private val context: Context) : SensorEventListener {
}
}
- private fun getRotatedValues(values: FloatArray?): FloatArray? {
- if (values == null || values.size != 3) {
- return null
- }
- val rotatedValues = FloatArray(3)
- when (windowManager.defaultDisplay.rotation) {
- Surface.ROTATION_0 -> {
- rotatedValues[0] = values[0]
- rotatedValues[1] = values[1]
- rotatedValues[2] = values[2]
- }
- Surface.ROTATION_90 -> {
- rotatedValues[0] = -values[1]
- rotatedValues[1] = values[0]
- rotatedValues[2] = values[2]
- }
- Surface.ROTATION_180 -> {
- rotatedValues[0] = -values[0]
- rotatedValues[1] = -values[1]
- rotatedValues[2] = values[2]
- }
- Surface.ROTATION_270 -> {
- rotatedValues[0] = values[1]
- rotatedValues[1] = -values[0]
- rotatedValues[2] = values[2]
- }
- }
- return rotatedValues
- }
-
- override fun onSensorChanged(event: SensorEvent) {
- if (renderView == null) {
- return
- }
-
- val rotatedValues = getRotatedValues(event.values)
-
- when (event.sensor.type) {
- Sensor.TYPE_ACCELEROMETER -> {
- rotatedValues?.let {
- renderView?.queueOnRenderThread {
- GodotLib.accelerometer(-it[0], -it[1], -it[2])
- }
- }
- }
- Sensor.TYPE_GRAVITY -> {
- rotatedValues?.let {
- renderView?.queueOnRenderThread {
- GodotLib.gravity(-it[0], -it[1], -it[2])
- }
- }
- }
- Sensor.TYPE_MAGNETIC_FIELD -> {
- rotatedValues?.let {
- renderView?.queueOnRenderThread {
- GodotLib.magnetometer(-it[0], -it[1], -it[2])
- }
- }
- }
- Sensor.TYPE_GYROSCOPE -> {
- rotatedValues?.let {
- renderView?.queueOnRenderThread {
- GodotLib.gyroscope(it[0], it[1], it[2])
- }
- }
- }
- }
- }
-
- override fun onAccuracyChanged(sensor: Sensor?, accuracy: Int) {}
-
/**
* Used by the native code (java_godot_wrapper.h) to vibrate the device.
* @param durationMs
@@ -1063,7 +1032,7 @@ class Godot(private val context: Context) : SensorEventListener {
@Keep
private fun initInputDevices() {
- renderView?.initInputDevices()
+ godotInputHandler.initInputDevices()
}
@Keep
diff --git a/platform/android/java/lib/src/org/godotengine/godot/GodotGLRenderView.java b/platform/android/java/lib/src/org/godotengine/godot/GodotGLRenderView.java
index 7fbdb34047..15a811ce83 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/GodotGLRenderView.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/GodotGLRenderView.java
@@ -83,12 +83,12 @@ class GodotGLRenderView extends GLSurfaceView implements GodotRenderView {
private final GodotRenderer godotRenderer;
private final SparseArray<PointerIcon> customPointerIcons = new SparseArray<>();
- public GodotGLRenderView(GodotHost host, Godot godot, XRMode xrMode, boolean useDebugOpengl) {
+ public GodotGLRenderView(GodotHost host, Godot godot, GodotInputHandler inputHandler, XRMode xrMode, boolean useDebugOpengl) {
super(host.getActivity());
this.host = host;
this.godot = godot;
- this.inputHandler = new GodotInputHandler(this);
+ this.inputHandler = inputHandler;
this.godotRenderer = new GodotRenderer();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
setPointerIcon(PointerIcon.getSystemIcon(getContext(), PointerIcon.TYPE_DEFAULT));
@@ -102,11 +102,6 @@ class GodotGLRenderView extends GLSurfaceView implements GodotRenderView {
}
@Override
- public void initInputDevices() {
- this.inputHandler.initInputDevices();
- }
-
- @Override
public void queueOnRenderThread(Runnable event) {
queueEvent(event);
}
@@ -145,11 +140,6 @@ class GodotGLRenderView extends GLSurfaceView implements GodotRenderView {
}
@Override
- public void onBackPressed() {
- godot.onBackPressed();
- }
-
- @Override
public GodotInputHandler getInputHandler() {
return inputHandler;
}
diff --git a/platform/android/java/lib/src/org/godotengine/godot/GodotRenderView.java b/platform/android/java/lib/src/org/godotengine/godot/GodotRenderView.java
index 19ec0fd1a4..30821eaa8e 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/GodotRenderView.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/GodotRenderView.java
@@ -37,8 +37,6 @@ import android.view.SurfaceView;
public interface GodotRenderView {
SurfaceView getView();
- void initInputDevices();
-
/**
* Starts the thread that will drive Godot's rendering.
*/
@@ -59,8 +57,6 @@ public interface GodotRenderView {
void onActivityDestroyed();
- void onBackPressed();
-
GodotInputHandler getInputHandler();
void configurePointerIcon(int pointerType, String imagePath, float hotSpotX, float hotSpotY);
diff --git a/platform/android/java/lib/src/org/godotengine/godot/GodotVulkanRenderView.java b/platform/android/java/lib/src/org/godotengine/godot/GodotVulkanRenderView.java
index f4411ddf2c..d5b05913d8 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/GodotVulkanRenderView.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/GodotVulkanRenderView.java
@@ -57,12 +57,12 @@ class GodotVulkanRenderView extends VkSurfaceView implements GodotRenderView {
private final VkRenderer mRenderer;
private final SparseArray<PointerIcon> customPointerIcons = new SparseArray<>();
- public GodotVulkanRenderView(GodotHost host, Godot godot) {
+ public GodotVulkanRenderView(GodotHost host, Godot godot, GodotInputHandler inputHandler) {
super(host.getActivity());
this.host = host;
this.godot = godot;
- mInputHandler = new GodotInputHandler(this);
+ mInputHandler = inputHandler;
mRenderer = new VkRenderer();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
setPointerIcon(PointerIcon.getSystemIcon(getContext(), PointerIcon.TYPE_DEFAULT));
@@ -81,11 +81,6 @@ class GodotVulkanRenderView extends VkSurfaceView implements GodotRenderView {
}
@Override
- public void initInputDevices() {
- mInputHandler.initInputDevices();
- }
-
- @Override
public void queueOnRenderThread(Runnable event) {
queueOnVkThread(event);
}
@@ -124,11 +119,6 @@ class GodotVulkanRenderView extends VkSurfaceView implements GodotRenderView {
}
@Override
- public void onBackPressed() {
- godot.onBackPressed();
- }
-
- @Override
public GodotInputHandler getInputHandler() {
return mInputHandler;
}
diff --git a/platform/android/java/lib/src/org/godotengine/godot/input/GodotInputHandler.java b/platform/android/java/lib/src/org/godotengine/godot/input/GodotInputHandler.java
index 273774a33d..fb41cd00c0 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/input/GodotInputHandler.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/input/GodotInputHandler.java
@@ -32,10 +32,14 @@ package org.godotengine.godot.input;
import static org.godotengine.godot.utils.GLUtils.DEBUG;
+import org.godotengine.godot.Godot;
import org.godotengine.godot.GodotLib;
import org.godotengine.godot.GodotRenderView;
import android.content.Context;
+import android.hardware.Sensor;
+import android.hardware.SensorEvent;
+import android.hardware.SensorEventListener;
import android.hardware.input.InputManager;
import android.os.Build;
import android.util.Log;
@@ -46,6 +50,10 @@ import android.view.InputDevice;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
+import android.view.Surface;
+import android.view.WindowManager;
+
+import androidx.annotation.NonNull;
import java.util.Collections;
import java.util.HashSet;
@@ -54,7 +62,7 @@ import java.util.Set;
/**
* Handles input related events for the {@link GodotRenderView} view.
*/
-public class GodotInputHandler implements InputManager.InputDeviceListener {
+public class GodotInputHandler implements InputManager.InputDeviceListener, SensorEventListener {
private static final String TAG = GodotInputHandler.class.getSimpleName();
private static final int ROTARY_INPUT_VERTICAL_AXIS = 1;
@@ -64,8 +72,9 @@ public class GodotInputHandler implements InputManager.InputDeviceListener {
private final SparseArray<Joystick> mJoysticksDevices = new SparseArray<>(4);
private final HashSet<Integer> mHardwareKeyboardIds = new HashSet<>();
- private final GodotRenderView mRenderView;
+ private final Godot godot;
private final InputManager mInputManager;
+ private final WindowManager windowManager;
private final GestureDetector gestureDetector;
private final ScaleGestureDetector scaleGestureDetector;
private final GodotGestureHandler godotGestureHandler;
@@ -77,12 +86,13 @@ public class GodotInputHandler implements InputManager.InputDeviceListener {
private int rotaryInputAxis = ROTARY_INPUT_VERTICAL_AXIS;
- public GodotInputHandler(GodotRenderView godotView) {
- final Context context = godotView.getView().getContext();
- mRenderView = godotView;
+ public GodotInputHandler(Context context, Godot godot) {
+ this.godot = godot;
mInputManager = (InputManager)context.getSystemService(Context.INPUT_SERVICE);
mInputManager.registerInputDeviceListener(this, null);
+ windowManager = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE);
+
this.godotGestureHandler = new GodotGestureHandler(this);
this.gestureDetector = new GestureDetector(context, godotGestureHandler);
this.gestureDetector.setIsLongpressEnabled(false);
@@ -174,7 +184,7 @@ public class GodotInputHandler implements InputManager.InputDeviceListener {
public boolean onKeyDown(final int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK) {
- mRenderView.onBackPressed();
+ godot.onBackPressed();
// press 'back' button should not terminate program
//normal handle 'back' event in game logic
return true;
@@ -507,7 +517,7 @@ public class GodotInputHandler implements InputManager.InputDeviceListener {
return handleTouchEvent(event, eventActionOverride, doubleTap);
}
- private static float getEventTiltX(MotionEvent event) {
+ static float getEventTiltX(MotionEvent event) {
// Orientation is returned as a radian value between 0 to pi clockwise or 0 to -pi counterclockwise.
final float orientation = event.getOrientation();
@@ -520,7 +530,7 @@ public class GodotInputHandler implements InputManager.InputDeviceListener {
return (float)-Math.sin(orientation) * tiltMult;
}
- private static float getEventTiltY(MotionEvent event) {
+ static float getEventTiltY(MotionEvent event) {
// Orientation is returned as a radian value between 0 to pi clockwise or 0 to -pi counterclockwise.
final float orientation = event.getOrientation();
@@ -579,6 +589,11 @@ public class GodotInputHandler implements InputManager.InputDeviceListener {
}
boolean handleMouseEvent(int eventAction, int buttonsMask, float x, float y, float deltaX, float deltaY, boolean doubleClick, boolean sourceMouseRelative, float pressure, float tiltX, float tiltY) {
+ InputEventRunnable runnable = InputEventRunnable.obtain();
+ if (runnable == null) {
+ return false;
+ }
+
// Fix the buttonsMask
switch (eventAction) {
case MotionEvent.ACTION_CANCEL:
@@ -594,7 +609,6 @@ public class GodotInputHandler implements InputManager.InputDeviceListener {
break;
}
- final int updatedButtonsMask = buttonsMask;
// We don't handle ACTION_BUTTON_PRESS and ACTION_BUTTON_RELEASE events as they typically
// follow ACTION_DOWN and ACTION_UP events. As such, handling them would result in duplicate
// stream of events to the engine.
@@ -607,11 +621,8 @@ public class GodotInputHandler implements InputManager.InputDeviceListener {
case MotionEvent.ACTION_HOVER_MOVE:
case MotionEvent.ACTION_MOVE:
case MotionEvent.ACTION_SCROLL: {
- if (shouldDispatchInputToRenderThread()) {
- mRenderView.queueOnRenderThread(() -> GodotLib.dispatchMouseEvent(eventAction, updatedButtonsMask, x, y, deltaX, deltaY, doubleClick, sourceMouseRelative, pressure, tiltX, tiltY));
- } else {
- GodotLib.dispatchMouseEvent(eventAction, updatedButtonsMask, x, y, deltaX, deltaY, doubleClick, sourceMouseRelative, pressure, tiltX, tiltY);
- }
+ runnable.setMouseEvent(eventAction, buttonsMask, x, y, deltaX, deltaY, doubleClick, sourceMouseRelative, pressure, tiltX, tiltY);
+ dispatchInputEventRunnable(runnable);
return true;
}
}
@@ -627,22 +638,14 @@ public class GodotInputHandler implements InputManager.InputDeviceListener {
}
boolean handleTouchEvent(final MotionEvent event, int eventActionOverride, boolean doubleTap) {
- final int pointerCount = event.getPointerCount();
- if (pointerCount == 0) {
+ if (event.getPointerCount() == 0) {
return true;
}
- final float[] positions = new float[pointerCount * 6]; // pointerId1, x1, y1, pressure1, tiltX1, tiltY1, pointerId2, etc...
-
- for (int i = 0; i < pointerCount; i++) {
- positions[i * 6 + 0] = event.getPointerId(i);
- positions[i * 6 + 1] = event.getX(i);
- positions[i * 6 + 2] = event.getY(i);
- positions[i * 6 + 3] = event.getPressure(i);
- positions[i * 6 + 4] = getEventTiltX(event);
- positions[i * 6 + 5] = getEventTiltY(event);
+ InputEventRunnable runnable = InputEventRunnable.obtain();
+ if (runnable == null) {
+ return false;
}
- final int actionPointerId = event.getPointerId(event.getActionIndex());
switch (eventActionOverride) {
case MotionEvent.ACTION_DOWN:
@@ -651,11 +654,8 @@ public class GodotInputHandler implements InputManager.InputDeviceListener {
case MotionEvent.ACTION_MOVE:
case MotionEvent.ACTION_POINTER_UP:
case MotionEvent.ACTION_POINTER_DOWN: {
- if (shouldDispatchInputToRenderThread()) {
- mRenderView.queueOnRenderThread(() -> GodotLib.dispatchTouchEvent(eventActionOverride, actionPointerId, pointerCount, positions, doubleTap));
- } else {
- GodotLib.dispatchTouchEvent(eventActionOverride, actionPointerId, pointerCount, positions, doubleTap);
- }
+ runnable.setTouchEvent(event, eventActionOverride, doubleTap);
+ dispatchInputEventRunnable(runnable);
return true;
}
}
@@ -663,58 +663,128 @@ public class GodotInputHandler implements InputManager.InputDeviceListener {
}
void handleMagnifyEvent(float x, float y, float factor) {
- if (shouldDispatchInputToRenderThread()) {
- mRenderView.queueOnRenderThread(() -> GodotLib.magnify(x, y, factor));
- } else {
- GodotLib.magnify(x, y, factor);
+ InputEventRunnable runnable = InputEventRunnable.obtain();
+ if (runnable == null) {
+ return;
}
+
+ runnable.setMagnifyEvent(x, y, factor);
+ dispatchInputEventRunnable(runnable);
}
void handlePanEvent(float x, float y, float deltaX, float deltaY) {
- if (shouldDispatchInputToRenderThread()) {
- mRenderView.queueOnRenderThread(() -> GodotLib.pan(x, y, deltaX, deltaY));
- } else {
- GodotLib.pan(x, y, deltaX, deltaY);
+ InputEventRunnable runnable = InputEventRunnable.obtain();
+ if (runnable == null) {
+ return;
}
+
+ runnable.setPanEvent(x, y, deltaX, deltaY);
+ dispatchInputEventRunnable(runnable);
}
private void handleJoystickButtonEvent(int device, int button, boolean pressed) {
- if (shouldDispatchInputToRenderThread()) {
- mRenderView.queueOnRenderThread(() -> GodotLib.joybutton(device, button, pressed));
- } else {
- GodotLib.joybutton(device, button, pressed);
+ InputEventRunnable runnable = InputEventRunnable.obtain();
+ if (runnable == null) {
+ return;
}
+
+ runnable.setJoystickButtonEvent(device, button, pressed);
+ dispatchInputEventRunnable(runnable);
}
private void handleJoystickAxisEvent(int device, int axis, float value) {
- if (shouldDispatchInputToRenderThread()) {
- mRenderView.queueOnRenderThread(() -> GodotLib.joyaxis(device, axis, value));
- } else {
- GodotLib.joyaxis(device, axis, value);
+ InputEventRunnable runnable = InputEventRunnable.obtain();
+ if (runnable == null) {
+ return;
}
+
+ runnable.setJoystickAxisEvent(device, axis, value);
+ dispatchInputEventRunnable(runnable);
}
private void handleJoystickHatEvent(int device, int hatX, int hatY) {
- if (shouldDispatchInputToRenderThread()) {
- mRenderView.queueOnRenderThread(() -> GodotLib.joyhat(device, hatX, hatY));
- } else {
- GodotLib.joyhat(device, hatX, hatY);
+ InputEventRunnable runnable = InputEventRunnable.obtain();
+ if (runnable == null) {
+ return;
}
+
+ runnable.setJoystickHatEvent(device, hatX, hatY);
+ dispatchInputEventRunnable(runnable);
}
private void handleJoystickConnectionChangedEvent(int device, boolean connected, String name) {
- if (shouldDispatchInputToRenderThread()) {
- mRenderView.queueOnRenderThread(() -> GodotLib.joyconnectionchanged(device, connected, name));
- } else {
- GodotLib.joyconnectionchanged(device, connected, name);
+ InputEventRunnable runnable = InputEventRunnable.obtain();
+ if (runnable == null) {
+ return;
}
+
+ runnable.setJoystickConnectionChangedEvent(device, connected, name);
+ dispatchInputEventRunnable(runnable);
}
void handleKeyEvent(int physicalKeycode, int unicode, int keyLabel, boolean pressed, boolean echo) {
+ InputEventRunnable runnable = InputEventRunnable.obtain();
+ if (runnable == null) {
+ return;
+ }
+
+ runnable.setKeyEvent(physicalKeycode, unicode, keyLabel, pressed, echo);
+ dispatchInputEventRunnable(runnable);
+ }
+
+ private void dispatchInputEventRunnable(@NonNull InputEventRunnable runnable) {
if (shouldDispatchInputToRenderThread()) {
- mRenderView.queueOnRenderThread(() -> GodotLib.key(physicalKeycode, unicode, keyLabel, pressed, echo));
+ godot.runOnRenderThread(runnable);
} else {
- GodotLib.key(physicalKeycode, unicode, keyLabel, pressed, echo);
+ runnable.run();
+ }
+ }
+
+ @Override
+ public void onSensorChanged(SensorEvent event) {
+ final float[] values = event.values;
+ if (values == null || values.length != 3) {
+ return;
}
+
+ InputEventRunnable runnable = InputEventRunnable.obtain();
+ if (runnable == null) {
+ return;
+ }
+
+ float rotatedValue0 = 0f;
+ float rotatedValue1 = 0f;
+ float rotatedValue2 = 0f;
+ switch (windowManager.getDefaultDisplay().getRotation()) {
+ case Surface.ROTATION_0:
+ rotatedValue0 = values[0];
+ rotatedValue1 = values[1];
+ rotatedValue2 = values[2];
+ break;
+
+ case Surface.ROTATION_90:
+ rotatedValue0 = -values[1];
+ rotatedValue1 = values[0];
+ rotatedValue2 = values[2];
+ break;
+
+ case Surface.ROTATION_180:
+ rotatedValue0 = -values[0];
+ rotatedValue1 = -values[1];
+ rotatedValue2 = values[2];
+ break;
+
+ case Surface.ROTATION_270:
+ rotatedValue0 = values[1];
+ rotatedValue1 = -values[0];
+ rotatedValue2 = values[2];
+ break;
+ }
+
+ runnable.setSensorEvent(event.sensor.getType(), rotatedValue0, rotatedValue1, rotatedValue2);
+ godot.runOnRenderThread(runnable);
}
+
+ @Override
+ public void onAccuracyChanged(Sensor sensor, int accuracy) {}
}
diff --git a/platform/android/java/lib/src/org/godotengine/godot/input/InputEventRunnable.java b/platform/android/java/lib/src/org/godotengine/godot/input/InputEventRunnable.java
new file mode 100644
index 0000000000..a282791b2e
--- /dev/null
+++ b/platform/android/java/lib/src/org/godotengine/godot/input/InputEventRunnable.java
@@ -0,0 +1,353 @@
+/**************************************************************************/
+/* InputEventRunnable.java */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* 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. */
+/**************************************************************************/
+
+package org.godotengine.godot.input;
+
+import org.godotengine.godot.GodotLib;
+
+import android.hardware.Sensor;
+import android.util.Log;
+import android.view.MotionEvent;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.core.util.Pools;
+
+import java.util.concurrent.ArrayBlockingQueue;
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * Used to dispatch input events.
+ *
+ * This is a specialized version of @{@link Runnable} which allows to allocate a finite pool of
+ * objects for input events dispatching, thus avoid the creation (and garbage collection) of
+ * spurious @{@link Runnable} objects.
+ */
+final class InputEventRunnable implements Runnable {
+ private static final String TAG = InputEventRunnable.class.getSimpleName();
+
+ private static final int MAX_TOUCH_POINTER_COUNT = 10; // assuming 10 fingers as max supported concurrent touch pointers
+
+ private static final Pools.Pool<InputEventRunnable> POOL = new Pools.Pool<>() {
+ private static final int MAX_POOL_SIZE = 120 * 10; // up to 120Hz input events rate for up to 5 secs (ANR limit) * 2
+
+ private final ArrayBlockingQueue<InputEventRunnable> queue = new ArrayBlockingQueue<>(MAX_POOL_SIZE);
+ private final AtomicInteger createdCount = new AtomicInteger();
+
+ @Nullable
+ @Override
+ public InputEventRunnable acquire() {
+ InputEventRunnable instance = queue.poll();
+ if (instance == null) {
+ int creationCount = createdCount.incrementAndGet();
+ if (creationCount <= MAX_POOL_SIZE) {
+ instance = new InputEventRunnable(creationCount - 1);
+ }
+ }
+
+ return instance;
+ }
+
+ @Override
+ public boolean release(@NonNull InputEventRunnable instance) {
+ return queue.offer(instance);
+ }
+ };
+
+ @Nullable
+ static InputEventRunnable obtain() {
+ InputEventRunnable runnable = POOL.acquire();
+ if (runnable == null) {
+ Log.w(TAG, "Input event pool is at capacity");
+ }
+ return runnable;
+ }
+
+ /**
+ * Used to track when this instance was created and added to the pool. Primarily used for
+ * debug purposes.
+ */
+ private final int creationRank;
+
+ private InputEventRunnable(int creationRank) {
+ this.creationRank = creationRank;
+ }
+
+ /**
+ * Set of supported input events.
+ */
+ private enum EventType {
+ MOUSE,
+ TOUCH,
+ MAGNIFY,
+ PAN,
+ JOYSTICK_BUTTON,
+ JOYSTICK_AXIS,
+ JOYSTICK_HAT,
+ JOYSTICK_CONNECTION_CHANGED,
+ KEY,
+ SENSOR
+ }
+
+ private EventType currentEventType = null;
+
+ // common event fields
+ private float eventX;
+ private float eventY;
+ private float eventDeltaX;
+ private float eventDeltaY;
+ private boolean eventPressed;
+
+ // common touch / mouse fields
+ private int eventAction;
+ private boolean doubleTap;
+
+ // Mouse event fields and setter
+ private int buttonsMask;
+ private boolean sourceMouseRelative;
+ private float pressure;
+ private float tiltX;
+ private float tiltY;
+ void setMouseEvent(int eventAction, int buttonsMask, float x, float y, float deltaX, float deltaY, boolean doubleClick, boolean sourceMouseRelative, float pressure, float tiltX, float tiltY) {
+ this.currentEventType = EventType.MOUSE;
+ this.eventAction = eventAction;
+ this.buttonsMask = buttonsMask;
+ this.eventX = x;
+ this.eventY = y;
+ this.eventDeltaX = deltaX;
+ this.eventDeltaY = deltaY;
+ this.doubleTap = doubleClick;
+ this.sourceMouseRelative = sourceMouseRelative;
+ this.pressure = pressure;
+ this.tiltX = tiltX;
+ this.tiltY = tiltY;
+ }
+
+ // Touch event fields and setter
+ private int actionPointerId;
+ private int pointerCount;
+ private final float[] positions = new float[MAX_TOUCH_POINTER_COUNT * 6]; // pointerId1, x1, y1, pressure1, tiltX1, tiltY1, pointerId2, etc...
+ void setTouchEvent(MotionEvent event, int eventAction, boolean doubleTap) {
+ this.currentEventType = EventType.TOUCH;
+ this.eventAction = eventAction;
+ this.doubleTap = doubleTap;
+ this.actionPointerId = event.getPointerId(event.getActionIndex());
+ this.pointerCount = Math.min(event.getPointerCount(), MAX_TOUCH_POINTER_COUNT);
+ for (int i = 0; i < pointerCount; i++) {
+ positions[i * 6 + 0] = event.getPointerId(i);
+ positions[i * 6 + 1] = event.getX(i);
+ positions[i * 6 + 2] = event.getY(i);
+ positions[i * 6 + 3] = event.getPressure(i);
+ positions[i * 6 + 4] = GodotInputHandler.getEventTiltX(event);
+ positions[i * 6 + 5] = GodotInputHandler.getEventTiltY(event);
+ }
+ }
+
+ // Magnify event fields and setter
+ private float magnifyFactor;
+ void setMagnifyEvent(float x, float y, float factor) {
+ this.currentEventType = EventType.MAGNIFY;
+ this.eventX = x;
+ this.eventY = y;
+ this.magnifyFactor = factor;
+ }
+
+ // Pan event setter
+ void setPanEvent(float x, float y, float deltaX, float deltaY) {
+ this.currentEventType = EventType.PAN;
+ this.eventX = x;
+ this.eventY = y;
+ this.eventDeltaX = deltaX;
+ this.eventDeltaY = deltaY;
+ }
+
+ // common joystick field
+ private int joystickDevice;
+
+ // Joystick button event fields and setter
+ private int button;
+ void setJoystickButtonEvent(int device, int button, boolean pressed) {
+ this.currentEventType = EventType.JOYSTICK_BUTTON;
+ this.joystickDevice = device;
+ this.button = button;
+ this.eventPressed = pressed;
+ }
+
+ // Joystick axis event fields and setter
+ private int axis;
+ private float value;
+ void setJoystickAxisEvent(int device, int axis, float value) {
+ this.currentEventType = EventType.JOYSTICK_AXIS;
+ this.joystickDevice = device;
+ this.axis = axis;
+ this.value = value;
+ }
+
+ // Joystick hat event fields and setter
+ private int hatX;
+ private int hatY;
+ void setJoystickHatEvent(int device, int hatX, int hatY) {
+ this.currentEventType = EventType.JOYSTICK_HAT;
+ this.joystickDevice = device;
+ this.hatX = hatX;
+ this.hatY = hatY;
+ }
+
+ // Joystick connection changed event fields and setter
+ private boolean connected;
+ private String joystickName;
+ void setJoystickConnectionChangedEvent(int device, boolean connected, String name) {
+ this.currentEventType = EventType.JOYSTICK_CONNECTION_CHANGED;
+ this.joystickDevice = device;
+ this.connected = connected;
+ this.joystickName = name;
+ }
+
+ // Key event fields and setter
+ private int physicalKeycode;
+ private int unicode;
+ private int keyLabel;
+ private boolean echo;
+ void setKeyEvent(int physicalKeycode, int unicode, int keyLabel, boolean pressed, boolean echo) {
+ this.currentEventType = EventType.KEY;
+ this.physicalKeycode = physicalKeycode;
+ this.unicode = unicode;
+ this.keyLabel = keyLabel;
+ this.eventPressed = pressed;
+ this.echo = echo;
+ }
+
+ // Sensor event fields and setter
+ private int sensorType;
+ private float rotatedValue0;
+ private float rotatedValue1;
+ private float rotatedValue2;
+ void setSensorEvent(int sensorType, float rotatedValue0, float rotatedValue1, float rotatedValue2) {
+ this.currentEventType = EventType.SENSOR;
+ this.sensorType = sensorType;
+ this.rotatedValue0 = rotatedValue0;
+ this.rotatedValue1 = rotatedValue1;
+ this.rotatedValue2 = rotatedValue2;
+ }
+
+ @Override
+ public void run() {
+ try {
+ if (currentEventType == null) {
+ Log.w(TAG, "Invalid event type");
+ return;
+ }
+
+ switch (currentEventType) {
+ case MOUSE:
+ GodotLib.dispatchMouseEvent(
+ eventAction,
+ buttonsMask,
+ eventX,
+ eventY,
+ eventDeltaX,
+ eventDeltaY,
+ doubleTap,
+ sourceMouseRelative,
+ pressure,
+ tiltX,
+ tiltY);
+ break;
+
+ case TOUCH:
+ GodotLib.dispatchTouchEvent(
+ eventAction,
+ actionPointerId,
+ pointerCount,
+ positions,
+ doubleTap);
+ break;
+
+ case MAGNIFY:
+ GodotLib.magnify(eventX, eventY, magnifyFactor);
+ break;
+
+ case PAN:
+ GodotLib.pan(eventX, eventY, eventDeltaX, eventDeltaY);
+ break;
+
+ case JOYSTICK_BUTTON:
+ GodotLib.joybutton(joystickDevice, button, eventPressed);
+ break;
+
+ case JOYSTICK_AXIS:
+ GodotLib.joyaxis(joystickDevice, axis, value);
+ break;
+
+ case JOYSTICK_HAT:
+ GodotLib.joyhat(joystickDevice, hatX, hatY);
+ break;
+
+ case JOYSTICK_CONNECTION_CHANGED:
+ GodotLib.joyconnectionchanged(joystickDevice, connected, joystickName);
+ break;
+
+ case KEY:
+ GodotLib.key(physicalKeycode, unicode, keyLabel, eventPressed, echo);
+ break;
+
+ case SENSOR:
+ switch (sensorType) {
+ case Sensor.TYPE_ACCELEROMETER:
+ GodotLib.accelerometer(-rotatedValue0, -rotatedValue1, -rotatedValue2);
+ break;
+
+ case Sensor.TYPE_GRAVITY:
+ GodotLib.gravity(-rotatedValue0, -rotatedValue1, -rotatedValue2);
+ break;
+
+ case Sensor.TYPE_MAGNETIC_FIELD:
+ GodotLib.magnetometer(-rotatedValue0, -rotatedValue1, -rotatedValue2);
+ break;
+
+ case Sensor.TYPE_GYROSCOPE:
+ GodotLib.gyroscope(rotatedValue0, rotatedValue1, rotatedValue2);
+ break;
+ }
+ break;
+ }
+ } finally {
+ recycle();
+ }
+ }
+
+ /**
+ * Release the current instance back to the pool
+ */
+ private void recycle() {
+ currentEventType = null;
+ POOL.release(this);
+ }
+}
diff --git a/platform/android/java/lib/src/org/godotengine/godot/plugin/GodotPluginRegistry.java b/platform/android/java/lib/src/org/godotengine/godot/plugin/GodotPluginRegistry.java
index 711bca02e7..8976dd65db 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/plugin/GodotPluginRegistry.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/plugin/GodotPluginRegistry.java
@@ -43,6 +43,7 @@ import androidx.annotation.Nullable;
import java.lang.reflect.Constructor;
import java.util.Collection;
+import java.util.Collections;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
@@ -82,6 +83,9 @@ public final class GodotPluginRegistry {
* Retrieve the full set of loaded plugins.
*/
public Collection<GodotPlugin> getAllPlugins() {
+ if (registry.isEmpty()) {
+ return Collections.emptyList();
+ }
return registry.values();
}
diff --git a/platform/android/java_godot_lib_jni.cpp b/platform/android/java_godot_lib_jni.cpp
index ec42e5bbd8..a4a425f685 100644
--- a/platform/android/java_godot_lib_jni.cpp
+++ b/platform/android/java_godot_lib_jni.cpp
@@ -51,6 +51,7 @@
#include "core/config/project_settings.h"
#include "core/input/input.h"
#include "main/main.h"
+#include "servers/xr_server.h"
#ifdef TOOLS_ENABLED
#include "editor/editor_settings.h"
@@ -270,7 +271,18 @@ JNIEXPORT jboolean JNICALL Java_org_godotengine_godot_GodotLib_step(JNIEnv *env,
}
if (step.get() == STEP_SHOW_LOGO) {
- Main::setup_boot_logo();
+ bool xr_enabled;
+ if (XRServer::get_xr_mode() == XRServer::XRMODE_DEFAULT) {
+ xr_enabled = GLOBAL_GET("xr/shaders/enabled");
+ } else {
+ xr_enabled = XRServer::get_xr_mode() == XRServer::XRMODE_ON;
+ }
+ // Unlike PCVR, there's no additional 2D screen onto which to render the boot logo,
+ // so we skip this step if xr is enabled.
+ if (!xr_enabled) {
+ Main::setup_boot_logo();
+ }
+
step.increment();
return true;
}
diff --git a/platform/android/java_godot_wrapper.cpp b/platform/android/java_godot_wrapper.cpp
index 70ea4b09c1..91bf7b48a6 100644
--- a/platform/android/java_godot_wrapper.cpp
+++ b/platform/android/java_godot_wrapper.cpp
@@ -213,25 +213,27 @@ bool GodotJavaWrapper::has_get_clipboard() {
}
String GodotJavaWrapper::get_clipboard() {
+ String clipboard;
if (_get_clipboard) {
JNIEnv *env = get_jni_env();
ERR_FAIL_NULL_V(env, String());
jstring s = (jstring)env->CallObjectMethod(godot_instance, _get_clipboard);
- return jstring_to_string(s, env);
- } else {
- return String();
+ clipboard = jstring_to_string(s, env);
+ env->DeleteLocalRef(s);
}
+ return clipboard;
}
String GodotJavaWrapper::get_input_fallback_mapping() {
+ String input_fallback_mapping;
if (_get_input_fallback_mapping) {
JNIEnv *env = get_jni_env();
ERR_FAIL_NULL_V(env, String());
jstring fallback_mapping = (jstring)env->CallObjectMethod(godot_instance, _get_input_fallback_mapping);
- return jstring_to_string(fallback_mapping, env);
- } else {
- return String();
+ input_fallback_mapping = jstring_to_string(fallback_mapping, env);
+ env->DeleteLocalRef(fallback_mapping);
}
+ return input_fallback_mapping;
}
bool GodotJavaWrapper::has_set_clipboard() {
@@ -324,14 +326,15 @@ Vector<String> GodotJavaWrapper::get_gdextension_list_config_file() const {
}
String GodotJavaWrapper::get_ca_certificates() const {
+ String ca_certificates;
if (_get_ca_certificates) {
JNIEnv *env = get_jni_env();
ERR_FAIL_NULL_V(env, String());
jstring s = (jstring)env->CallObjectMethod(godot_instance, _get_ca_certificates);
- return jstring_to_string(s, env);
- } else {
- return String();
+ ca_certificates = jstring_to_string(s, env);
+ env->DeleteLocalRef(s);
}
+ return ca_certificates;
}
void GodotJavaWrapper::init_input_devices() {
diff --git a/platform/ios/godot_app_delegate.h b/platform/ios/godot_app_delegate.h
index a9bfcbb0b2..85dc6bb390 100644
--- a/platform/ios/godot_app_delegate.h
+++ b/platform/ios/godot_app_delegate.h
@@ -32,7 +32,7 @@
typedef NSObject<UIApplicationDelegate> ApplicationDelegateService;
-@interface GodotApplicalitionDelegate : NSObject <UIApplicationDelegate>
+@interface GodotApplicationDelegate : NSObject <UIApplicationDelegate>
@property(class, readonly, strong) NSArray<ApplicationDelegateService *> *services;
diff --git a/platform/ios/godot_app_delegate.m b/platform/ios/godot_app_delegate.m
index 74e8705bc3..53e53cd0c6 100644
--- a/platform/ios/godot_app_delegate.m
+++ b/platform/ios/godot_app_delegate.m
@@ -32,11 +32,11 @@
#import "app_delegate.h"
-@interface GodotApplicalitionDelegate ()
+@interface GodotApplicationDelegate ()
@end
-@implementation GodotApplicalitionDelegate
+@implementation GodotApplicationDelegate
static NSMutableArray<ApplicationDelegateService *> *services = nil;
diff --git a/platform/ios/main.m b/platform/ios/main.m
index 33b1034d98..89a00c9ae9 100644
--- a/platform/ios/main.m
+++ b/platform/ios/main.m
@@ -46,7 +46,7 @@ int main(int argc, char *argv[]) {
gargv = argv;
@autoreleasepool {
- NSString *className = NSStringFromClass([GodotApplicalitionDelegate class]);
+ NSString *className = NSStringFromClass([GodotApplicationDelegate class]);
UIApplicationMain(argc, argv, nil, className);
}
return 0;
diff --git a/platform/linuxbsd/export/export_plugin.cpp b/platform/linuxbsd/export/export_plugin.cpp
index 936adddda3..0032b898d2 100644
--- a/platform/linuxbsd/export/export_plugin.cpp
+++ b/platform/linuxbsd/export/export_plugin.cpp
@@ -61,6 +61,20 @@ Error EditorExportPlatformLinuxBSD::_export_debug_script(const Ref<EditorExportP
}
Error EditorExportPlatformLinuxBSD::export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags) {
+ String custom_debug = p_preset->get("custom_template/debug");
+ String custom_release = p_preset->get("custom_template/release");
+ String arch = p_preset->get("binary_format/architecture");
+
+ String template_path = p_debug ? custom_debug : custom_release;
+ template_path = template_path.strip_edges();
+ if (!template_path.is_empty()) {
+ String exe_arch = _get_exe_arch(template_path);
+ if (arch != exe_arch) {
+ add_message(EXPORT_MESSAGE_ERROR, TTR("Prepare Templates"), vformat(TTR("Mismatching custom export template executable architecture, found \"%s\", expected \"%s\"."), exe_arch, arch));
+ return ERR_CANT_CREATE;
+ }
+ }
+
bool export_as_zip = p_path.ends_with("zip");
String pkg_name;
@@ -205,8 +219,76 @@ bool EditorExportPlatformLinuxBSD::is_executable(const String &p_path) const {
return is_elf(p_path) || is_shebang(p_path);
}
+bool EditorExportPlatformLinuxBSD::has_valid_export_configuration(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates, bool p_debug) const {
+ String err;
+ bool valid = EditorExportPlatformPC::has_valid_export_configuration(p_preset, err, r_missing_templates, p_debug);
+
+ String custom_debug = p_preset->get("custom_template/debug").operator String().strip_edges();
+ String custom_release = p_preset->get("custom_template/release").operator String().strip_edges();
+ String arch = p_preset->get("binary_format/architecture");
+
+ if (!custom_debug.is_empty() && FileAccess::exists(custom_debug)) {
+ String exe_arch = _get_exe_arch(custom_debug);
+ if (arch != exe_arch) {
+ err += vformat(TTR("Mismatching custom debug export template executable architecture: found \"%s\", expected \"%s\"."), exe_arch, arch) + "\n";
+ }
+ }
+ if (!custom_release.is_empty() && FileAccess::exists(custom_release)) {
+ String exe_arch = _get_exe_arch(custom_release);
+ if (arch != exe_arch) {
+ err += vformat(TTR("Mismatching custom release export template executable architecture: found \"%s\", expected \"%s\"."), exe_arch, arch) + "\n";
+ }
+ }
+
+ if (!err.is_empty()) {
+ r_error = err;
+ }
+
+ return valid;
+}
+
+String EditorExportPlatformLinuxBSD::_get_exe_arch(const String &p_path) const {
+ Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::READ);
+ if (f.is_null()) {
+ return "invalid";
+ }
+
+ // Read and check ELF magic number.
+ {
+ uint32_t magic = f->get_32();
+ if (magic != 0x464c457f) { // 0x7F + "ELF"
+ return "invalid";
+ }
+ }
+
+ // Process header.
+ int64_t header_pos = f->get_position();
+ f->seek(header_pos + 14);
+ uint16_t machine = f->get_16();
+ f->close();
+
+ switch (machine) {
+ case 0x0003:
+ return "x86_32";
+ case 0x003e:
+ return "x86_64";
+ case 0x0014:
+ return "ppc32";
+ case 0x0015:
+ return "ppc64";
+ case 0x0028:
+ return "arm32";
+ case 0x00b7:
+ return "arm64";
+ case 0x00f3:
+ return "rv64";
+ default:
+ return "unknown";
+ }
+}
+
Error EditorExportPlatformLinuxBSD::fixup_embedded_pck(const String &p_path, int64_t p_embedded_start, int64_t p_embedded_size) {
- // Patch the header of the "pck" section in the ELF file so that it corresponds to the embedded data
+ // Patch the header of the "pck" section in the ELF file so that it corresponds to the embedded data.
Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::READ_WRITE);
if (f.is_null()) {
@@ -214,7 +296,7 @@ Error EditorExportPlatformLinuxBSD::fixup_embedded_pck(const String &p_path, int
return ERR_CANT_OPEN;
}
- // Read and check ELF magic number
+ // Read and check ELF magic number.
{
uint32_t magic = f->get_32();
if (magic != 0x464c457f) { // 0x7F + "ELF"
@@ -223,7 +305,7 @@ Error EditorExportPlatformLinuxBSD::fixup_embedded_pck(const String &p_path, int
}
}
- // Read program architecture bits from class field
+ // Read program architecture bits from class field.
int bits = f->get_8() * 32;
@@ -231,7 +313,7 @@ Error EditorExportPlatformLinuxBSD::fixup_embedded_pck(const String &p_path, int
add_message(EXPORT_MESSAGE_ERROR, TTR("PCK Embedding"), TTR("32-bit executables cannot have embedded data >= 4 GiB."));
}
- // Get info about the section header table
+ // Get info about the section header table.
int64_t section_table_pos;
int64_t section_header_size;
@@ -249,13 +331,13 @@ Error EditorExportPlatformLinuxBSD::fixup_embedded_pck(const String &p_path, int
int num_sections = f->get_16();
int string_section_idx = f->get_16();
- // Load the strings table
+ // Load the strings table.
uint8_t *strings;
{
- // Jump to the strings section header
+ // Jump to the strings section header.
f->seek(section_table_pos + string_section_idx * section_header_size);
- // Read strings data size and offset
+ // Read strings data size and offset.
int64_t string_data_pos;
int64_t string_data_size;
if (bits == 32) {
@@ -268,7 +350,7 @@ Error EditorExportPlatformLinuxBSD::fixup_embedded_pck(const String &p_path, int
string_data_size = f->get_64();
}
- // Read strings data
+ // Read strings data.
f->seek(string_data_pos);
strings = (uint8_t *)memalloc(string_data_size);
if (!strings) {
@@ -277,7 +359,7 @@ Error EditorExportPlatformLinuxBSD::fixup_embedded_pck(const String &p_path, int
f->get_buffer(strings, string_data_size);
}
- // Search for the "pck" section
+ // Search for the "pck" section.
bool found = false;
for (int i = 0; i < num_sections; ++i) {
diff --git a/platform/linuxbsd/export/export_plugin.h b/platform/linuxbsd/export/export_plugin.h
index 21bd81ed2f..bbc55b82ce 100644
--- a/platform/linuxbsd/export/export_plugin.h
+++ b/platform/linuxbsd/export/export_plugin.h
@@ -69,11 +69,13 @@ class EditorExportPlatformLinuxBSD : public EditorExportPlatformPC {
bool is_shebang(const String &p_path) const;
Error _export_debug_script(const Ref<EditorExportPreset> &p_preset, const String &p_app_name, const String &p_pkg_name, const String &p_path);
+ String _get_exe_arch(const String &p_path) const;
public:
virtual void get_export_options(List<ExportOption> *r_options) const override;
virtual List<String> get_binary_extensions(const Ref<EditorExportPreset> &p_preset) const override;
virtual bool get_export_option_visibility(const EditorExportPreset *p_preset, const String &p_option) const override;
+ virtual bool has_valid_export_configuration(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates, bool p_debug = false) const override;
virtual Error export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags = 0) override;
virtual String get_template_file_name(const String &p_target, const String &p_arch) const override;
virtual Error fixup_embedded_pck(const String &p_path, int64_t p_embedded_start, int64_t p_embedded_size) override;
diff --git a/platform/macos/export/export_plugin.cpp b/platform/macos/export/export_plugin.cpp
index 2ff02d2e74..057fb4ec16 100644
--- a/platform/macos/export/export_plugin.cpp
+++ b/platform/macos/export/export_plugin.cpp
@@ -1092,7 +1092,7 @@ void EditorExportPlatformMacOS::_code_sign(const Ref<EditorExportPreset> &p_pres
List<String> args;
args.push_back("sign");
- if (p_path.get_extension() != "dmg") {
+ if (!p_ent_path.is_empty()) {
args.push_back("--entitlements-xml-path");
args.push_back(p_ent_path);
}
@@ -1153,7 +1153,7 @@ void EditorExportPlatformMacOS::_code_sign(const Ref<EditorExportPreset> &p_pres
args.push_back("runtime");
}
- if (p_path.get_extension() != "dmg") {
+ if (!p_ent_path.is_empty()) {
args.push_back("--entitlements");
args.push_back(p_ent_path);
}
@@ -1237,7 +1237,7 @@ void EditorExportPlatformMacOS::_code_sign_directory(const Ref<EditorExportPrese
}
if (extensions_to_sign.has(current_file.get_extension())) {
- String ent_path = p_ent_path;
+ String ent_path;
bool set_bundle_id = false;
if (sandbox && FileAccess::exists(current_file_path)) {
int ftype = MachO::get_filetype(current_file_path);
@@ -1357,7 +1357,7 @@ Error EditorExportPlatformMacOS::_copy_and_sign_files(Ref<DirAccess> &dir_access
_code_sign_directory(p_preset, p_in_app_path, p_ent_path, p_helper_ent_path, p_should_error_on_non_code_sign);
} else {
if (extensions_to_sign.has(p_in_app_path.get_extension())) {
- String ent_path = p_ent_path;
+ String ent_path;
bool set_bundle_id = false;
if (p_sandbox && FileAccess::exists(p_in_app_path)) {
int ftype = MachO::get_filetype(p_in_app_path);
diff --git a/platform/web/audio_driver_web.cpp b/platform/web/audio_driver_web.cpp
index 22487d2756..5e046d7050 100644
--- a/platform/web/audio_driver_web.cpp
+++ b/platform/web/audio_driver_web.cpp
@@ -312,6 +312,11 @@ bool AudioDriverWeb::is_sample_playback_active(const Ref<AudioSamplePlayback> &p
return godot_audio_sample_is_active(itos(p_playback->get_instance_id()).utf8().get_data()) != 0;
}
+double AudioDriverWeb::get_sample_playback_position(const Ref<AudioSamplePlayback> &p_playback) {
+ ERR_FAIL_COND_V_MSG(p_playback.is_null(), false, "Parameter p_playback is null.");
+ return godot_audio_get_sample_playback_position(itos(p_playback->get_instance_id()).utf8().get_data());
+}
+
void AudioDriverWeb::update_sample_playback_pitch_scale(const Ref<AudioSamplePlayback> &p_playback, float p_pitch_scale) {
ERR_FAIL_COND_MSG(p_playback.is_null(), "Parameter p_playback is null.");
godot_audio_sample_update_pitch_scale(
diff --git a/platform/web/audio_driver_web.h b/platform/web/audio_driver_web.h
index 46c5ce4de1..d352fa4692 100644
--- a/platform/web/audio_driver_web.h
+++ b/platform/web/audio_driver_web.h
@@ -96,6 +96,7 @@ public:
virtual void stop_sample_playback(const Ref<AudioSamplePlayback> &p_playback) override;
virtual void set_sample_playback_pause(const Ref<AudioSamplePlayback> &p_playback, bool p_paused) override;
virtual bool is_sample_playback_active(const Ref<AudioSamplePlayback> &p_playback) override;
+ virtual double get_sample_playback_position(const Ref<AudioSamplePlayback> &p_playback) override;
virtual void update_sample_playback_pitch_scale(const Ref<AudioSamplePlayback> &p_playback, float p_pitch_scale = 0.0f) override;
virtual void set_sample_playback_bus_volumes_linear(const Ref<AudioSamplePlayback> &p_playback, const HashMap<StringName, Vector<AudioFrame>> &p_bus_volumes) override;
diff --git a/platform/web/display_server_web.cpp b/platform/web/display_server_web.cpp
index 40de4e523b..4e55cc137a 100644
--- a/platform/web/display_server_web.cpp
+++ b/platform/web/display_server_web.cpp
@@ -902,8 +902,10 @@ void DisplayServerWeb::process_joypads() {
for (int b = 0; b < s_btns_num; b++) {
// Buttons 6 and 7 in the standard mapping need to be
// axis to be handled as JoyAxis::TRIGGER by Godot.
- if (s_standard && (b == 6 || b == 7)) {
- input->joy_axis(idx, (JoyAxis)b, s_btns[b]);
+ if (s_standard && (b == 6)) {
+ input->joy_axis(idx, JoyAxis::TRIGGER_LEFT, s_btns[b]);
+ } else if (s_standard && (b == 7)) {
+ input->joy_axis(idx, JoyAxis::TRIGGER_RIGHT, s_btns[b]);
} else {
input->joy_button(idx, (JoyButton)b, s_btns[b]);
}
diff --git a/platform/web/emscripten_helpers.py b/platform/web/emscripten_helpers.py
index 2cee3e8110..8fcabb21c7 100644
--- a/platform/web/emscripten_helpers.py
+++ b/platform/web/emscripten_helpers.py
@@ -51,11 +51,13 @@ def create_template_zip(env, js, wasm, worker, side):
js,
wasm,
"#platform/web/js/libs/audio.worklet.js",
+ "#platform/web/js/libs/audio.position.worklet.js",
]
out_files = [
zip_dir.File(binary_name + ".js"),
zip_dir.File(binary_name + ".wasm"),
zip_dir.File(binary_name + ".audio.worklet.js"),
+ zip_dir.File(binary_name + ".audio.position.worklet.js"),
]
if env["threads"]:
in_files.append(worker)
@@ -74,6 +76,7 @@ def create_template_zip(env, js, wasm, worker, side):
"offline.html",
"godot.editor.js",
"godot.editor.audio.worklet.js",
+ "godot.editor.audio.position.worklet.js",
"logo.svg",
"favicon.png",
]
diff --git a/platform/web/export/export_plugin.cpp b/platform/web/export/export_plugin.cpp
index d83e465e8e..d8c1b6033d 100644
--- a/platform/web/export/export_plugin.cpp
+++ b/platform/web/export/export_plugin.cpp
@@ -242,6 +242,7 @@ Error EditorExportPlatformWeb::_build_pwa(const Ref<EditorExportPreset> &p_prese
}
cache_files.push_back(name + ".worker.js");
cache_files.push_back(name + ".audio.worklet.js");
+ cache_files.push_back(name + ".audio.position.worklet.js");
replaces["___GODOT_CACHE___"] = Variant(cache_files).to_json_string();
// Heavy files that are cached on demand.
@@ -835,6 +836,7 @@ Error EditorExportPlatformWeb::_export_project(const Ref<EditorExportPreset> &p_
DirAccess::remove_file_or_error(basepath + ".js");
DirAccess::remove_file_or_error(basepath + ".worker.js");
DirAccess::remove_file_or_error(basepath + ".audio.worklet.js");
+ DirAccess::remove_file_or_error(basepath + ".audio.position.worklet.js");
DirAccess::remove_file_or_error(basepath + ".service.worker.js");
DirAccess::remove_file_or_error(basepath + ".pck");
DirAccess::remove_file_or_error(basepath + ".png");
diff --git a/platform/web/godot_audio.h b/platform/web/godot_audio.h
index dd5bec00cf..4961ebd2bb 100644
--- a/platform/web/godot_audio.h
+++ b/platform/web/godot_audio.h
@@ -55,6 +55,7 @@ extern void godot_audio_sample_start(const char *p_playback_object_id, const cha
extern void godot_audio_sample_stop(const char *p_playback_object_id);
extern void godot_audio_sample_set_pause(const char *p_playback_object_id, bool p_pause);
extern int godot_audio_sample_is_active(const char *p_playback_object_id);
+extern double godot_audio_get_sample_playback_position(const char *p_playback_object_id);
extern void godot_audio_sample_update_pitch_scale(const char *p_playback_object_id, float p_pitch_scale);
extern void godot_audio_sample_set_volumes_linear(const char *p_playback_object_id, int *p_buses_buf, int p_buses_size, float *p_volumes_buf, int p_volumes_size);
extern void godot_audio_sample_set_finished_callback(void (*p_callback)(const char *));
diff --git a/platform/web/js/engine/config.js b/platform/web/js/engine/config.js
index 8c4e1b1b24..61b488cf81 100644
--- a/platform/web/js/engine/config.js
+++ b/platform/web/js/engine/config.js
@@ -299,6 +299,8 @@ const InternalConfig = function (initConfig) { // eslint-disable-line no-unused-
return `${loadPath}.worker.js`;
} else if (path.endsWith('.audio.worklet.js')) {
return `${loadPath}.audio.worklet.js`;
+ } else if (path.endsWith('.audio.position.worklet.js')) {
+ return `${loadPath}.audio.position.worklet.js`;
} else if (path.endsWith('.js')) {
return `${loadPath}.js`;
} else if (path in gdext) {
diff --git a/platform/web/js/libs/audio.position.worklet.js b/platform/web/js/libs/audio.position.worklet.js
new file mode 100644
index 0000000000..bf3ac4ae2d
--- /dev/null
+++ b/platform/web/js/libs/audio.position.worklet.js
@@ -0,0 +1,50 @@
+/**************************************************************************/
+/* godot.audio.position.worklet.js */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* 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. */
+/**************************************************************************/
+
+class GodotPositionReportingProcessor extends AudioWorkletProcessor {
+ constructor() {
+ super();
+ this.position = 0;
+ }
+
+ process(inputs, _outputs, _parameters) {
+ if (inputs.length > 0) {
+ const input = inputs[0];
+ if (input.length > 0) {
+ this.position += input[0].length;
+ this.port.postMessage({ 'type': 'position', 'data': this.position });
+ return true;
+ }
+ }
+ return true;
+ }
+}
+
+registerProcessor('godot-position-reporting-processor', GodotPositionReportingProcessor);
diff --git a/platform/web/js/libs/library_godot_audio.js b/platform/web/js/libs/library_godot_audio.js
index 8b7c572196..0ba6eed464 100644
--- a/platform/web/js/libs/library_godot_audio.js
+++ b/platform/web/js/libs/library_godot_audio.js
@@ -330,6 +330,7 @@ class SampleNodeBus {
* startTime?: number
* loopMode?: LoopMode
* volume?: Float32Array
+ * start?: boolean
* }} SampleNodeOptions
*/
@@ -421,9 +422,15 @@ class SampleNode {
/** @type {number} */
this.offset = options.offset ?? 0;
/** @type {number} */
+ this._playbackPosition = options.offset;
+ /** @type {number} */
this.startTime = options.startTime ?? 0;
/** @type {boolean} */
this.isPaused = false;
+ /** @type {boolean} */
+ this.isStarted = false;
+ /** @type {boolean} */
+ this.isCanceled = false;
/** @type {number} */
this.pauseTime = 0;
/** @type {number} */
@@ -440,6 +447,8 @@ class SampleNode {
this._source = GodotAudio.ctx.createBufferSource();
this._onended = null;
+ /** @type {AudioWorkletNode | null} */
+ this._positionWorklet = null;
this.setPlaybackRate(options.playbackRate ?? 44100);
this._source.buffer = this.getSample().getAudioBuffer();
@@ -449,6 +458,8 @@ class SampleNode {
const bus = GodotAudio.Bus.getBus(params.busIndex);
const sampleNodeBus = this.getSampleNodeBus(bus);
sampleNodeBus.setVolume(options.volume);
+
+ this.connectPositionWorklet(options.start);
}
/**
@@ -460,6 +471,14 @@ class SampleNode {
}
/**
+ * Gets the playback position.
+ * @returns {number}
+ */
+ getPlaybackPosition() {
+ return this._playbackPosition;
+ }
+
+ /**
* Sets the playback rate.
* @param {number} val Value to set.
* @returns {void}
@@ -508,8 +527,12 @@ class SampleNode {
* @returns {void}
*/
start() {
+ if (this.isStarted) {
+ return;
+ }
this._resetSourceStartTime();
this._source.start(this.startTime, this.offset);
+ this.isStarted = true;
}
/**
@@ -585,17 +608,73 @@ class SampleNode {
}
/**
+ * Sets up and connects the source to the GodotPositionReportingProcessor
+ * If the worklet module is not loaded in, it will be added
+ */
+ connectPositionWorklet(start) {
+ try {
+ this._positionWorklet = this.createPositionWorklet();
+ this._source.connect(this._positionWorklet);
+ if (start) {
+ this.start();
+ }
+ } catch (error) {
+ if (error?.name !== 'InvalidStateError') {
+ throw error;
+ }
+ const path = GodotConfig.locate_file('godot.audio.position.worklet.js');
+ GodotAudio.ctx.audioWorklet
+ .addModule(path)
+ .then(() => {
+ if (!this.isCanceled) {
+ this._positionWorklet = this.createPositionWorklet();
+ this._source.connect(this._positionWorklet);
+ if (start) {
+ this.start();
+ }
+ }
+ }).catch((addModuleError) => {
+ GodotRuntime.error('Failed to create PositionWorklet.', addModuleError);
+ });
+ }
+ }
+
+ /**
+ * Creates the AudioWorkletProcessor used to track playback position.
+ * @returns {AudioWorkletNode}
+ */
+ createPositionWorklet() {
+ const worklet = new AudioWorkletNode(
+ GodotAudio.ctx,
+ 'godot-position-reporting-processor'
+ );
+ worklet.port.onmessage = (event) => {
+ switch (event.data['type']) {
+ case 'position':
+ this._playbackPosition = (parseInt(event.data.data, 10) / this.getSample().sampleRate) + this.offset;
+ break;
+ default:
+ // Do nothing.
+ }
+ };
+ return worklet;
+ }
+
+ /**
* Clears the `SampleNode`.
* @returns {void}
*/
clear() {
+ this.isCanceled = true;
this.isPaused = false;
this.pauseTime = 0;
if (this._source != null) {
this._source.removeEventListener('ended', this._onended);
this._onended = null;
- this._source.stop();
+ if (this.isStarted) {
+ this._source.stop();
+ }
this._source.disconnect();
this._source = null;
}
@@ -605,6 +684,12 @@ class SampleNode {
}
this._sampleNodeBuses.clear();
+ if (this._positionWorklet) {
+ this._positionWorklet.disconnect();
+ this._positionWorklet.port.onmessage = null;
+ this._positionWorklet = null;
+ }
+
GodotAudio.SampleNode.delete(this.id);
}
@@ -645,7 +730,9 @@ class SampleNode {
const pauseTime = this.isPaused
? this.pauseTime
: 0;
+ this.connectPositionWorklet();
this._source.start(this.startTime, this.offset + pauseTime);
+ this.isStarted = true;
}
/**
@@ -1262,7 +1349,7 @@ const _GodotAudio = {
startOptions
) {
GodotAudio.SampleNode.stopSampleNode(playbackObjectId);
- const sampleNode = GodotAudio.SampleNode.create(
+ GodotAudio.SampleNode.create(
{
busIndex,
id: playbackObjectId,
@@ -1270,7 +1357,6 @@ const _GodotAudio = {
},
startOptions
);
- sampleNode.start();
},
/**
@@ -1590,6 +1676,7 @@ const _GodotAudio = {
offset,
volume,
playbackRate: 1,
+ start: true,
};
GodotAudio.start_sample(
playbackObjectId,
@@ -1635,6 +1722,22 @@ const _GodotAudio = {
return Number(GodotAudio.sampleNodes.has(playbackObjectId));
},
+ godot_audio_get_sample_playback_position__proxy: 'sync',
+ godot_audio_get_sample_playback_position__sig: 'di',
+ /**
+ * Returns the position of the playback position.
+ * @param {number} playbackObjectIdStrPtr Playback object id pointer
+ * @returns {number}
+ */
+ godot_audio_get_sample_playback_position: function (playbackObjectIdStrPtr) {
+ const playbackObjectId = GodotRuntime.parseString(playbackObjectIdStrPtr);
+ const sampleNode = GodotAudio.SampleNode.getSampleNodeOrNull(playbackObjectId);
+ if (sampleNode == null) {
+ return 0;
+ }
+ return sampleNode.getPlaybackPosition();
+ },
+
godot_audio_sample_update_pitch_scale__proxy: 'sync',
godot_audio_sample_update_pitch_scale__sig: 'vii',
/**
diff --git a/platform/web/serve.py b/platform/web/serve.py
index f0b0ec9622..4e1521449b 100755
--- a/platform/web/serve.py
+++ b/platform/web/serve.py
@@ -6,7 +6,7 @@ import os
import socket
import subprocess
import sys
-from http.server import HTTPServer, SimpleHTTPRequestHandler, test # type: ignore
+from http.server import HTTPServer, SimpleHTTPRequestHandler
from pathlib import Path
@@ -38,12 +38,24 @@ def shell_open(url):
def serve(root, port, run_browser):
os.chdir(root)
+ address = ("", port)
+ httpd = DualStackServer(address, CORSRequestHandler)
+
+ url = f"http://127.0.0.1:{port}"
if run_browser:
# Open the served page in the user's default browser.
- print("Opening the served URL in the default browser (use `--no-browser` or `-n` to disable this).")
- shell_open(f"http://127.0.0.1:{port}")
+ print(f"Opening the served URL in the default browser (use `--no-browser` or `-n` to disable this): {url}")
+ shell_open(url)
+ else:
+ print(f"Serving at: {url}")
- test(CORSRequestHandler, DualStackServer, port=port)
+ try:
+ httpd.serve_forever()
+ except KeyboardInterrupt:
+ print("\nKeyboard interrupt received, stopping server.")
+ finally:
+ # Clean-up server
+ httpd.server_close()
if __name__ == "__main__":
diff --git a/platform/windows/display_server_windows.cpp b/platform/windows/display_server_windows.cpp
index 88ab9a4af0..635e8326e2 100644
--- a/platform/windows/display_server_windows.cpp
+++ b/platform/windows/display_server_windows.cpp
@@ -250,6 +250,14 @@ void DisplayServerWindows::tts_stop() {
tts->stop();
}
+Error DisplayServerWindows::file_dialog_show(const String &p_title, const String &p_current_directory, const String &p_filename, bool p_show_hidden, FileDialogMode p_mode, const Vector<String> &p_filters, const Callable &p_callback) {
+ return _file_dialog_with_options_show(p_title, p_current_directory, String(), p_filename, p_show_hidden, p_mode, p_filters, TypedArray<Dictionary>(), p_callback, false);
+}
+
+Error DisplayServerWindows::file_dialog_with_options_show(const String &p_title, const String &p_current_directory, const String &p_root, const String &p_filename, bool p_show_hidden, FileDialogMode p_mode, const Vector<String> &p_filters, const TypedArray<Dictionary> &p_options, const Callable &p_callback) {
+ return _file_dialog_with_options_show(p_title, p_current_directory, p_root, p_filename, p_show_hidden, p_mode, p_filters, p_options, p_callback, true);
+}
+
// Silence warning due to a COM API weirdness.
#if defined(__GNUC__) && !defined(__clang__)
#pragma GCC diagnostic push
@@ -377,22 +385,85 @@ public:
#pragma GCC diagnostic pop
#endif
-Error DisplayServerWindows::file_dialog_show(const String &p_title, const String &p_current_directory, const String &p_filename, bool p_show_hidden, FileDialogMode p_mode, const Vector<String> &p_filters, const Callable &p_callback) {
- return _file_dialog_with_options_show(p_title, p_current_directory, String(), p_filename, p_show_hidden, p_mode, p_filters, TypedArray<Dictionary>(), p_callback, false);
+LRESULT CALLBACK WndProcFileDialog(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
+ DisplayServerWindows *ds_win = static_cast<DisplayServerWindows *>(DisplayServer::get_singleton());
+ if (ds_win) {
+ return ds_win->WndProcFileDialog(hWnd, uMsg, wParam, lParam);
+ } else {
+ return DefWindowProcW(hWnd, uMsg, wParam, lParam);
+ }
}
-Error DisplayServerWindows::file_dialog_with_options_show(const String &p_title, const String &p_current_directory, const String &p_root, const String &p_filename, bool p_show_hidden, FileDialogMode p_mode, const Vector<String> &p_filters, const TypedArray<Dictionary> &p_options, const Callable &p_callback) {
- return _file_dialog_with_options_show(p_title, p_current_directory, p_root, p_filename, p_show_hidden, p_mode, p_filters, p_options, p_callback, true);
+LRESULT DisplayServerWindows::WndProcFileDialog(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
+ MutexLock lock(file_dialog_mutex);
+ if (file_dialog_wnd.has(hWnd)) {
+ if (file_dialog_wnd[hWnd]->close_requested.is_set()) {
+ IPropertyStore *prop_store;
+ HRESULT hr = SHGetPropertyStoreForWindow(hWnd, IID_IPropertyStore, (void **)&prop_store);
+ if (hr == S_OK) {
+ PROPVARIANT val;
+ PropVariantInit(&val);
+ prop_store->SetValue(PKEY_AppUserModel_ID, val);
+ prop_store->Release();
+ }
+ DestroyWindow(hWnd);
+ file_dialog_wnd.erase(hWnd);
+ }
+ }
+ return DefWindowProcW(hWnd, uMsg, wParam, lParam);
}
-Error DisplayServerWindows::_file_dialog_with_options_show(const String &p_title, const String &p_current_directory, const String &p_root, const String &p_filename, bool p_show_hidden, FileDialogMode p_mode, const Vector<String> &p_filters, const TypedArray<Dictionary> &p_options, const Callable &p_callback, bool p_options_in_cb) {
- _THREAD_SAFE_METHOD_
+void DisplayServerWindows::_thread_fd_monitor(void *p_ud) {
+ DisplayServerWindows *ds = static_cast<DisplayServerWindows *>(get_singleton());
+ FileDialogData *fd = (FileDialogData *)p_ud;
- ERR_FAIL_INDEX_V(int(p_mode), FILE_DIALOG_MODE_SAVE_MAX, FAILED);
+ if (fd->mode < 0 && fd->mode >= DisplayServer::FILE_DIALOG_MODE_SAVE_MAX) {
+ fd->finished.set();
+ return;
+ }
+ CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED);
+
+ int64_t x = fd->wrect.position.x;
+ int64_t y = fd->wrect.position.y;
+ int64_t w = fd->wrect.size.x;
+ int64_t h = fd->wrect.size.y;
+
+ WNDCLASSW wc = {};
+ wc.lpfnWndProc = (WNDPROC)::WndProcFileDialog;
+ wc.hInstance = GetModuleHandle(nullptr);
+ wc.lpszClassName = L"Engine File Dialog";
+ RegisterClassW(&wc);
+
+ HWND hwnd_dialog = CreateWindowExW(WS_EX_APPWINDOW, L"Engine File Dialog", L"", WS_OVERLAPPEDWINDOW, x, y, w, h, nullptr, nullptr, GetModuleHandle(nullptr), nullptr);
+ if (hwnd_dialog) {
+ {
+ MutexLock lock(ds->file_dialog_mutex);
+ ds->file_dialog_wnd[hwnd_dialog] = fd;
+ }
+
+ HICON mainwindow_icon = (HICON)SendMessage(fd->hwnd_owner, WM_GETICON, ICON_SMALL, 0);
+ if (mainwindow_icon) {
+ SendMessage(hwnd_dialog, WM_SETICON, ICON_SMALL, (LPARAM)mainwindow_icon);
+ }
+ mainwindow_icon = (HICON)SendMessage(fd->hwnd_owner, WM_GETICON, ICON_BIG, 0);
+ if (mainwindow_icon) {
+ SendMessage(hwnd_dialog, WM_SETICON, ICON_BIG, (LPARAM)mainwindow_icon);
+ }
+ IPropertyStore *prop_store;
+ HRESULT hr = SHGetPropertyStoreForWindow(hwnd_dialog, IID_IPropertyStore, (void **)&prop_store);
+ if (hr == S_OK) {
+ PROPVARIANT val;
+ InitPropVariantFromString((PCWSTR)fd->appid.utf16().get_data(), &val);
+ prop_store->SetValue(PKEY_AppUserModel_ID, val);
+ prop_store->Release();
+ }
+ }
+
+ SetCurrentProcessExplicitAppUserModelID((PCWSTR)fd->appid.utf16().get_data());
Vector<Char16String> filter_names;
Vector<Char16String> filter_exts;
- for (const String &E : p_filters) {
+ for (const String &E : fd->filters) {
Vector<String> tokens = E.split(";");
if (tokens.size() >= 1) {
String flt = tokens[0].strip_edges();
@@ -425,11 +496,9 @@ Error DisplayServerWindows::_file_dialog_with_options_show(const String &p_title
filters.push_back({ (LPCWSTR)filter_names[i].ptr(), (LPCWSTR)filter_exts[i].ptr() });
}
- WindowID prev_focus = last_focused_window;
-
HRESULT hr = S_OK;
IFileDialog *pfd = nullptr;
- if (p_mode == FILE_DIALOG_MODE_SAVE_FILE) {
+ if (fd->mode == DisplayServer::FILE_DIALOG_MODE_SAVE_FILE) {
hr = CoCreateInstance(CLSID_FileSaveDialog, nullptr, CLSCTX_INPROC_SERVER, IID_IFileSaveDialog, (void **)&pfd);
} else {
hr = CoCreateInstance(CLSID_FileOpenDialog, nullptr, CLSCTX_INPROC_SERVER, IID_IFileOpenDialog, (void **)&pfd);
@@ -445,40 +514,32 @@ Error DisplayServerWindows::_file_dialog_with_options_show(const String &p_title
IFileDialogCustomize *pfdc = nullptr;
hr = pfd->QueryInterface(IID_PPV_ARGS(&pfdc));
- for (int i = 0; i < p_options.size(); i++) {
- const Dictionary &item = p_options[i];
+ for (int i = 0; i < fd->options.size(); i++) {
+ const Dictionary &item = fd->options[i];
if (!item.has("name") || !item.has("values") || !item.has("default")) {
continue;
}
- const String &name = item["name"];
- const Vector<String> &options = item["values"];
- int default_idx = item["default"];
-
- event_handler->add_option(pfdc, name, options, default_idx);
+ event_handler->add_option(pfdc, item["name"], item["values"], item["default_idx"]);
}
- event_handler->set_root(p_root);
+ event_handler->set_root(fd->root);
pfdc->Release();
DWORD flags;
pfd->GetOptions(&flags);
- if (p_mode == FILE_DIALOG_MODE_OPEN_FILES) {
+ if (fd->mode == DisplayServer::FILE_DIALOG_MODE_OPEN_FILES) {
flags |= FOS_ALLOWMULTISELECT;
}
- if (p_mode == FILE_DIALOG_MODE_OPEN_DIR) {
+ if (fd->mode == DisplayServer::FILE_DIALOG_MODE_OPEN_DIR) {
flags |= FOS_PICKFOLDERS;
}
- if (p_show_hidden) {
+ if (fd->show_hidden) {
flags |= FOS_FORCESHOWHIDDEN;
}
pfd->SetOptions(flags | FOS_FORCEFILESYSTEM);
- pfd->SetTitle((LPCWSTR)p_title.utf16().ptr());
+ pfd->SetTitle((LPCWSTR)fd->title.utf16().ptr());
- String dir = ProjectSettings::get_singleton()->globalize_path(p_current_directory);
- if (dir == ".") {
- dir = OS::get_singleton()->get_executable_path().get_base_dir();
- }
- dir = dir.replace("/", "\\");
+ String dir = fd->current_directory.replace("/", "\\");
IShellItem *shellitem = nullptr;
hr = SHCreateItemFromParsingName((LPCWSTR)dir.utf16().ptr(), nullptr, IID_IShellItem, (void **)&shellitem);
@@ -487,16 +548,11 @@ Error DisplayServerWindows::_file_dialog_with_options_show(const String &p_title
pfd->SetFolder(shellitem);
}
- pfd->SetFileName((LPCWSTR)p_filename.utf16().ptr());
+ pfd->SetFileName((LPCWSTR)fd->filename.utf16().ptr());
pfd->SetFileTypes(filters.size(), filters.ptr());
pfd->SetFileTypeIndex(0);
- WindowID window_id = _get_focused_window_or_popup();
- if (!windows.has(window_id)) {
- window_id = MAIN_WINDOW_ID;
- }
-
- hr = pfd->Show(windows[window_id].hWnd);
+ hr = pfd->Show(hwnd_dialog);
pfd->Unadvise(cookie);
Dictionary options = event_handler->get_selected();
@@ -513,7 +569,7 @@ Error DisplayServerWindows::_file_dialog_with_options_show(const String &p_title
if (SUCCEEDED(hr)) {
Vector<String> file_names;
- if (p_mode == FILE_DIALOG_MODE_OPEN_FILES) {
+ if (fd->mode == DisplayServer::FILE_DIALOG_MODE_OPEN_FILES) {
IShellItemArray *results;
hr = static_cast<IFileOpenDialog *>(pfd)->GetResults(&results);
if (SUCCEEDED(hr)) {
@@ -546,73 +602,148 @@ Error DisplayServerWindows::_file_dialog_with_options_show(const String &p_title
result->Release();
}
}
- if (p_callback.is_valid()) {
- if (p_options_in_cb) {
+ if (fd->callback.is_valid()) {
+ if (fd->options_in_cb) {
Variant v_result = true;
Variant v_files = file_names;
Variant v_index = index;
Variant v_opt = options;
- Variant ret;
- Callable::CallError ce;
- const Variant *args[4] = { &v_result, &v_files, &v_index, &v_opt };
+ const Variant *cb_args[4] = { &v_result, &v_files, &v_index, &v_opt };
- p_callback.callp(args, 4, ret, ce);
- if (ce.error != Callable::CallError::CALL_OK) {
- ERR_PRINT(vformat("Failed to execute file dialogs callback: %s.", Variant::get_callable_error_text(p_callback, args, 4, ce)));
- }
+ fd->callback.call_deferredp(cb_args, 4);
} else {
Variant v_result = true;
Variant v_files = file_names;
Variant v_index = index;
- Variant ret;
- Callable::CallError ce;
- const Variant *args[3] = { &v_result, &v_files, &v_index };
+ const Variant *cb_args[3] = { &v_result, &v_files, &v_index };
- p_callback.callp(args, 3, ret, ce);
- if (ce.error != Callable::CallError::CALL_OK) {
- ERR_PRINT(vformat("Failed to execute file dialogs callback: %s.", Variant::get_callable_error_text(p_callback, args, 3, ce)));
- }
+ fd->callback.call_deferredp(cb_args, 3);
}
}
} else {
- if (p_callback.is_valid()) {
- if (p_options_in_cb) {
+ if (fd->callback.is_valid()) {
+ if (fd->options_in_cb) {
Variant v_result = false;
Variant v_files = Vector<String>();
- Variant v_index = index;
- Variant v_opt = options;
- Variant ret;
- Callable::CallError ce;
- const Variant *args[4] = { &v_result, &v_files, &v_index, &v_opt };
+ Variant v_index = 0;
+ Variant v_opt = Dictionary();
+ const Variant *cb_args[4] = { &v_result, &v_files, &v_index, &v_opt };
- p_callback.callp(args, 4, ret, ce);
- if (ce.error != Callable::CallError::CALL_OK) {
- ERR_PRINT(vformat("Failed to execute file dialogs callback: %s.", Variant::get_callable_error_text(p_callback, args, 4, ce)));
- }
+ fd->callback.call_deferredp(cb_args, 4);
} else {
Variant v_result = false;
Variant v_files = Vector<String>();
- Variant v_index = index;
- Variant ret;
- Callable::CallError ce;
- const Variant *args[3] = { &v_result, &v_files, &v_index };
+ Variant v_index = 0;
+ const Variant *cb_args[3] = { &v_result, &v_files, &v_index };
- p_callback.callp(args, 3, ret, ce);
- if (ce.error != Callable::CallError::CALL_OK) {
- ERR_PRINT(vformat("Failed to execute file dialogs callback: %s.", Variant::get_callable_error_text(p_callback, args, 3, ce)));
- }
+ fd->callback.call_deferredp(cb_args, 3);
}
}
}
pfd->Release();
- if (prev_focus != INVALID_WINDOW_ID) {
- callable_mp(DisplayServer::get_singleton(), &DisplayServer::window_move_to_foreground).call_deferred(prev_focus);
+ } else {
+ if (fd->callback.is_valid()) {
+ if (fd->options_in_cb) {
+ Variant v_result = false;
+ Variant v_files = Vector<String>();
+ Variant v_index = 0;
+ Variant v_opt = Dictionary();
+ const Variant *cb_args[4] = { &v_result, &v_files, &v_index, &v_opt };
+
+ fd->callback.call_deferredp(cb_args, 4);
+ } else {
+ Variant v_result = false;
+ Variant v_files = Vector<String>();
+ Variant v_index = 0;
+ const Variant *cb_args[3] = { &v_result, &v_files, &v_index };
+
+ fd->callback.call_deferredp(cb_args, 3);
+ }
}
+ }
+ {
+ MutexLock lock(ds->file_dialog_mutex);
+ if (hwnd_dialog && ds->file_dialog_wnd.has(hwnd_dialog)) {
+ IPropertyStore *prop_store;
+ hr = SHGetPropertyStoreForWindow(hwnd_dialog, IID_IPropertyStore, (void **)&prop_store);
+ if (hr == S_OK) {
+ PROPVARIANT val;
+ PropVariantInit(&val);
+ prop_store->SetValue(PKEY_AppUserModel_ID, val);
+ prop_store->Release();
+ }
+ DestroyWindow(hwnd_dialog);
+ ds->file_dialog_wnd.erase(hwnd_dialog);
+ }
+ }
+ UnregisterClassW(L"Engine File Dialog", GetModuleHandle(nullptr));
+ CoUninitialize();
+
+ fd->finished.set();
+
+ if (fd->window_id != INVALID_WINDOW_ID) {
+ callable_mp(DisplayServer::get_singleton(), &DisplayServer::window_move_to_foreground).call_deferred(fd->window_id);
+ }
+}
+
+Error DisplayServerWindows::_file_dialog_with_options_show(const String &p_title, const String &p_current_directory, const String &p_root, const String &p_filename, bool p_show_hidden, FileDialogMode p_mode, const Vector<String> &p_filters, const TypedArray<Dictionary> &p_options, const Callable &p_callback, bool p_options_in_cb) {
+ _THREAD_SAFE_METHOD_
+
+ ERR_FAIL_INDEX_V(int(p_mode), FILE_DIALOG_MODE_SAVE_MAX, FAILED);
+
+ WindowID window_id = _get_focused_window_or_popup();
+ if (!windows.has(window_id)) {
+ window_id = MAIN_WINDOW_ID;
+ }
+ String appname;
+ if (Engine::get_singleton()->is_editor_hint()) {
+ appname = "Godot.GodotEditor." + String(VERSION_BRANCH);
+ } else {
+ String name = GLOBAL_GET("application/config/name");
+ String version = GLOBAL_GET("application/config/version");
+ if (version.is_empty()) {
+ version = "0";
+ }
+ String clean_app_name = name.to_pascal_case();
+ for (int i = 0; i < clean_app_name.length(); i++) {
+ if (!is_ascii_alphanumeric_char(clean_app_name[i]) && clean_app_name[i] != '_' && clean_app_name[i] != '.') {
+ clean_app_name[i] = '_';
+ }
+ }
+ clean_app_name = clean_app_name.substr(0, 120 - version.length()).trim_suffix(".");
+ appname = "Godot." + clean_app_name + "." + version;
+ }
- return OK;
+ FileDialogData *fd = memnew(FileDialogData);
+ if (window_id != INVALID_WINDOW_ID) {
+ fd->hwnd_owner = windows[window_id].hWnd;
+ RECT crect;
+ GetWindowRect(fd->hwnd_owner, &crect);
+ fd->wrect = Rect2i(crect.left, crect.top, crect.right - crect.left, crect.bottom - crect.top);
} else {
- return ERR_CANT_OPEN;
+ fd->hwnd_owner = 0;
+ fd->wrect = Rect2i(CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT);
}
+ fd->appid = appname;
+ fd->title = p_title;
+ fd->current_directory = p_current_directory;
+ fd->root = p_root;
+ fd->filename = p_filename;
+ fd->show_hidden = p_show_hidden;
+ fd->mode = p_mode;
+ fd->window_id = window_id;
+ fd->filters = p_filters;
+ fd->options = p_options;
+ fd->callback = p_callback;
+ fd->options_in_cb = p_options_in_cb;
+ fd->finished.clear();
+ fd->close_requested.clear();
+
+ fd->listener_thread.start(DisplayServerWindows::_thread_fd_monitor, fd);
+
+ file_dialogs.push_back(fd);
+
+ return OK;
}
void DisplayServerWindows::mouse_set_mode(MouseMode p_mode) {
@@ -3022,6 +3153,21 @@ void DisplayServerWindows::process_events() {
_process_key_events();
Input::get_singleton()->flush_buffered_events();
}
+
+ LocalVector<List<FileDialogData *>::Element *> to_remove;
+ for (List<FileDialogData *>::Element *E = file_dialogs.front(); E; E = E->next()) {
+ FileDialogData *fd = E->get();
+ if (fd->finished.is_set()) {
+ if (fd->listener_thread.is_started()) {
+ fd->listener_thread.wait_to_finish();
+ }
+ to_remove.push_back(E);
+ }
+ }
+ for (List<FileDialogData *>::Element *E : to_remove) {
+ memdelete(E->get());
+ E->erase();
+ }
}
void DisplayServerWindows::force_process_and_drop_events() {
@@ -5612,6 +5758,8 @@ DisplayServer::WindowID DisplayServerWindows::_create_window(WindowMode p_mode,
return id;
}
+BitField<DisplayServerWindows::DriverID> DisplayServerWindows::tested_drivers = 0;
+
// WinTab API.
bool DisplayServerWindows::wintab_available = false;
WTOpenPtr DisplayServerWindows::wintab_WTOpen = nullptr;
@@ -5701,12 +5849,6 @@ Vector2i _get_device_ids(const String &p_device_name) {
return ids;
}
-typedef enum _SHC_PROCESS_DPI_AWARENESS {
- SHC_PROCESS_DPI_UNAWARE = 0,
- SHC_PROCESS_SYSTEM_DPI_AWARE = 1,
- SHC_PROCESS_PER_MONITOR_DPI_AWARE = 2
-} SHC_PROCESS_DPI_AWARENESS;
-
bool DisplayServerWindows::is_dark_mode_supported() const {
return ux_theme_available;
}
@@ -5774,6 +5916,8 @@ void DisplayServerWindows::tablet_set_current_driver(const String &p_driver) {
DisplayServerWindows::DisplayServerWindows(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, int p_screen, Context p_context, Error &r_error) {
KeyMappingWindows::initialize();
+ tested_drivers.clear();
+
drop_events = false;
key_event_pos = 0;
@@ -5942,7 +6086,6 @@ DisplayServerWindows::DisplayServerWindows(const String &p_rendering_driver, Win
wc.lpszClassName = L"Engine";
if (!RegisterClassExW(&wc)) {
- MessageBoxW(nullptr, L"Failed To Register The Window Class.", L"ERROR", MB_OK | MB_ICONEXCLAMATION);
r_error = ERR_UNAVAILABLE;
return;
}
@@ -5953,11 +6096,13 @@ DisplayServerWindows::DisplayServerWindows(const String &p_rendering_driver, Win
#if defined(VULKAN_ENABLED)
if (rendering_driver == "vulkan") {
rendering_context = memnew(RenderingContextDriverVulkanWindows);
+ tested_drivers.set_flag(DRIVER_ID_RD_VULKAN);
}
#endif
#if defined(D3D12_ENABLED)
if (rendering_driver == "d3d12") {
rendering_context = memnew(RenderingContextDriverD3D12);
+ tested_drivers.set_flag(DRIVER_ID_RD_D3D12);
}
#endif
@@ -5969,6 +6114,7 @@ DisplayServerWindows::DisplayServerWindows(const String &p_rendering_driver, Win
if (failed && fallback_to_vulkan && rendering_driver != "vulkan") {
memdelete(rendering_context);
rendering_context = memnew(RenderingContextDriverVulkanWindows);
+ tested_drivers.set_flag(DRIVER_ID_RD_VULKAN);
if (rendering_context->initialize() == OK) {
WARN_PRINT("Your video card drivers seem not to support Direct3D 12, switching to Vulkan.");
rendering_driver = "vulkan";
@@ -5981,6 +6127,7 @@ DisplayServerWindows::DisplayServerWindows(const String &p_rendering_driver, Win
if (failed && fallback_to_d3d12 && rendering_driver != "d3d12") {
memdelete(rendering_context);
rendering_context = memnew(RenderingContextDriverD3D12);
+ tested_drivers.set_flag(DRIVER_ID_RD_D3D12);
if (rendering_context->initialize() == OK) {
WARN_PRINT("Your video card drivers seem not to support Vulkan, switching to Direct3D 12.");
rendering_driver = "d3d12";
@@ -6051,6 +6198,7 @@ DisplayServerWindows::DisplayServerWindows(const String &p_rendering_driver, Win
}
if (force_angle || (gl_info["version"].operator int() < 30003)) {
+ tested_drivers.set_flag(DRIVER_ID_COMPAT_OPENGL3);
if (show_warning) {
WARN_PRINT("Your video card drivers seem not to support the required OpenGL 3.3 version, switching to ANGLE.");
}
@@ -6060,6 +6208,7 @@ DisplayServerWindows::DisplayServerWindows(const String &p_rendering_driver, Win
if (rendering_driver == "opengl3") {
gl_manager_native = memnew(GLManagerNative_Windows);
+ tested_drivers.set_flag(DRIVER_ID_COMPAT_OPENGL3);
if (gl_manager_native->initialize() != OK) {
memdelete(gl_manager_native);
@@ -6072,6 +6221,7 @@ DisplayServerWindows::DisplayServerWindows(const String &p_rendering_driver, Win
}
if (rendering_driver == "opengl3_angle") {
gl_manager_angle = memnew(GLManagerANGLE_Windows);
+ tested_drivers.set_flag(DRIVER_ID_COMPAT_ANGLE_D3D11);
if (gl_manager_angle->initialize() != OK) {
memdelete(gl_manager_angle);
@@ -6205,32 +6355,41 @@ Vector<String> DisplayServerWindows::get_rendering_drivers_func() {
DisplayServer *DisplayServerWindows::create_func(const String &p_rendering_driver, WindowMode p_mode, VSyncMode p_vsync_mode, uint32_t p_flags, const Vector2i *p_position, const Vector2i &p_resolution, int p_screen, Context p_context, Error &r_error) {
DisplayServer *ds = memnew(DisplayServerWindows(p_rendering_driver, p_mode, p_vsync_mode, p_flags, p_position, p_resolution, p_screen, p_context, r_error));
if (r_error != OK) {
- if (p_rendering_driver == "vulkan") {
- String executable_name = OS::get_singleton()->get_executable_path().get_file();
- OS::get_singleton()->alert(
- vformat("Your video card drivers seem not to support the required Vulkan version.\n\n"
- "If possible, consider updating your video card drivers or using the OpenGL 3 driver.\n\n"
- "You can enable the OpenGL 3 driver by starting the engine from the\n"
- "command line with the command:\n\n \"%s\" --rendering-driver opengl3\n\n"
- "If you have recently updated your video card drivers, try rebooting.",
- executable_name),
- "Unable to initialize Vulkan video driver");
- } else if (p_rendering_driver == "d3d12") {
+ if (tested_drivers == 0) {
+ OS::get_singleton()->alert("Failed to register the window class.", "Unable to initialize DisplayServer");
+ } else if (tested_drivers.has_flag(DRIVER_ID_RD_VULKAN) || tested_drivers.has_flag(DRIVER_ID_RD_D3D12)) {
+ Vector<String> drivers;
+ if (tested_drivers.has_flag(DRIVER_ID_RD_VULKAN)) {
+ drivers.push_back("Vulkan");
+ }
+ if (tested_drivers.has_flag(DRIVER_ID_RD_D3D12)) {
+ drivers.push_back("Direct3D 12");
+ }
String executable_name = OS::get_singleton()->get_executable_path().get_file();
OS::get_singleton()->alert(
- vformat("Your video card drivers seem not to support the required DirectX 12 version.\n\n"
+ vformat("Your video card drivers seem not to support the required %s version.\n\n"
"If possible, consider updating your video card drivers or using the OpenGL 3 driver.\n\n"
"You can enable the OpenGL 3 driver by starting the engine from the\n"
"command line with the command:\n\n \"%s\" --rendering-driver opengl3\n\n"
"If you have recently updated your video card drivers, try rebooting.",
+ String(" or ").join(drivers),
executable_name),
- "Unable to initialize DirectX 12 video driver");
+ "Unable to initialize video driver");
} else {
+ Vector<String> drivers;
+ if (tested_drivers.has_flag(DRIVER_ID_COMPAT_OPENGL3)) {
+ drivers.push_back("OpenGL 3.3");
+ }
+ if (tested_drivers.has_flag(DRIVER_ID_COMPAT_ANGLE_D3D11)) {
+ drivers.push_back("Direct3D 11");
+ }
OS::get_singleton()->alert(
- "Your video card drivers seem not to support the required OpenGL 3.3 version.\n\n"
- "If possible, consider updating your video card drivers.\n\n"
- "If you have recently updated your video card drivers, try rebooting.",
- "Unable to initialize OpenGL video driver");
+ vformat(
+ "Your video card drivers seem not to support the required %s version.\n\n"
+ "If possible, consider updating your video card drivers.\n\n"
+ "If you have recently updated your video card drivers, try rebooting.",
+ String(" or ").join(drivers)),
+ "Unable to initialize video driver");
}
}
return ds;
@@ -6241,6 +6400,20 @@ void DisplayServerWindows::register_windows_driver() {
}
DisplayServerWindows::~DisplayServerWindows() {
+ LocalVector<List<FileDialogData *>::Element *> to_remove;
+ for (List<FileDialogData *>::Element *E = file_dialogs.front(); E; E = E->next()) {
+ FileDialogData *fd = E->get();
+ if (fd->listener_thread.is_started()) {
+ fd->close_requested.set();
+ fd->listener_thread.wait_to_finish();
+ }
+ to_remove.push_back(E);
+ }
+ for (List<FileDialogData *>::Element *E : to_remove) {
+ memdelete(E->get());
+ E->erase();
+ }
+
delete joypad;
touch_state.clear();
diff --git a/platform/windows/display_server_windows.h b/platform/windows/display_server_windows.h
index de5b813953..8a2f9f81a6 100644
--- a/platform/windows/display_server_windows.h
+++ b/platform/windows/display_server_windows.h
@@ -350,6 +350,12 @@ typedef struct {
ICONDIRENTRY idEntries[1]; // An entry for each image (idCount of 'em)
} ICONDIR, *LPICONDIR;
+typedef enum _SHC_PROCESS_DPI_AWARENESS {
+ SHC_PROCESS_DPI_UNAWARE = 0,
+ SHC_PROCESS_SYSTEM_DPI_AWARE = 1,
+ SHC_PROCESS_PER_MONITOR_DPI_AWARE = 2,
+} SHC_PROCESS_DPI_AWARENESS;
+
class DisplayServerWindows : public DisplayServer {
// No need to register with GDCLASS, it's platform-specific and nothing is added.
@@ -388,6 +394,14 @@ class DisplayServerWindows : public DisplayServer {
String tablet_driver;
Vector<String> tablet_drivers;
+ enum DriverID {
+ DRIVER_ID_COMPAT_OPENGL3 = 1 << 0,
+ DRIVER_ID_COMPAT_ANGLE_D3D11 = 1 << 1,
+ DRIVER_ID_RD_VULKAN = 1 << 2,
+ DRIVER_ID_RD_D3D12 = 1 << 3,
+ };
+ static BitField<DriverID> tested_drivers;
+
enum TimerID {
TIMER_ID_MOVE_REDRAW = 1,
TIMER_ID_WINDOW_ACTIVATION = 2,
@@ -536,6 +550,31 @@ class DisplayServerWindows : public DisplayServer {
IndicatorID indicator_id_counter = 0;
HashMap<IndicatorID, IndicatorData> indicators;
+ struct FileDialogData {
+ HWND hwnd_owner = 0;
+ Rect2i wrect;
+ String appid;
+ String title;
+ String current_directory;
+ String root;
+ String filename;
+ bool show_hidden = false;
+ DisplayServer::FileDialogMode mode = FileDialogMode::FILE_DIALOG_MODE_OPEN_ANY;
+ Vector<String> filters;
+ TypedArray<Dictionary> options;
+ WindowID window_id = DisplayServer::INVALID_WINDOW_ID;
+ Callable callback;
+ bool options_in_cb = false;
+ Thread listener_thread;
+ SafeFlag close_requested;
+ SafeFlag finished;
+ };
+ Mutex file_dialog_mutex;
+ List<FileDialogData *> file_dialogs;
+ HashMap<HWND, FileDialogData *> file_dialog_wnd;
+
+ static void _thread_fd_monitor(void *p_ud);
+
HashMap<int64_t, MouseButton> pointer_prev_button;
HashMap<int64_t, MouseButton> pointer_button;
HashMap<int64_t, LONG> pointer_down_time;
@@ -597,6 +636,7 @@ class DisplayServerWindows : public DisplayServer {
String _get_klid(HKL p_hkl) const;
public:
+ LRESULT WndProcFileDialog(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
LRESULT WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
LRESULT MouseProc(int code, WPARAM wParam, LPARAM lParam);
diff --git a/platform/windows/export/export_plugin.cpp b/platform/windows/export/export_plugin.cpp
index 12694b0155..b465bd4ecd 100644
--- a/platform/windows/export/export_plugin.cpp
+++ b/platform/windows/export/export_plugin.cpp
@@ -187,6 +187,12 @@ Error EditorExportPlatformWindows::export_project(const Ref<EditorExportPreset>
template_path = template_path.strip_edges();
if (template_path.is_empty()) {
template_path = find_export_template(get_template_file_name(p_debug ? "debug" : "release", arch));
+ } else {
+ String exe_arch = _get_exe_arch(template_path);
+ if (arch != exe_arch) {
+ add_message(EXPORT_MESSAGE_ERROR, TTR("Prepare Templates"), vformat(TTR("Mismatching custom export template executable architecture: found \"%s\", expected \"%s\"."), exe_arch, arch));
+ return ERR_CANT_CREATE;
+ }
}
int export_angle = p_preset->get("application/export_angle");
@@ -753,9 +759,26 @@ Error EditorExportPlatformWindows::_code_sign(const Ref<EditorExportPreset> &p_p
}
bool EditorExportPlatformWindows::has_valid_export_configuration(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates, bool p_debug) const {
- String err = "";
+ String err;
bool valid = EditorExportPlatformPC::has_valid_export_configuration(p_preset, err, r_missing_templates, p_debug);
+ String custom_debug = p_preset->get("custom_template/debug").operator String().strip_edges();
+ String custom_release = p_preset->get("custom_template/release").operator String().strip_edges();
+ String arch = p_preset->get("binary_format/architecture");
+
+ if (!custom_debug.is_empty() && FileAccess::exists(custom_debug)) {
+ String exe_arch = _get_exe_arch(custom_debug);
+ if (arch != exe_arch) {
+ err += vformat(TTR("Mismatching custom debug export template executable architecture: found \"%s\", expected \"%s\"."), exe_arch, arch) + "\n";
+ }
+ }
+ if (!custom_release.is_empty() && FileAccess::exists(custom_release)) {
+ String exe_arch = _get_exe_arch(custom_release);
+ if (arch != exe_arch) {
+ err += vformat(TTR("Mismatching custom release export template executable architecture: found \"%s\", expected \"%s\"."), exe_arch, arch) + "\n";
+ }
+ }
+
String rcedit_path = EDITOR_GET("export/windows/rcedit");
if (p_preset->get("application/modify_resources") && rcedit_path.is_empty()) {
err += TTR("The rcedit tool must be configured in the Editor Settings (Export > Windows > rcedit) to change the icon or app information data.") + "\n";
@@ -769,7 +792,7 @@ bool EditorExportPlatformWindows::has_valid_export_configuration(const Ref<Edito
}
bool EditorExportPlatformWindows::has_valid_project_configuration(const Ref<EditorExportPreset> &p_preset, String &r_error) const {
- String err = "";
+ String err;
bool valid = true;
List<ExportOption> options;
@@ -793,6 +816,43 @@ bool EditorExportPlatformWindows::has_valid_project_configuration(const Ref<Edit
return valid;
}
+String EditorExportPlatformWindows::_get_exe_arch(const String &p_path) const {
+ Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::READ);
+ if (f.is_null()) {
+ return "invalid";
+ }
+
+ // Jump to the PE header and check the magic number.
+ {
+ f->seek(0x3c);
+ uint32_t pe_pos = f->get_32();
+
+ f->seek(pe_pos);
+ uint32_t magic = f->get_32();
+ if (magic != 0x00004550) {
+ return "invalid";
+ }
+ }
+
+ // Process header.
+ uint16_t machine = f->get_16();
+ f->close();
+
+ switch (machine) {
+ case 0x014c:
+ return "x86_32";
+ case 0x8664:
+ return "x86_64";
+ case 0x01c0:
+ case 0x01c4:
+ return "arm32";
+ case 0xaa64:
+ return "arm64";
+ default:
+ return "unknown";
+ }
+}
+
Error EditorExportPlatformWindows::fixup_embedded_pck(const String &p_path, int64_t p_embedded_start, int64_t p_embedded_size) {
// Patch the header of the "pck" section in the PE file so that it corresponds to the embedded data
diff --git a/platform/windows/export/export_plugin.h b/platform/windows/export/export_plugin.h
index c644b1f9e1..6ccb4a15a7 100644
--- a/platform/windows/export/export_plugin.h
+++ b/platform/windows/export/export_plugin.h
@@ -73,6 +73,8 @@ class EditorExportPlatformWindows : public EditorExportPlatformPC {
Error _rcedit_add_data(const Ref<EditorExportPreset> &p_preset, const String &p_path, bool p_console_icon);
Error _code_sign(const Ref<EditorExportPreset> &p_preset, const String &p_path);
+ String _get_exe_arch(const String &p_path) const;
+
public:
virtual Error export_project(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags = 0) override;
virtual Error modify_template(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &p_path, int p_flags) override;
diff --git a/scene/2d/audio_stream_player_2d.cpp b/scene/2d/audio_stream_player_2d.cpp
index 8e91dce425..89a0479de3 100644
--- a/scene/2d/audio_stream_player_2d.cpp
+++ b/scene/2d/audio_stream_player_2d.cpp
@@ -242,7 +242,7 @@ void AudioStreamPlayer2D::seek(float p_seconds) {
void AudioStreamPlayer2D::stop() {
setplay.set(-1);
- internal->stop();
+ internal->stop_basic();
}
bool AudioStreamPlayer2D::is_playing() const {
@@ -430,7 +430,7 @@ void AudioStreamPlayer2D::_bind_methods() {
}
AudioStreamPlayer2D::AudioStreamPlayer2D() {
- internal = memnew(AudioStreamPlayerInternal(this, callable_mp(this, &AudioStreamPlayer2D::play), true));
+ internal = memnew(AudioStreamPlayerInternal(this, callable_mp(this, &AudioStreamPlayer2D::play), callable_mp(this, &AudioStreamPlayer2D::stop), true));
cached_global_panning_strength = GLOBAL_GET("audio/general/2d_panning_strength");
set_hide_clip_children(true);
}
diff --git a/scene/2d/navigation_link_2d.cpp b/scene/2d/navigation_link_2d.cpp
index 04ba550888..111f5a7b78 100644
--- a/scene/2d/navigation_link_2d.cpp
+++ b/scene/2d/navigation_link_2d.cpp
@@ -41,6 +41,9 @@ void NavigationLink2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_enabled", "enabled"), &NavigationLink2D::set_enabled);
ClassDB::bind_method(D_METHOD("is_enabled"), &NavigationLink2D::is_enabled);
+ ClassDB::bind_method(D_METHOD("set_navigation_map", "navigation_map"), &NavigationLink2D::set_navigation_map);
+ ClassDB::bind_method(D_METHOD("get_navigation_map"), &NavigationLink2D::get_navigation_map);
+
ClassDB::bind_method(D_METHOD("set_bidirectional", "bidirectional"), &NavigationLink2D::set_bidirectional);
ClassDB::bind_method(D_METHOD("is_bidirectional"), &NavigationLink2D::is_bidirectional);
@@ -106,12 +109,7 @@ bool NavigationLink2D::_get(const StringName &p_name, Variant &r_ret) const {
void NavigationLink2D::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_ENTER_TREE: {
- if (enabled) {
- NavigationServer2D::get_singleton()->link_set_map(link, get_world_2d()->get_navigation_map());
- }
- current_global_transform = get_global_transform();
- NavigationServer2D::get_singleton()->link_set_start_position(link, current_global_transform.xform(start_position));
- NavigationServer2D::get_singleton()->link_set_end_position(link, current_global_transform.xform(end_position));
+ _link_enter_navigation_map();
} break;
case NOTIFICATION_TRANSFORM_CHANGED: {
@@ -120,36 +118,15 @@ void NavigationLink2D::_notification(int p_what) {
case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: {
set_physics_process_internal(false);
- if (is_inside_tree()) {
- Transform2D new_global_transform = get_global_transform();
- if (current_global_transform != new_global_transform) {
- current_global_transform = new_global_transform;
- NavigationServer2D::get_singleton()->link_set_start_position(link, current_global_transform.xform(start_position));
- NavigationServer2D::get_singleton()->link_set_end_position(link, current_global_transform.xform(end_position));
- queue_redraw();
- }
- }
+ _link_update_transform();
} break;
case NOTIFICATION_EXIT_TREE: {
- NavigationServer2D::get_singleton()->link_set_map(link, RID());
+ _link_exit_navigation_map();
} break;
case NOTIFICATION_DRAW: {
#ifdef DEBUG_ENABLED
- if (is_inside_tree() && (Engine::get_singleton()->is_editor_hint() || NavigationServer2D::get_singleton()->get_debug_enabled())) {
- Color color;
- if (enabled) {
- color = NavigationServer2D::get_singleton()->get_debug_navigation_link_connection_color();
- } else {
- color = NavigationServer2D::get_singleton()->get_debug_navigation_link_connection_disabled_color();
- }
-
- real_t radius = NavigationServer2D::get_singleton()->map_get_link_connection_radius(get_world_2d()->get_navigation_map());
-
- draw_line(get_start_position(), get_end_position(), color);
- draw_arc(get_start_position(), radius, 0, Math_TAU, 10, color);
- draw_arc(get_end_position(), radius, 0, Math_TAU, 10, color);
- }
+ _update_debug_mesh();
#endif // DEBUG_ENABLED
} break;
}
@@ -188,15 +165,32 @@ void NavigationLink2D::set_enabled(bool p_enabled) {
enabled = p_enabled;
- NavigationServer3D::get_singleton()->link_set_enabled(link, enabled);
+ NavigationServer2D::get_singleton()->link_set_enabled(link, enabled);
#ifdef DEBUG_ENABLED
- if (Engine::get_singleton()->is_editor_hint() || NavigationServer2D::get_singleton()->get_debug_enabled()) {
- queue_redraw();
- }
+ queue_redraw();
#endif // DEBUG_ENABLED
}
+void NavigationLink2D::set_navigation_map(RID p_navigation_map) {
+ if (map_override == p_navigation_map) {
+ return;
+ }
+
+ map_override = p_navigation_map;
+
+ NavigationServer2D::get_singleton()->link_set_map(link, map_override);
+}
+
+RID NavigationLink2D::get_navigation_map() const {
+ if (map_override.is_valid()) {
+ return map_override;
+ } else if (is_inside_tree()) {
+ return get_world_2d()->get_navigation_map();
+ }
+ return RID();
+}
+
void NavigationLink2D::set_bidirectional(bool p_bidirectional) {
if (bidirectional == p_bidirectional) {
return;
@@ -255,9 +249,7 @@ void NavigationLink2D::set_start_position(Vector2 p_position) {
update_configuration_warnings();
#ifdef DEBUG_ENABLED
- if (Engine::get_singleton()->is_editor_hint() || NavigationServer2D::get_singleton()->get_debug_enabled()) {
- queue_redraw();
- }
+ queue_redraw();
#endif // DEBUG_ENABLED
}
@@ -277,9 +269,7 @@ void NavigationLink2D::set_end_position(Vector2 p_position) {
update_configuration_warnings();
#ifdef DEBUG_ENABLED
- if (Engine::get_singleton()->is_editor_hint() || NavigationServer2D::get_singleton()->get_debug_enabled()) {
- queue_redraw();
- }
+ queue_redraw();
#endif // DEBUG_ENABLED
}
@@ -347,6 +337,69 @@ PackedStringArray NavigationLink2D::get_configuration_warnings() const {
return warnings;
}
+void NavigationLink2D::_link_enter_navigation_map() {
+ if (!is_inside_tree()) {
+ return;
+ }
+
+ if (map_override.is_valid()) {
+ NavigationServer2D::get_singleton()->link_set_map(link, map_override);
+ } else {
+ NavigationServer2D::get_singleton()->link_set_map(link, get_world_2d()->get_navigation_map());
+ }
+
+ current_global_transform = get_global_transform();
+
+ NavigationServer2D::get_singleton()->link_set_start_position(link, current_global_transform.xform(start_position));
+ NavigationServer2D::get_singleton()->link_set_end_position(link, current_global_transform.xform(end_position));
+ NavigationServer2D::get_singleton()->link_set_enabled(link, enabled);
+
+ queue_redraw();
+}
+
+void NavigationLink2D::_link_exit_navigation_map() {
+ NavigationServer2D::get_singleton()->link_set_map(link, RID());
+}
+
+void NavigationLink2D::_link_update_transform() {
+ if (!is_inside_tree()) {
+ return;
+ }
+
+ Transform2D new_global_transform = get_global_transform();
+ if (current_global_transform != new_global_transform) {
+ current_global_transform = new_global_transform;
+ NavigationServer2D::get_singleton()->link_set_start_position(link, current_global_transform.xform(start_position));
+ NavigationServer2D::get_singleton()->link_set_end_position(link, current_global_transform.xform(end_position));
+ queue_redraw();
+ }
+}
+
+#ifdef DEBUG_ENABLED
+void NavigationLink2D::_update_debug_mesh() {
+ if (!is_inside_tree()) {
+ return;
+ }
+
+ if (!Engine::get_singleton()->is_editor_hint() && !NavigationServer2D::get_singleton()->get_debug_enabled()) {
+ return;
+ }
+
+ Color color;
+ if (enabled) {
+ color = NavigationServer2D::get_singleton()->get_debug_navigation_link_connection_color();
+ } else {
+ color = NavigationServer2D::get_singleton()->get_debug_navigation_link_connection_disabled_color();
+ }
+
+ real_t radius = NavigationServer2D::get_singleton()->map_get_link_connection_radius(get_world_2d()->get_navigation_map());
+
+ draw_line(get_start_position(), get_end_position(), color);
+ draw_arc(get_start_position(), radius, 0, Math_TAU, 10, color);
+ draw_arc(get_end_position(), radius, 0, Math_TAU, 10, color);
+}
+#endif // DEBUG_ENABLED
+
NavigationLink2D::NavigationLink2D() {
link = NavigationServer2D::get_singleton()->link_create();
diff --git a/scene/2d/navigation_link_2d.h b/scene/2d/navigation_link_2d.h
index 2929691c04..c724096607 100644
--- a/scene/2d/navigation_link_2d.h
+++ b/scene/2d/navigation_link_2d.h
@@ -38,6 +38,7 @@ class NavigationLink2D : public Node2D {
bool enabled = true;
RID link;
+ RID map_override;
bool bidirectional = true;
uint32_t navigation_layers = 1;
Vector2 end_position;
@@ -47,6 +48,10 @@ class NavigationLink2D : public Node2D {
Transform2D current_global_transform;
+#ifdef DEBUG_ENABLED
+ void _update_debug_mesh();
+#endif // DEBUG_ENABLED
+
protected:
static void _bind_methods();
void _notification(int p_what);
@@ -66,6 +71,9 @@ public:
void set_enabled(bool p_enabled);
bool is_enabled() const { return enabled; }
+ void set_navigation_map(RID p_navigation_map);
+ RID get_navigation_map() const;
+
void set_bidirectional(bool p_bidirectional);
bool is_bidirectional() const { return bidirectional; }
@@ -97,6 +105,11 @@ public:
NavigationLink2D();
~NavigationLink2D();
+
+private:
+ void _link_enter_navigation_map();
+ void _link_exit_navigation_map();
+ void _link_update_transform();
};
#endif // NAVIGATION_LINK_2D_H
diff --git a/scene/2d/parallax_layer.cpp b/scene/2d/parallax_layer.cpp
index dfe321a435..023e9201fc 100644
--- a/scene/2d/parallax_layer.cpp
+++ b/scene/2d/parallax_layer.cpp
@@ -78,13 +78,7 @@ void ParallaxLayer::_update_mirroring() {
}
void ParallaxLayer::set_mirroring(const Size2 &p_mirroring) {
- mirroring = p_mirroring;
- if (mirroring.x < 0) {
- mirroring.x = 0;
- }
- if (mirroring.y < 0) {
- mirroring.y = 0;
- }
+ mirroring = p_mirroring.maxf(0);
_update_mirroring();
}
diff --git a/scene/2d/polygon_2d.cpp b/scene/2d/polygon_2d.cpp
index 4266060466..42f7a75c0a 100644
--- a/scene/2d/polygon_2d.cpp
+++ b/scene/2d/polygon_2d.cpp
@@ -682,7 +682,7 @@ void Polygon2D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::PACKED_VECTOR2_ARRAY, "uv"), "set_uv", "get_uv");
ADD_PROPERTY(PropertyInfo(Variant::PACKED_COLOR_ARRAY, "vertex_colors"), "set_vertex_colors", "get_vertex_colors");
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "polygons"), "set_polygons", "get_polygons");
- ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "bones", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "_set_bones", "_get_bones");
+ ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "bones", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_bones", "_get_bones");
ADD_PROPERTY(PropertyInfo(Variant::INT, "internal_vertex_count", PROPERTY_HINT_RANGE, "0,1000"), "set_internal_vertex_count", "get_internal_vertex_count");
}
diff --git a/scene/2d/skeleton_2d.cpp b/scene/2d/skeleton_2d.cpp
index 8aa50668eb..f9e8c831d1 100644
--- a/scene/2d/skeleton_2d.cpp
+++ b/scene/2d/skeleton_2d.cpp
@@ -39,19 +39,17 @@
#endif //TOOLS_ENABLED
bool Bone2D::_set(const StringName &p_path, const Variant &p_value) {
- String path = p_path;
-
- if (path.begins_with("auto_calculate_length_and_angle")) {
+ if (p_path == SNAME("auto_calculate_length_and_angle")) {
set_autocalculate_length_and_angle(p_value);
- } else if (path.begins_with("length")) {
+ } else if (p_path == SNAME("length")) {
set_length(p_value);
- } else if (path.begins_with("bone_angle")) {
+ } else if (p_path == SNAME("bone_angle")) {
set_bone_angle(Math::deg_to_rad(real_t(p_value)));
- } else if (path.begins_with("default_length")) {
+ } else if (p_path == SNAME("default_length")) {
set_length(p_value);
}
#ifdef TOOLS_ENABLED
- else if (path.begins_with("editor_settings/show_bone_gizmo")) {
+ else if (p_path == SNAME("editor_settings/show_bone_gizmo")) {
_editor_set_show_bone_gizmo(p_value);
}
#endif // TOOLS_ENABLED
@@ -63,19 +61,17 @@ bool Bone2D::_set(const StringName &p_path, const Variant &p_value) {
}
bool Bone2D::_get(const StringName &p_path, Variant &r_ret) const {
- String path = p_path;
-
- if (path.begins_with("auto_calculate_length_and_angle")) {
+ if (p_path == SNAME("auto_calculate_length_and_angle")) {
r_ret = get_autocalculate_length_and_angle();
- } else if (path.begins_with("length")) {
+ } else if (p_path == SNAME("length")) {
r_ret = get_length();
- } else if (path.begins_with("bone_angle")) {
+ } else if (p_path == SNAME("bone_angle")) {
r_ret = Math::rad_to_deg(get_bone_angle());
- } else if (path.begins_with("default_length")) {
+ } else if (p_path == SNAME("default_length")) {
r_ret = get_length();
}
#ifdef TOOLS_ENABLED
- else if (path.begins_with("editor_settings/show_bone_gizmo")) {
+ else if (p_path == SNAME("editor_settings/show_bone_gizmo")) {
r_ret = _editor_get_show_bone_gizmo();
}
#endif // TOOLS_ENABLED
@@ -330,9 +326,7 @@ bool Bone2D::_editor_get_bone_shape(Vector<Vector2> *p_shape, Vector<Vector2> *p
rel = (p_other_bone->get_global_position() - get_global_position());
rel = rel.rotated(-get_global_rotation()); // Undo Bone2D node's rotation so its drawn correctly regardless of the node's rotation
} else {
- real_t angle_to_use = get_rotation() + bone_angle;
- rel = Vector2(cos(angle_to_use), sin(angle_to_use)) * (length * MIN(get_global_scale().x, get_global_scale().y));
- rel = rel.rotated(-get_rotation()); // Undo Bone2D node's rotation so its drawn correctly regardless of the node's rotation
+ rel = Vector2(Math::cos(bone_angle), Math::sin(bone_angle)) * length * get_global_scale();
}
Vector2 relt = rel.rotated(Math_PI * 0.5).normalized() * bone_width;
@@ -518,23 +512,19 @@ Bone2D::~Bone2D() {
//////////////////////////////////////
bool Skeleton2D::_set(const StringName &p_path, const Variant &p_value) {
- String path = p_path;
-
- if (path.begins_with("modification_stack")) {
+ if (p_path == SNAME("modification_stack")) {
set_modification_stack(p_value);
return true;
}
- return true;
+ return false;
}
bool Skeleton2D::_get(const StringName &p_path, Variant &r_ret) const {
- String path = p_path;
-
- if (path.begins_with("modification_stack")) {
+ if (p_path == SNAME("modification_stack")) {
r_ret = get_modification_stack();
return true;
}
- return true;
+ return false;
}
void Skeleton2D::_get_property_list(List<PropertyInfo> *p_list) const {
diff --git a/scene/2d/tile_map.cpp b/scene/2d/tile_map.cpp
index c12b95314e..b10f2097da 100644
--- a/scene/2d/tile_map.cpp
+++ b/scene/2d/tile_map.cpp
@@ -532,6 +532,18 @@ TileData *TileMap::get_cell_tile_data(int p_layer, const Vector2i &p_coords, boo
}
}
+bool TileMap::is_cell_flipped_h(int p_layer, const Vector2i &p_coords, bool p_use_proxies) const {
+ return get_cell_alternative_tile(p_layer, p_coords, p_use_proxies) & TileSetAtlasSource::TRANSFORM_FLIP_H;
+}
+
+bool TileMap::is_cell_flipped_v(int p_layer, const Vector2i &p_coords, bool p_use_proxies) const {
+ return get_cell_alternative_tile(p_layer, p_coords, p_use_proxies) & TileSetAtlasSource::TRANSFORM_FLIP_V;
+}
+
+bool TileMap::is_cell_transposed(int p_layer, const Vector2i &p_coords, bool p_use_proxies) const {
+ return get_cell_alternative_tile(p_layer, p_coords, p_use_proxies) & TileSetAtlasSource::TRANSFORM_TRANSPOSE;
+}
+
Ref<TileMapPattern> TileMap::get_pattern(int p_layer, TypedArray<Vector2i> p_coords_array) {
TILEMAP_CALL_FOR_LAYER_V(p_layer, Ref<TileMapPattern>(), get_pattern, p_coords_array);
}
@@ -926,6 +938,10 @@ void TileMap::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_cell_alternative_tile", "layer", "coords", "use_proxies"), &TileMap::get_cell_alternative_tile, DEFVAL(false));
ClassDB::bind_method(D_METHOD("get_cell_tile_data", "layer", "coords", "use_proxies"), &TileMap::get_cell_tile_data, DEFVAL(false));
+ ClassDB::bind_method(D_METHOD("is_cell_flipped_h", "layer", "coords", "use_proxies"), &TileMap::is_cell_flipped_h, DEFVAL(false));
+ ClassDB::bind_method(D_METHOD("is_cell_flipped_v", "layer", "coords", "use_proxies"), &TileMap::is_cell_flipped_v, DEFVAL(false));
+ ClassDB::bind_method(D_METHOD("is_cell_transposed", "layer", "coords", "use_proxies"), &TileMap::is_cell_transposed, DEFVAL(false));
+
ClassDB::bind_method(D_METHOD("get_coords_for_body_rid", "body"), &TileMap::get_coords_for_body_rid);
ClassDB::bind_method(D_METHOD("get_layer_for_body_rid", "body"), &TileMap::get_layer_for_body_rid);
diff --git a/scene/2d/tile_map.h b/scene/2d/tile_map.h
index 690102f730..142dc1193f 100644
--- a/scene/2d/tile_map.h
+++ b/scene/2d/tile_map.h
@@ -167,6 +167,10 @@ public:
// Helper method to make accessing the data easier.
TileData *get_cell_tile_data(int p_layer, const Vector2i &p_coords, bool p_use_proxies = false) const;
+ bool is_cell_flipped_h(int p_layer, const Vector2i &p_coords, bool p_use_proxies = false) const;
+ bool is_cell_flipped_v(int p_layer, const Vector2i &p_coords, bool p_use_proxies = false) const;
+ bool is_cell_transposed(int p_layer, const Vector2i &p_coords, bool p_use_proxies = false) const;
+
// Patterns.
Ref<TileMapPattern> get_pattern(int p_layer, TypedArray<Vector2i> p_coords_array);
Vector2i map_pattern(const Vector2i &p_position_in_tilemap, const Vector2i &p_coords_in_pattern, Ref<TileMapPattern> p_pattern);
diff --git a/scene/2d/tile_map_layer.cpp b/scene/2d/tile_map_layer.cpp
index 437790bb99..7b125a6895 100644
--- a/scene/2d/tile_map_layer.cpp
+++ b/scene/2d/tile_map_layer.cpp
@@ -1773,6 +1773,10 @@ void TileMapLayer::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_cell_alternative_tile", "coords"), &TileMapLayer::get_cell_alternative_tile);
ClassDB::bind_method(D_METHOD("get_cell_tile_data", "coords"), &TileMapLayer::get_cell_tile_data);
+ ClassDB::bind_method(D_METHOD("is_cell_flipped_h", "coords"), &TileMapLayer::is_cell_flipped_h);
+ ClassDB::bind_method(D_METHOD("is_cell_flipped_v", "coords"), &TileMapLayer::is_cell_flipped_v);
+ ClassDB::bind_method(D_METHOD("is_cell_transposed", "coords"), &TileMapLayer::is_cell_transposed);
+
ClassDB::bind_method(D_METHOD("get_used_cells"), &TileMapLayer::get_used_cells);
ClassDB::bind_method(D_METHOD("get_used_cells_by_id", "source_id", "atlas_coords", "alternative_tile"), &TileMapLayer::get_used_cells_by_id, DEFVAL(TileSet::INVALID_SOURCE), DEFVAL(TileSetSource::INVALID_ATLAS_COORDS), DEFVAL(TileSetSource::INVALID_TILE_ALTERNATIVE));
ClassDB::bind_method(D_METHOD("get_used_rect"), &TileMapLayer::get_used_rect);
@@ -2490,6 +2494,18 @@ Rect2i TileMapLayer::get_used_rect() const {
return used_rect_cache;
}
+bool TileMapLayer::is_cell_flipped_h(const Vector2i &p_coords) const {
+ return get_cell_alternative_tile(p_coords) & TileSetAtlasSource::TRANSFORM_FLIP_H;
+}
+
+bool TileMapLayer::is_cell_flipped_v(const Vector2i &p_coords) const {
+ return get_cell_alternative_tile(p_coords) & TileSetAtlasSource::TRANSFORM_FLIP_V;
+}
+
+bool TileMapLayer::is_cell_transposed(const Vector2i &p_coords) const {
+ return get_cell_alternative_tile(p_coords) & TileSetAtlasSource::TRANSFORM_TRANSPOSE;
+}
+
Ref<TileMapPattern> TileMapLayer::get_pattern(TypedArray<Vector2i> p_coords_array) {
ERR_FAIL_COND_V(tile_set.is_null(), nullptr);
diff --git a/scene/2d/tile_map_layer.h b/scene/2d/tile_map_layer.h
index c71f13d7be..1a6d182094 100644
--- a/scene/2d/tile_map_layer.h
+++ b/scene/2d/tile_map_layer.h
@@ -438,6 +438,10 @@ public:
TypedArray<Vector2i> get_used_cells_by_id(int p_source_id = TileSet::INVALID_SOURCE, const Vector2i &p_atlas_coords = TileSetSource::INVALID_ATLAS_COORDS, int p_alternative_tile = TileSetSource::INVALID_TILE_ALTERNATIVE) const;
Rect2i get_used_rect() const;
+ bool is_cell_flipped_h(const Vector2i &p_coords) const;
+ bool is_cell_flipped_v(const Vector2i &p_coords) const;
+ bool is_cell_transposed(const Vector2i &p_coords) const;
+
// Patterns.
Ref<TileMapPattern> get_pattern(TypedArray<Vector2i> p_coords_array);
void set_pattern(const Vector2i &p_position, const Ref<TileMapPattern> p_pattern);
diff --git a/scene/3d/audio_stream_player_3d.cpp b/scene/3d/audio_stream_player_3d.cpp
index 6888462876..4d3f494ccf 100644
--- a/scene/3d/audio_stream_player_3d.cpp
+++ b/scene/3d/audio_stream_player_3d.cpp
@@ -562,7 +562,7 @@ void AudioStreamPlayer3D::seek(float p_seconds) {
void AudioStreamPlayer3D::stop() {
setplay.set(-1);
- internal->stop();
+ internal->stop_basic();
}
bool AudioStreamPlayer3D::is_playing() const {
@@ -862,7 +862,7 @@ void AudioStreamPlayer3D::_bind_methods() {
}
AudioStreamPlayer3D::AudioStreamPlayer3D() {
- internal = memnew(AudioStreamPlayerInternal(this, callable_mp(this, &AudioStreamPlayer3D::play), true));
+ internal = memnew(AudioStreamPlayerInternal(this, callable_mp(this, &AudioStreamPlayer3D::play), callable_mp(this, &AudioStreamPlayer3D::stop), true));
velocity_tracker.instantiate();
set_disable_scale(true);
cached_global_panning_strength = GLOBAL_GET("audio/general/3d_panning_strength");
diff --git a/scene/3d/navigation_link_3d.cpp b/scene/3d/navigation_link_3d.cpp
index dc776ebea2..bebba9a6c0 100644
--- a/scene/3d/navigation_link_3d.cpp
+++ b/scene/3d/navigation_link_3d.cpp
@@ -152,6 +152,9 @@ void NavigationLink3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_enabled", "enabled"), &NavigationLink3D::set_enabled);
ClassDB::bind_method(D_METHOD("is_enabled"), &NavigationLink3D::is_enabled);
+ ClassDB::bind_method(D_METHOD("set_navigation_map", "navigation_map"), &NavigationLink3D::set_navigation_map);
+ ClassDB::bind_method(D_METHOD("get_navigation_map"), &NavigationLink3D::get_navigation_map);
+
ClassDB::bind_method(D_METHOD("set_bidirectional", "bidirectional"), &NavigationLink3D::set_bidirectional);
ClassDB::bind_method(D_METHOD("is_bidirectional"), &NavigationLink3D::is_bidirectional);
@@ -217,16 +220,7 @@ bool NavigationLink3D::_get(const StringName &p_name, Variant &r_ret) const {
void NavigationLink3D::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_ENTER_TREE: {
- if (enabled) {
- NavigationServer3D::get_singleton()->link_set_map(link, get_world_3d()->get_navigation_map());
- }
- current_global_transform = get_global_transform();
- NavigationServer3D::get_singleton()->link_set_start_position(link, current_global_transform.xform(start_position));
- NavigationServer3D::get_singleton()->link_set_end_position(link, current_global_transform.xform(end_position));
-
-#ifdef DEBUG_ENABLED
- _update_debug_mesh();
-#endif // DEBUG_ENABLED
+ _link_enter_navigation_map();
} break;
case NOTIFICATION_TRANSFORM_CHANGED: {
@@ -235,30 +229,11 @@ void NavigationLink3D::_notification(int p_what) {
case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: {
set_physics_process_internal(false);
- if (is_inside_tree()) {
- Transform3D new_global_transform = get_global_transform();
- if (current_global_transform != new_global_transform) {
- current_global_transform = new_global_transform;
- NavigationServer3D::get_singleton()->link_set_start_position(link, current_global_transform.xform(start_position));
- NavigationServer3D::get_singleton()->link_set_end_position(link, current_global_transform.xform(end_position));
-#ifdef DEBUG_ENABLED
- if (debug_instance.is_valid()) {
- RS::get_singleton()->instance_set_transform(debug_instance, current_global_transform);
- }
-#endif // DEBUG_ENABLED
- }
- }
+ _link_update_transform();
} break;
case NOTIFICATION_EXIT_TREE: {
- NavigationServer3D::get_singleton()->link_set_map(link, RID());
-
-#ifdef DEBUG_ENABLED
- if (debug_instance.is_valid()) {
- RS::get_singleton()->instance_set_scenario(debug_instance, RID());
- RS::get_singleton()->instance_set_visible(debug_instance, false);
- }
-#endif // DEBUG_ENABLED
+ _link_exit_navigation_map();
} break;
}
}
@@ -320,6 +295,25 @@ void NavigationLink3D::set_enabled(bool p_enabled) {
update_gizmos();
}
+void NavigationLink3D::set_navigation_map(RID p_navigation_map) {
+ if (map_override == p_navigation_map) {
+ return;
+ }
+
+ map_override = p_navigation_map;
+
+ NavigationServer3D::get_singleton()->link_set_map(link, map_override);
+}
+
+RID NavigationLink3D::get_navigation_map() const {
+ if (map_override.is_valid()) {
+ return map_override;
+ } else if (is_inside_tree()) {
+ return get_world_3d()->get_navigation_map();
+ }
+ return RID();
+}
+
void NavigationLink3D::set_bidirectional(bool p_bidirectional) {
if (bidirectional == p_bidirectional) {
return;
@@ -467,3 +461,53 @@ PackedStringArray NavigationLink3D::get_configuration_warnings() const {
return warnings;
}
+
+void NavigationLink3D::_link_enter_navigation_map() {
+ if (!is_inside_tree()) {
+ return;
+ }
+
+ if (map_override.is_valid()) {
+ NavigationServer3D::get_singleton()->link_set_map(link, map_override);
+ } else {
+ NavigationServer3D::get_singleton()->link_set_map(link, get_world_3d()->get_navigation_map());
+ }
+
+ current_global_transform = get_global_transform();
+ NavigationServer3D::get_singleton()->link_set_start_position(link, current_global_transform.xform(start_position));
+ NavigationServer3D::get_singleton()->link_set_end_position(link, current_global_transform.xform(end_position));
+ NavigationServer3D::get_singleton()->link_set_enabled(link, enabled);
+
+#ifdef DEBUG_ENABLED
+ if (NavigationServer3D::get_singleton()->get_debug_navigation_enabled()) {
+ _update_debug_mesh();
+ }
+#endif // DEBUG_ENABLED
+}
+
+void NavigationLink3D::_link_exit_navigation_map() {
+ NavigationServer3D::get_singleton()->link_set_map(link, RID());
+#ifdef DEBUG_ENABLED
+ if (debug_instance.is_valid()) {
+ RS::get_singleton()->instance_set_visible(debug_instance, false);
+ }
+#endif // DEBUG_ENABLED
+}
+
+void NavigationLink3D::_link_update_transform() {
+ if (!is_inside_tree()) {
+ return;
+ }
+
+ Transform3D new_global_transform = get_global_transform();
+ if (current_global_transform != new_global_transform) {
+ current_global_transform = new_global_transform;
+ NavigationServer3D::get_singleton()->link_set_start_position(link, current_global_transform.xform(start_position));
+ NavigationServer3D::get_singleton()->link_set_end_position(link, current_global_transform.xform(end_position));
+#ifdef DEBUG_ENABLED
+ if (NavigationServer3D::get_singleton()->get_debug_navigation_enabled()) {
+ _update_debug_mesh();
+ }
+#endif // DEBUG_ENABLED
+ }
+}
diff --git a/scene/3d/navigation_link_3d.h b/scene/3d/navigation_link_3d.h
index 1867082811..e894761f40 100644
--- a/scene/3d/navigation_link_3d.h
+++ b/scene/3d/navigation_link_3d.h
@@ -38,6 +38,7 @@ class NavigationLink3D : public Node3D {
bool enabled = true;
RID link;
+ RID map_override;
bool bidirectional = true;
uint32_t navigation_layers = 1;
Vector3 end_position;
@@ -72,6 +73,9 @@ public:
void set_enabled(bool p_enabled);
bool is_enabled() const { return enabled; }
+ void set_navigation_map(RID p_navigation_map);
+ RID get_navigation_map() const;
+
void set_bidirectional(bool p_bidirectional);
bool is_bidirectional() const { return bidirectional; }
@@ -100,6 +104,11 @@ public:
real_t get_travel_cost() const { return travel_cost; }
PackedStringArray get_configuration_warnings() const override;
+
+private:
+ void _link_enter_navigation_map();
+ void _link_exit_navigation_map();
+ void _link_update_transform();
};
#endif // NAVIGATION_LINK_3D_H
diff --git a/scene/3d/navigation_region_3d.cpp b/scene/3d/navigation_region_3d.cpp
index 40e04f0fb4..d7397a932d 100644
--- a/scene/3d/navigation_region_3d.cpp
+++ b/scene/3d/navigation_region_3d.cpp
@@ -686,6 +686,8 @@ void NavigationRegion3D::_update_debug_edge_connections_mesh() {
Vector<Vector3> vertex_array;
vertex_array.resize(connections_count * 6);
+ Vector3 *vertex_array_ptrw = vertex_array.ptrw();
+ int vertex_array_index = 0;
for (int i = 0; i < connections_count; i++) {
Vector3 connection_pathway_start = NavigationServer3D::get_singleton()->region_get_connection_pathway_start(region, i);
@@ -705,13 +707,12 @@ void NavigationRegion3D::_update_debug_edge_connections_mesh() {
Vector3 left_end_pos = connection_pathway_end + (end_right_dir * half_edge_connection_margin);
Vector3 right_end_pos = connection_pathway_end + (end_left_dir * half_edge_connection_margin);
- vertex_array.push_back(right_end_pos);
- vertex_array.push_back(left_start_pos);
- vertex_array.push_back(right_start_pos);
-
- vertex_array.push_back(left_end_pos);
- vertex_array.push_back(right_end_pos);
- vertex_array.push_back(right_start_pos);
+ vertex_array_ptrw[vertex_array_index++] = connection_pathway_start;
+ vertex_array_ptrw[vertex_array_index++] = connection_pathway_end;
+ vertex_array_ptrw[vertex_array_index++] = left_start_pos;
+ vertex_array_ptrw[vertex_array_index++] = right_start_pos;
+ vertex_array_ptrw[vertex_array_index++] = left_end_pos;
+ vertex_array_ptrw[vertex_array_index++] = right_end_pos;
}
if (vertex_array.size() == 0) {
@@ -724,7 +725,7 @@ void NavigationRegion3D::_update_debug_edge_connections_mesh() {
mesh_array.resize(Mesh::ARRAY_MAX);
mesh_array[Mesh::ARRAY_VERTEX] = vertex_array;
- debug_edge_connections_mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, mesh_array);
+ debug_edge_connections_mesh->add_surface_from_arrays(Mesh::PRIMITIVE_LINES, mesh_array);
debug_edge_connections_mesh->surface_set_material(0, edge_connections_material);
RS::get_singleton()->instance_set_base(debug_edge_connections_instance, debug_edge_connections_mesh->get_rid());
diff --git a/scene/3d/occluder_instance_3d.cpp b/scene/3d/occluder_instance_3d.cpp
index 150771545b..6982df12f6 100644
--- a/scene/3d/occluder_instance_3d.cpp
+++ b/scene/3d/occluder_instance_3d.cpp
@@ -129,9 +129,6 @@ void Occluder3D::_notification(int p_what) {
}
}
-void Occluder3D::_bind_methods() {
-}
-
Occluder3D::Occluder3D() {
occluder = RS::get_singleton()->occluder_create();
}
diff --git a/scene/3d/occluder_instance_3d.h b/scene/3d/occluder_instance_3d.h
index 91445710b3..62e9478527 100644
--- a/scene/3d/occluder_instance_3d.h
+++ b/scene/3d/occluder_instance_3d.h
@@ -49,7 +49,6 @@ protected:
void _update();
virtual void _update_arrays(PackedVector3Array &r_vertices, PackedInt32Array &r_indices) = 0;
- static void _bind_methods();
void _notification(int p_what);
public:
diff --git a/scene/3d/skeleton_3d.cpp b/scene/3d/skeleton_3d.cpp
index 5a4e176e99..c6ece84cdd 100644
--- a/scene/3d/skeleton_3d.cpp
+++ b/scene/3d/skeleton_3d.cpp
@@ -69,13 +69,13 @@ SkinReference::~SkinReference() {
///////////////////////////////////////
bool Skeleton3D::_set(const StringName &p_path, const Variant &p_value) {
- String path = p_path;
-
#ifndef DISABLE_DEPRECATED
- if (path.begins_with("animate_physical_bones")) {
+ if (p_path == SNAME("animate_physical_bones")) {
set_animate_physical_bones(p_value);
+ return true;
}
#endif
+ String path = p_path;
if (!path.begins_with("bones/")) {
return false;
@@ -139,13 +139,13 @@ bool Skeleton3D::_set(const StringName &p_path, const Variant &p_value) {
}
bool Skeleton3D::_get(const StringName &p_path, Variant &r_ret) const {
- String path = p_path;
-
#ifndef DISABLE_DEPRECATED
- if (path.begins_with("animate_physical_bones")) {
+ if (p_path == SNAME("animate_physical_bones")) {
r_ret = get_animate_physical_bones();
+ return true;
}
#endif
+ String path = p_path;
if (!path.begins_with("bones/")) {
return false;
diff --git a/scene/3d/soft_body_3d.cpp b/scene/3d/soft_body_3d.cpp
index f02cd9b700..4fe5dd2385 100644
--- a/scene/3d/soft_body_3d.cpp
+++ b/scene/3d/soft_body_3d.cpp
@@ -218,7 +218,13 @@ bool SoftBody3D::_set_property_pinned_points_attachment(int p_item, const String
if ("spatial_attachment_path" == p_what) {
PinnedPoint *w = pinned_points.ptrw();
- callable_mp(this, &SoftBody3D::_pin_point_deferred).call_deferred(Variant(w[p_item].point_index), true, p_value);
+
+ if (is_inside_tree()) {
+ callable_mp(this, &SoftBody3D::_pin_point_deferred).call_deferred(Variant(w[p_item].point_index), true, p_value);
+ } else {
+ pin_point(w[p_item].point_index, true, p_value);
+ _make_cache_dirty();
+ }
} else if ("offset" == p_what) {
PinnedPoint *w = pinned_points.ptrw();
w[p_item].offset = p_value;
diff --git a/scene/animation/animation_blend_tree.cpp b/scene/animation/animation_blend_tree.cpp
index e4baae1afb..cdc85d2b2d 100644
--- a/scene/animation/animation_blend_tree.cpp
+++ b/scene/animation/animation_blend_tree.cpp
@@ -754,9 +754,6 @@ AnimationNode::NodeTimeInfo AnimationNodeAdd2::_process(const AnimationMixer::Pl
return nti;
}
-void AnimationNodeAdd2::_bind_methods() {
-}
-
AnimationNodeAdd2::AnimationNodeAdd2() {
add_input("in");
add_input("add");
@@ -800,9 +797,6 @@ AnimationNode::NodeTimeInfo AnimationNodeAdd3::_process(const AnimationMixer::Pl
return nti;
}
-void AnimationNodeAdd3::_bind_methods() {
-}
-
AnimationNodeAdd3::AnimationNodeAdd3() {
add_input("-add");
add_input("in");
@@ -845,9 +839,6 @@ bool AnimationNodeBlend2::has_filter() const {
return true;
}
-void AnimationNodeBlend2::_bind_methods() {
-}
-
AnimationNodeBlend2::AnimationNodeBlend2() {
add_input("in");
add_input("blend");
@@ -887,9 +878,6 @@ AnimationNode::NodeTimeInfo AnimationNodeBlend3::_process(const AnimationMixer::
return amount > 0.5 ? nti2 : (amount < -0.5 ? nti0 : nti1); // Hacky but good enough.
}
-void AnimationNodeBlend3::_bind_methods() {
-}
-
AnimationNodeBlend3::AnimationNodeBlend3() {
add_input("-blend");
add_input("in");
@@ -932,9 +920,6 @@ AnimationNode::NodeTimeInfo AnimationNodeSub2::_process(const AnimationMixer::Pl
return blend_input(0, pi, FILTER_IGNORE, sync, p_test_only);
}
-void AnimationNodeSub2::_bind_methods() {
-}
-
AnimationNodeSub2::AnimationNodeSub2() {
add_input("in");
add_input("sub");
@@ -972,9 +957,6 @@ AnimationNode::NodeTimeInfo AnimationNodeTimeScale::_process(const AnimationMixe
return blend_input(0, pi, FILTER_IGNORE, true, p_test_only);
}
-void AnimationNodeTimeScale::_bind_methods() {
-}
-
AnimationNodeTimeScale::AnimationNodeTimeScale() {
add_input("in");
}
@@ -1014,9 +996,6 @@ AnimationNode::NodeTimeInfo AnimationNodeTimeSeek::_process(const AnimationMixer
return blend_input(0, pi, FILTER_IGNORE, true, p_test_only);
}
-void AnimationNodeTimeSeek::_bind_methods() {
-}
-
AnimationNodeTimeSeek::AnimationNodeTimeSeek() {
add_input("in");
}
diff --git a/scene/animation/animation_blend_tree.h b/scene/animation/animation_blend_tree.h
index c7ef7ed624..2add35d009 100644
--- a/scene/animation/animation_blend_tree.h
+++ b/scene/animation/animation_blend_tree.h
@@ -200,9 +200,6 @@ class AnimationNodeAdd2 : public AnimationNodeSync {
StringName add_amount = PNAME("add_amount");
-protected:
- static void _bind_methods();
-
public:
void get_parameter_list(List<PropertyInfo> *r_list) const override;
virtual Variant get_parameter_default_value(const StringName &p_parameter) const override;
@@ -220,9 +217,6 @@ class AnimationNodeAdd3 : public AnimationNodeSync {
StringName add_amount = PNAME("add_amount");
-protected:
- static void _bind_methods();
-
public:
void get_parameter_list(List<PropertyInfo> *r_list) const override;
virtual Variant get_parameter_default_value(const StringName &p_parameter) const override;
@@ -240,9 +234,6 @@ class AnimationNodeBlend2 : public AnimationNodeSync {
StringName blend_amount = PNAME("blend_amount");
-protected:
- static void _bind_methods();
-
public:
virtual void get_parameter_list(List<PropertyInfo> *r_list) const override;
virtual Variant get_parameter_default_value(const StringName &p_parameter) const override;
@@ -259,9 +250,6 @@ class AnimationNodeBlend3 : public AnimationNodeSync {
StringName blend_amount = PNAME("blend_amount");
-protected:
- static void _bind_methods();
-
public:
virtual void get_parameter_list(List<PropertyInfo> *r_list) const override;
virtual Variant get_parameter_default_value(const StringName &p_parameter) const override;
@@ -277,9 +265,6 @@ class AnimationNodeSub2 : public AnimationNodeSync {
StringName sub_amount = PNAME("sub_amount");
-protected:
- static void _bind_methods();
-
public:
void get_parameter_list(List<PropertyInfo> *r_list) const override;
virtual Variant get_parameter_default_value(const StringName &p_parameter) const override;
@@ -297,9 +282,6 @@ class AnimationNodeTimeScale : public AnimationNode {
StringName scale = PNAME("scale");
-protected:
- static void _bind_methods();
-
public:
virtual void get_parameter_list(List<PropertyInfo> *r_list) const override;
virtual Variant get_parameter_default_value(const StringName &p_parameter) const override;
@@ -316,9 +298,6 @@ class AnimationNodeTimeSeek : public AnimationNode {
StringName seek_pos_request = PNAME("seek_request");
-protected:
- static void _bind_methods();
-
public:
virtual void get_parameter_list(List<PropertyInfo> *r_list) const override;
virtual Variant get_parameter_default_value(const StringName &p_parameter) const override;
diff --git a/scene/animation/tween.cpp b/scene/animation/tween.cpp
index 6a61e8693d..f8bbd704f4 100644
--- a/scene/animation/tween.cpp
+++ b/scene/animation/tween.cpp
@@ -61,6 +61,11 @@ Ref<Tween> Tweener::_get_tween() {
return Ref<Tween>(ObjectDB::get_instance(tween_id));
}
+void Tweener::_finish() {
+ finished = true;
+ emit_signal(SceneStringName(finished));
+}
+
void Tweener::_bind_methods() {
ADD_SIGNAL(MethodInfo("finished"));
}
@@ -612,9 +617,8 @@ bool PropertyTweener::step(double &r_delta) {
return true;
} else {
target_instance->set_indexed(property, final_val);
- finished = true;
r_delta = elapsed_time - delay - duration;
- emit_signal(SceneStringName(finished));
+ _finish();
return false;
}
}
@@ -672,9 +676,8 @@ bool IntervalTweener::step(double &r_delta) {
r_delta = 0;
return true;
} else {
- finished = true;
r_delta = elapsed_time - duration;
- emit_signal(SceneStringName(finished));
+ _finish();
return false;
}
}
@@ -715,9 +718,8 @@ bool CallbackTweener::step(double &r_delta) {
ERR_FAIL_V_MSG(false, "Error calling method from CallbackTweener: " + Variant::get_callable_error_text(callback, nullptr, 0, ce) + ".");
}
- finished = true;
r_delta = elapsed_time - delay;
- emit_signal(SceneStringName(finished));
+ _finish();
return false;
}
@@ -801,9 +803,8 @@ bool MethodTweener::step(double &r_delta) {
r_delta = 0;
return true;
} else {
- finished = true;
r_delta = elapsed_time - delay - duration;
- emit_signal(SceneStringName(finished));
+ _finish();
return false;
}
}
diff --git a/scene/animation/tween.h b/scene/animation/tween.h
index f5ae5e9776..40e1da0ad3 100644
--- a/scene/animation/tween.h
+++ b/scene/animation/tween.h
@@ -50,6 +50,7 @@ protected:
static void _bind_methods();
Ref<Tween> _get_tween();
+ void _finish();
double elapsed_time = 0;
bool finished = false;
diff --git a/scene/audio/audio_stream_player.cpp b/scene/audio/audio_stream_player.cpp
index e90c1aa245..183c4af950 100644
--- a/scene/audio/audio_stream_player.cpp
+++ b/scene/audio/audio_stream_player.cpp
@@ -112,7 +112,7 @@ void AudioStreamPlayer::seek(float p_seconds) {
}
void AudioStreamPlayer::stop() {
- internal->stop();
+ internal->stop_basic();
}
bool AudioStreamPlayer::is_playing() const {
@@ -283,7 +283,7 @@ void AudioStreamPlayer::_bind_methods() {
}
AudioStreamPlayer::AudioStreamPlayer() {
- internal = memnew(AudioStreamPlayerInternal(this, callable_mp(this, &AudioStreamPlayer::play), false));
+ internal = memnew(AudioStreamPlayerInternal(this, callable_mp(this, &AudioStreamPlayer::play), callable_mp(this, &AudioStreamPlayer::stop), false));
}
AudioStreamPlayer::~AudioStreamPlayer() {
diff --git a/scene/audio/audio_stream_player_internal.cpp b/scene/audio/audio_stream_player_internal.cpp
index 36c14e03d5..206408e3a7 100644
--- a/scene/audio/audio_stream_player_internal.cpp
+++ b/scene/audio/audio_stream_player_internal.cpp
@@ -132,7 +132,7 @@ Ref<AudioStreamPlayback> AudioStreamPlayerInternal::play_basic() {
}
ERR_FAIL_COND_V_MSG(!node->is_inside_tree(), stream_playback, "Playback can only happen when a node is inside the scene tree");
if (stream->is_monophonic() && is_playing()) {
- stop();
+ stop_callable.call();
}
stream_playback = stream->instantiate_playback();
ERR_FAIL_COND_V_MSG(stream_playback.is_null(), stream_playback, "Failed to instantiate playback.");
@@ -242,7 +242,7 @@ void AudioStreamPlayerInternal::set_stream(Ref<AudioStream> p_stream) {
if (stream.is_valid()) {
stream->disconnect(SNAME("parameter_list_changed"), callable_mp(this, &AudioStreamPlayerInternal::_update_stream_parameters));
}
- stop();
+ stop_callable.call();
stream = p_stream;
_update_stream_parameters();
if (stream.is_valid()) {
@@ -253,12 +253,12 @@ void AudioStreamPlayerInternal::set_stream(Ref<AudioStream> p_stream) {
void AudioStreamPlayerInternal::seek(float p_seconds) {
if (is_playing()) {
- stop();
+ stop_callable.call();
play_callable.call(p_seconds);
}
}
-void AudioStreamPlayerInternal::stop() {
+void AudioStreamPlayerInternal::stop_basic() {
for (Ref<AudioStreamPlayback> &playback : stream_playbacks) {
AudioServer::get_singleton()->stop_playback_stream(playback);
}
@@ -289,7 +289,7 @@ void AudioStreamPlayerInternal::set_playing(bool p_enable) {
if (p_enable) {
play_callable.call(0.0);
} else {
- stop();
+ stop_callable.call();
}
}
@@ -339,9 +339,10 @@ StringName AudioStreamPlayerInternal::get_bus() const {
return SceneStringName(Master);
}
-AudioStreamPlayerInternal::AudioStreamPlayerInternal(Node *p_node, const Callable &p_play_callable, bool p_physical) {
+AudioStreamPlayerInternal::AudioStreamPlayerInternal(Node *p_node, const Callable &p_play_callable, const Callable &p_stop_callable, bool p_physical) {
node = p_node;
play_callable = p_play_callable;
+ stop_callable = p_stop_callable;
physical = p_physical;
bus = SceneStringName(Master);
diff --git a/scene/audio/audio_stream_player_internal.h b/scene/audio/audio_stream_player_internal.h
index ec4489067e..7d8faeba06 100644
--- a/scene/audio/audio_stream_player_internal.h
+++ b/scene/audio/audio_stream_player_internal.h
@@ -53,6 +53,7 @@ private:
Node *node = nullptr;
Callable play_callable;
+ Callable stop_callable;
bool physical = false;
AudioServer::PlaybackType playback_type = AudioServer::PlaybackType::PLAYBACK_TYPE_DEFAULT;
@@ -94,7 +95,7 @@ public:
Ref<AudioStreamPlayback> play_basic();
void seek(float p_seconds);
- void stop();
+ void stop_basic();
bool is_playing() const;
float get_playback_position();
@@ -110,7 +111,7 @@ public:
void set_playback_type(AudioServer::PlaybackType p_playback_type);
AudioServer::PlaybackType get_playback_type() const;
- AudioStreamPlayerInternal(Node *p_node, const Callable &p_play_callable, bool p_physical);
+ AudioStreamPlayerInternal(Node *p_node, const Callable &p_play_callable, const Callable &p_stop_callable, bool p_physical);
};
#endif // AUDIO_STREAM_PLAYER_INTERNAL_H
diff --git a/scene/gui/code_edit.cpp b/scene/gui/code_edit.cpp
index 00b9a3478a..412eb83515 100644
--- a/scene/gui/code_edit.cpp
+++ b/scene/gui/code_edit.cpp
@@ -765,7 +765,7 @@ void CodeEdit::_backspace_internal(int p_caret) {
continue;
}
- if (to_line > 0 && _is_line_hidden(to_line - 1)) {
+ if (to_line > 0 && to_column == 0 && _is_line_hidden(to_line - 1)) {
unfold_line(to_line - 1);
}
diff --git a/scene/gui/control.cpp b/scene/gui/control.cpp
index d169e82e5d..2d425490d1 100644
--- a/scene/gui/control.cpp
+++ b/scene/gui/control.cpp
@@ -37,7 +37,7 @@
#include "core/os/keyboard.h"
#include "core/os/os.h"
#include "core/string/print_string.h"
-#include "core/string/translation.h"
+#include "core/string/translation_server.h"
#include "scene/gui/label.h"
#include "scene/gui/panel.h"
#include "scene/main/canvas_layer.h"
diff --git a/scene/gui/menu_bar.cpp b/scene/gui/menu_bar.cpp
index 4f8d818a6c..3264733548 100644
--- a/scene/gui/menu_bar.cpp
+++ b/scene/gui/menu_bar.cpp
@@ -121,8 +121,9 @@ void MenuBar::_open_popup(int p_index, bool p_focus_item) {
}
Rect2 item_rect = _get_menu_item_rect(p_index);
- Point2 screen_pos = get_screen_position() + item_rect.position * get_viewport()->get_canvas_transform().get_scale();
- Size2 screen_size = item_rect.size * get_viewport()->get_canvas_transform().get_scale();
+ Size2 canvas_scale = get_canvas_transform().get_scale();
+ Point2 screen_pos = get_screen_position() + item_rect.position * canvas_scale;
+ Size2 screen_size = item_rect.size * canvas_scale;
active_menu = p_index;
diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp
index 1da3668ebe..e9fe78e162 100644
--- a/scene/gui/rich_text_label.cpp
+++ b/scene/gui/rich_text_label.cpp
@@ -1448,6 +1448,7 @@ float RichTextLabel::_find_click_in_line(ItemFrame *p_frame, int p_line, const V
bool line_clicked = false;
float text_rect_begin = 0.0;
int char_pos = -1;
+ bool char_clicked = false;
Line &l = p_frame->lines[p_line];
MutexLock lock(l.text_buf->get_mutex());
@@ -1578,6 +1579,9 @@ float RichTextLabel::_find_click_in_line(ItemFrame *p_frame, int p_line, const V
}
if (p_click.y >= rect.position.y && p_click.y <= rect.position.y + rect.size.y) {
+ if (!p_meta) {
+ char_pos = rtl ? TS->shaped_text_get_range(rid).y : TS->shaped_text_get_range(rid).x;
+ }
if ((!rtl && p_click.x >= rect.position.x) || (rtl && p_click.x <= rect.position.x + rect.size.x)) {
if (p_meta) {
int64_t glyph_idx = TS->shaped_text_hit_test_grapheme(rid, p_click.x - rect.position.x);
@@ -1592,6 +1596,7 @@ float RichTextLabel::_find_click_in_line(ItemFrame *p_frame, int p_line, const V
obj_rect.position.y += baseline_y;
if (p_click.y >= obj_rect.position.y && p_click.y <= obj_rect.position.y + obj_rect.size.y) {
char_pos = glyphs[glyph_idx].start;
+ char_clicked = true;
}
break;
}
@@ -1602,18 +1607,21 @@ float RichTextLabel::_find_click_in_line(ItemFrame *p_frame, int p_line, const V
float fd = TS->font_get_descent(glyphs[glyph_idx].font_rid, glyphs[glyph_idx].font_size);
if (p_click.y >= baseline_y - fa && p_click.y <= baseline_y + fd) {
char_pos = glyphs[glyph_idx].start;
+ char_clicked = true;
}
} else if (!(glyphs[glyph_idx].flags & TextServer::GRAPHEME_IS_VIRTUAL)) {
// Hex code box.
Vector2 gl_size = TS->get_hex_code_box_size(glyphs[glyph_idx].font_size, glyphs[glyph_idx].index);
if (p_click.y >= baseline_y - gl_size.y * 0.9 && p_click.y <= baseline_y + gl_size.y * 0.2) {
char_pos = glyphs[glyph_idx].start;
+ char_clicked = true;
}
}
}
} else {
char_pos = TS->shaped_text_hit_test_position(rid, p_click.x - rect.position.x);
char_pos = TS->shaped_text_closest_character_pos(rid, char_pos);
+ char_clicked = true;
}
}
line_clicked = true;
@@ -1621,7 +1629,7 @@ float RichTextLabel::_find_click_in_line(ItemFrame *p_frame, int p_line, const V
}
// If table hit was detected, and line hit is in the table bounds use table hit.
- if (table_hit && (((char_pos + p_frame->lines[p_line].char_offset) >= table_range.x && (char_pos + p_frame->lines[p_line].char_offset) <= table_range.y) || char_pos == -1)) {
+ if (table_hit && (((char_pos + p_frame->lines[p_line].char_offset) >= table_range.x && (char_pos + p_frame->lines[p_line].char_offset) <= table_range.y) || !char_clicked)) {
if (r_click_frame != nullptr) {
*r_click_frame = table_click_frame;
}
diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp
index ac6ebd5cc1..422783b01b 100644
--- a/scene/gui/text_edit.cpp
+++ b/scene/gui/text_edit.cpp
@@ -38,7 +38,7 @@
#include "core/os/keyboard.h"
#include "core/os/os.h"
#include "core/string/string_builder.h"
-#include "core/string/translation.h"
+#include "core/string/translation_server.h"
#include "scene/gui/label.h"
#include "scene/main/window.h"
#include "scene/theme/theme_db.h"
@@ -4328,25 +4328,12 @@ Point2i TextEdit::get_line_column_at_pos(const Point2i &p_pos, bool p_allow_out_
return Point2i(text[row].length(), row);
}
- int col = 0;
int colx = p_pos.x - (theme_cache.style_normal->get_margin(SIDE_LEFT) + gutters_width + gutter_padding);
colx += first_visible_col;
if (!editable) {
colx -= theme_cache.style_readonly->get_offset().x / 2;
colx += theme_cache.style_normal->get_offset().x / 2;
}
- col = _get_char_pos_for_line(colx, row, wrap_index);
- if (get_line_wrapping_mode() != LineWrappingMode::LINE_WRAPPING_NONE && wrap_index < get_line_wrap_count(row)) {
- // Move back one if we are at the end of the row.
- Vector<String> rows2 = get_line_wrapped_text(row);
- int row_end_col = 0;
- for (int i = 0; i < wrap_index + 1; i++) {
- row_end_col += rows2[i].length();
- }
- if (col >= row_end_col) {
- col -= 1;
- }
- }
RID text_rid = text.get_line_data(row)->get_line_rid(wrap_index);
float wrap_indent = (text.is_indent_wrapped_lines() && wrap_index > 0) ? get_indent_level(row) * theme_cache.font->get_char_size(' ', theme_cache.font_size).width : 0.0;
@@ -4355,7 +4342,7 @@ Point2i TextEdit::get_line_column_at_pos(const Point2i &p_pos, bool p_allow_out_
} else {
colx -= wrap_indent;
}
- col = TS->shaped_text_hit_test_position(text_rid, colx);
+ int col = TS->shaped_text_hit_test_position(text_rid, colx);
if (!caret_mid_grapheme_enabled) {
col = TS->shaped_text_closest_character_pos(text_rid, col);
}
@@ -7531,7 +7518,7 @@ int TextEdit::_get_column_x_offset_for_line(int p_char, int p_line, int p_column
int row = 0;
Vector<Vector2i> rows2 = text.get_line_wrap_ranges(p_line);
for (int i = 0; i < rows2.size(); i++) {
- if ((p_char >= rows2[i].x) && (p_char <= rows2[i].y)) {
+ if ((p_char >= rows2[i].x) && (p_char < rows2[i].y || (i == rows2.size() - 1 && p_char == rows2[i].y))) {
row = i;
break;
}
diff --git a/scene/main/node.cpp b/scene/main/node.cpp
index 5c46abc732..de6d49761b 100644
--- a/scene/main/node.cpp
+++ b/scene/main/node.cpp
@@ -2809,9 +2809,11 @@ Node *Node::duplicate(int p_flags) const {
ERR_THREAD_GUARD_V(nullptr);
Node *dupe = _duplicate(p_flags);
+ ERR_FAIL_NULL_V_MSG(dupe, nullptr, "Failed to duplicate node.");
+
_duplicate_properties(this, this, dupe, p_flags);
- if (dupe && (p_flags & DUPLICATE_SIGNALS)) {
+ if (p_flags & DUPLICATE_SIGNALS) {
_duplicate_signals(this, dupe);
}
@@ -2827,6 +2829,8 @@ Node *Node::duplicate_from_editor(HashMap<const Node *, Node *> &r_duplimap, con
int flags = DUPLICATE_SIGNALS | DUPLICATE_GROUPS | DUPLICATE_SCRIPTS | DUPLICATE_USE_INSTANTIATION | DUPLICATE_FROM_EDITOR;
Node *dupe = _duplicate(flags, &r_duplimap);
+ ERR_FAIL_NULL_V_MSG(dupe, nullptr, "Failed to duplicate node.");
+
_duplicate_properties(this, this, dupe, flags);
// This is used by SceneTreeDock's paste functionality. When pasting to foreign scene, resources are duplicated.
diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp
index c85fda2371..0036247625 100644
--- a/scene/main/viewport.cpp
+++ b/scene/main/viewport.cpp
@@ -5070,6 +5070,7 @@ void SubViewport::_internal_set_size(const Size2i &p_size, bool p_force) {
if (c) {
c->update_minimum_size();
+ c->queue_redraw();
}
}
diff --git a/scene/main/window.cpp b/scene/main/window.cpp
index 23843938a4..19fb62df2e 100644
--- a/scene/main/window.cpp
+++ b/scene/main/window.cpp
@@ -34,7 +34,7 @@
#include "core/config/project_settings.h"
#include "core/debugger/engine_debugger.h"
#include "core/input/shortcut.h"
-#include "core/string/translation.h"
+#include "core/string/translation_server.h"
#include "core/variant/variant_parser.h"
#include "scene/gui/control.h"
#include "scene/theme/theme_db.h"
@@ -1602,7 +1602,7 @@ Size2 Window::_get_contents_minimum_size() const {
}
}
- return max;
+ return max * content_scale_factor;
}
void Window::child_controls_changed() {
diff --git a/scene/resources/2d/navigation_mesh_source_geometry_data_2d.cpp b/scene/resources/2d/navigation_mesh_source_geometry_data_2d.cpp
index 686560829b..ee6fc61af3 100644
--- a/scene/resources/2d/navigation_mesh_source_geometry_data_2d.cpp
+++ b/scene/resources/2d/navigation_mesh_source_geometry_data_2d.cpp
@@ -37,6 +37,7 @@ void NavigationMeshSourceGeometryData2D::clear() {
traversable_outlines.clear();
obstruction_outlines.clear();
_projected_obstructions.clear();
+ bounds_dirty = true;
}
bool NavigationMeshSourceGeometryData2D::has_data() {
@@ -47,16 +48,19 @@ bool NavigationMeshSourceGeometryData2D::has_data() {
void NavigationMeshSourceGeometryData2D::clear_projected_obstructions() {
RWLockWrite write_lock(geometry_rwlock);
_projected_obstructions.clear();
+ bounds_dirty = true;
}
void NavigationMeshSourceGeometryData2D::_set_traversable_outlines(const Vector<Vector<Vector2>> &p_traversable_outlines) {
RWLockWrite write_lock(geometry_rwlock);
traversable_outlines = p_traversable_outlines;
+ bounds_dirty = true;
}
void NavigationMeshSourceGeometryData2D::_set_obstruction_outlines(const Vector<Vector<Vector2>> &p_obstruction_outlines) {
RWLockWrite write_lock(geometry_rwlock);
obstruction_outlines = p_obstruction_outlines;
+ bounds_dirty = true;
}
const Vector<Vector<Vector2>> &NavigationMeshSourceGeometryData2D::_get_traversable_outlines() const {
@@ -73,6 +77,7 @@ void NavigationMeshSourceGeometryData2D::_add_traversable_outline(const Vector<V
if (p_shape_outline.size() > 1) {
RWLockWrite write_lock(geometry_rwlock);
traversable_outlines.push_back(p_shape_outline);
+ bounds_dirty = true;
}
}
@@ -80,6 +85,7 @@ void NavigationMeshSourceGeometryData2D::_add_obstruction_outline(const Vector<V
if (p_shape_outline.size() > 1) {
RWLockWrite write_lock(geometry_rwlock);
obstruction_outlines.push_back(p_shape_outline);
+ bounds_dirty = true;
}
}
@@ -89,6 +95,7 @@ void NavigationMeshSourceGeometryData2D::set_traversable_outlines(const TypedArr
for (int i = 0; i < p_traversable_outlines.size(); i++) {
traversable_outlines.write[i] = p_traversable_outlines[i];
}
+ bounds_dirty = true;
}
TypedArray<Vector<Vector2>> NavigationMeshSourceGeometryData2D::get_traversable_outlines() const {
@@ -108,6 +115,7 @@ void NavigationMeshSourceGeometryData2D::set_obstruction_outlines(const TypedArr
for (int i = 0; i < p_obstruction_outlines.size(); i++) {
obstruction_outlines.write[i] = p_obstruction_outlines[i];
}
+ bounds_dirty = true;
}
TypedArray<Vector<Vector2>> NavigationMeshSourceGeometryData2D::get_obstruction_outlines() const {
@@ -128,6 +136,7 @@ void NavigationMeshSourceGeometryData2D::append_traversable_outlines(const Typed
for (int i = traversable_outlines_size; i < p_traversable_outlines.size(); i++) {
traversable_outlines.write[i] = p_traversable_outlines[i];
}
+ bounds_dirty = true;
}
void NavigationMeshSourceGeometryData2D::append_obstruction_outlines(const TypedArray<Vector<Vector2>> &p_obstruction_outlines) {
@@ -137,6 +146,7 @@ void NavigationMeshSourceGeometryData2D::append_obstruction_outlines(const Typed
for (int i = obstruction_outlines_size; i < p_obstruction_outlines.size(); i++) {
obstruction_outlines.write[i] = p_obstruction_outlines[i];
}
+ bounds_dirty = true;
}
void NavigationMeshSourceGeometryData2D::add_traversable_outline(const PackedVector2Array &p_shape_outline) {
@@ -148,6 +158,7 @@ void NavigationMeshSourceGeometryData2D::add_traversable_outline(const PackedVec
traversable_outline.write[i] = p_shape_outline[i];
}
traversable_outlines.push_back(traversable_outline);
+ bounds_dirty = true;
}
}
@@ -160,6 +171,7 @@ void NavigationMeshSourceGeometryData2D::add_obstruction_outline(const PackedVec
obstruction_outline.write[i] = p_shape_outline[i];
}
obstruction_outlines.push_back(obstruction_outline);
+ bounds_dirty = true;
}
}
@@ -176,6 +188,7 @@ void NavigationMeshSourceGeometryData2D::merge(const Ref<NavigationMeshSourceGeo
traversable_outlines.append_array(other_traversable_outlines);
obstruction_outlines.append_array(other_obstruction_outlines);
_projected_obstructions.append_array(other_projected_obstructions);
+ bounds_dirty = true;
}
void NavigationMeshSourceGeometryData2D::add_projected_obstruction(const Vector<Vector2> &p_vertices, bool p_carve) {
@@ -195,6 +208,7 @@ void NavigationMeshSourceGeometryData2D::add_projected_obstruction(const Vector<
RWLockWrite write_lock(geometry_rwlock);
_projected_obstructions.push_back(projected_obstruction);
+ bounds_dirty = true;
}
void NavigationMeshSourceGeometryData2D::set_projected_obstructions(const Array &p_array) {
@@ -217,6 +231,7 @@ void NavigationMeshSourceGeometryData2D::set_projected_obstructions(const Array
RWLockWrite write_lock(geometry_rwlock);
_projected_obstructions.push_back(projected_obstruction);
+ bounds_dirty = true;
}
}
@@ -266,6 +281,7 @@ void NavigationMeshSourceGeometryData2D::set_data(const Vector<Vector<Vector2>>
traversable_outlines = p_traversable_outlines;
obstruction_outlines = p_obstruction_outlines;
_projected_obstructions = p_projected_obstructions;
+ bounds_dirty = true;
}
void NavigationMeshSourceGeometryData2D::get_data(Vector<Vector<Vector2>> &r_traversable_outlines, Vector<Vector<Vector2>> &r_obstruction_outlines, Vector<ProjectedObstruction> &r_projected_obstructions) {
@@ -275,6 +291,58 @@ void NavigationMeshSourceGeometryData2D::get_data(Vector<Vector<Vector2>> &r_tra
r_projected_obstructions = _projected_obstructions;
}
+Rect2 NavigationMeshSourceGeometryData2D::get_bounds() {
+ geometry_rwlock.read_lock();
+
+ if (bounds_dirty) {
+ geometry_rwlock.read_unlock();
+ RWLockWrite write_lock(geometry_rwlock);
+
+ bounds_dirty = false;
+ bounds = Rect2();
+ bool first_vertex = true;
+
+ for (const Vector<Vector2> &traversable_outline : traversable_outlines) {
+ for (const Vector2 &traversable_point : traversable_outline) {
+ if (first_vertex) {
+ first_vertex = false;
+ bounds.position = traversable_point;
+ } else {
+ bounds.expand_to(traversable_point);
+ }
+ }
+ }
+
+ for (const Vector<Vector2> &obstruction_outline : obstruction_outlines) {
+ for (const Vector2 &obstruction_point : obstruction_outline) {
+ if (first_vertex) {
+ first_vertex = false;
+ bounds.position = obstruction_point;
+ } else {
+ bounds.expand_to(obstruction_point);
+ }
+ }
+ }
+
+ for (const ProjectedObstruction &projected_obstruction : _projected_obstructions) {
+ for (int i = 0; i < projected_obstruction.vertices.size() / 2; i++) {
+ const Vector2 vertex = Vector2(projected_obstruction.vertices[i * 2], projected_obstruction.vertices[i * 2 + 1]);
+ if (first_vertex) {
+ first_vertex = false;
+ bounds.position = vertex;
+ } else {
+ bounds.expand_to(vertex);
+ }
+ }
+ }
+ } else {
+ geometry_rwlock.read_unlock();
+ }
+
+ RWLockRead read_lock(geometry_rwlock);
+ return bounds;
+}
+
void NavigationMeshSourceGeometryData2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("clear"), &NavigationMeshSourceGeometryData2D::clear);
ClassDB::bind_method(D_METHOD("has_data"), &NavigationMeshSourceGeometryData2D::has_data);
@@ -298,6 +366,8 @@ void NavigationMeshSourceGeometryData2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_projected_obstructions", "projected_obstructions"), &NavigationMeshSourceGeometryData2D::set_projected_obstructions);
ClassDB::bind_method(D_METHOD("get_projected_obstructions"), &NavigationMeshSourceGeometryData2D::get_projected_obstructions);
+ ClassDB::bind_method(D_METHOD("get_bounds"), &NavigationMeshSourceGeometryData2D::get_bounds);
+
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "traversable_outlines", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "set_traversable_outlines", "get_traversable_outlines");
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "obstruction_outlines", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "set_obstruction_outlines", "get_obstruction_outlines");
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "projected_obstructions", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "set_projected_obstructions", "get_projected_obstructions");
diff --git a/scene/resources/2d/navigation_mesh_source_geometry_data_2d.h b/scene/resources/2d/navigation_mesh_source_geometry_data_2d.h
index 01e97eee48..b29c106fb5 100644
--- a/scene/resources/2d/navigation_mesh_source_geometry_data_2d.h
+++ b/scene/resources/2d/navigation_mesh_source_geometry_data_2d.h
@@ -42,6 +42,9 @@ class NavigationMeshSourceGeometryData2D : public Resource {
Vector<Vector<Vector2>> traversable_outlines;
Vector<Vector<Vector2>> obstruction_outlines;
+ Rect2 bounds;
+ bool bounds_dirty = true;
+
public:
struct ProjectedObstruction;
@@ -103,6 +106,8 @@ public:
void set_data(const Vector<Vector<Vector2>> &p_traversable_outlines, const Vector<Vector<Vector2>> &p_obstruction_outlines, Vector<ProjectedObstruction> &p_projected_obstructions);
void get_data(Vector<Vector<Vector2>> &r_traversable_outlines, Vector<Vector<Vector2>> &r_obstruction_outlines, Vector<ProjectedObstruction> &r_projected_obstructions);
+ Rect2 get_bounds();
+
NavigationMeshSourceGeometryData2D() {}
~NavigationMeshSourceGeometryData2D() { clear(); }
};
diff --git a/scene/resources/2d/navigation_polygon.cpp b/scene/resources/2d/navigation_polygon.cpp
index 4a290db86b..3dfa906e3b 100644
--- a/scene/resources/2d/navigation_polygon.cpp
+++ b/scene/resources/2d/navigation_polygon.cpp
@@ -104,7 +104,7 @@ void NavigationPolygon::_set_polygons(const TypedArray<Vector<int32_t>> &p_array
}
polygons.resize(p_array.size());
for (int i = 0; i < p_array.size(); i++) {
- polygons.write[i].indices = p_array[i];
+ polygons.write[i] = p_array[i];
}
}
@@ -113,7 +113,7 @@ TypedArray<Vector<int32_t>> NavigationPolygon::_get_polygons() const {
TypedArray<Vector<int32_t>> ret;
ret.resize(polygons.size());
for (int i = 0; i < ret.size(); i++) {
- ret[i] = polygons[i].indices;
+ ret[i] = polygons[i];
}
return ret;
@@ -141,9 +141,7 @@ TypedArray<Vector<Vector2>> NavigationPolygon::_get_outlines() const {
void NavigationPolygon::add_polygon(const Vector<int> &p_polygon) {
RWLockWrite write_lock(rwlock);
- Polygon polygon;
- polygon.indices = p_polygon;
- polygons.push_back(polygon);
+ polygons.push_back(p_polygon);
{
MutexLock lock(navigation_mesh_generation);
navigation_mesh.unref();
@@ -164,7 +162,7 @@ int NavigationPolygon::get_polygon_count() const {
Vector<int> NavigationPolygon::get_polygon(int p_idx) {
RWLockRead read_lock(rwlock);
ERR_FAIL_INDEX_V(p_idx, polygons.size(), Vector<int>());
- return polygons[p_idx].indices;
+ return polygons[p_idx];
}
void NavigationPolygon::clear_polygons() {
@@ -189,10 +187,19 @@ void NavigationPolygon::clear() {
void NavigationPolygon::set_data(const Vector<Vector2> &p_vertices, const Vector<Vector<int>> &p_polygons) {
RWLockWrite write_lock(rwlock);
vertices = p_vertices;
- polygons.resize(p_polygons.size());
- for (int i = 0; i < p_polygons.size(); i++) {
- polygons.write[i].indices = p_polygons[i];
+ polygons = p_polygons;
+ {
+ MutexLock lock(navigation_mesh_generation);
+ navigation_mesh.unref();
}
+}
+
+void NavigationPolygon::set_data(const Vector<Vector2> &p_vertices, const Vector<Vector<int>> &p_polygons, const Vector<Vector<Vector2>> &p_outlines) {
+ RWLockWrite write_lock(rwlock);
+ vertices = p_vertices;
+ polygons = p_polygons;
+ outlines = p_outlines;
+ rect_cache_dirty = true;
{
MutexLock lock(navigation_mesh_generation);
navigation_mesh.unref();
@@ -202,10 +209,14 @@ void NavigationPolygon::set_data(const Vector<Vector2> &p_vertices, const Vector
void NavigationPolygon::get_data(Vector<Vector2> &r_vertices, Vector<Vector<int>> &r_polygons) {
RWLockRead read_lock(rwlock);
r_vertices = vertices;
- r_polygons.resize(polygons.size());
- for (int i = 0; i < polygons.size(); i++) {
- r_polygons.write[i] = polygons[i].indices;
- }
+ r_polygons = polygons;
+}
+
+void NavigationPolygon::get_data(Vector<Vector2> &r_vertices, Vector<Vector<int>> &r_polygons, Vector<Vector<Vector2>> &r_outlines) {
+ RWLockRead read_lock(rwlock);
+ r_vertices = vertices;
+ r_polygons = polygons;
+ r_outlines = outlines;
}
Ref<NavigationMesh> NavigationPolygon::get_navigation_mesh() {
@@ -237,6 +248,31 @@ Ref<NavigationMesh> NavigationPolygon::get_navigation_mesh() {
return navigation_mesh;
}
+void NavigationPolygon::set_outlines(const Vector<Vector<Vector2>> &p_outlines) {
+ RWLockWrite write_lock(rwlock);
+ outlines = p_outlines;
+ rect_cache_dirty = true;
+}
+
+Vector<Vector<Vector2>> NavigationPolygon::get_outlines() const {
+ RWLockRead read_lock(rwlock);
+ return outlines;
+}
+
+void NavigationPolygon::set_polygons(const Vector<Vector<int>> &p_polygons) {
+ RWLockWrite write_lock(rwlock);
+ polygons = p_polygons;
+ {
+ MutexLock lock(navigation_mesh_generation);
+ navigation_mesh.unref();
+ }
+}
+
+Vector<Vector<int>> NavigationPolygon::get_polygons() const {
+ RWLockRead read_lock(rwlock);
+ return polygons;
+}
+
void NavigationPolygon::add_outline(const Vector<Vector2> &p_outline) {
RWLockWrite write_lock(rwlock);
outlines.push_back(p_outline);
@@ -364,7 +400,7 @@ void NavigationPolygon::make_polygons_from_outlines() {
for (List<TPPLPoly>::Element *I = out_poly.front(); I; I = I->next()) {
TPPLPoly &tp = I->get();
- struct Polygon p;
+ Vector<int> p;
for (int64_t i = 0; i < tp.GetNumPoints(); i++) {
HashMap<Vector2, int>::Iterator E = points.find(tp[i]);
@@ -372,7 +408,7 @@ void NavigationPolygon::make_polygons_from_outlines() {
E = points.insert(tp[i], vertices.size());
vertices.push_back(tp[i]);
}
- p.indices.push_back(E->value);
+ p.push_back(E->value);
}
polygons.push_back(p);
@@ -400,6 +436,15 @@ real_t NavigationPolygon::get_border_size() const {
return border_size;
}
+void NavigationPolygon::set_sample_partition_type(SamplePartitionType p_value) {
+ ERR_FAIL_INDEX(p_value, SAMPLE_PARTITION_MAX);
+ partition_type = p_value;
+}
+
+NavigationPolygon::SamplePartitionType NavigationPolygon::get_sample_partition_type() const {
+ return partition_type;
+}
+
void NavigationPolygon::set_parsed_geometry_type(ParsedGeometryType p_geometry_type) {
ERR_FAIL_INDEX(p_geometry_type, PARSED_GEOMETRY_MAX);
parsed_geometry_type = p_geometry_type;
@@ -514,6 +559,9 @@ void NavigationPolygon::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_border_size", "border_size"), &NavigationPolygon::set_border_size);
ClassDB::bind_method(D_METHOD("get_border_size"), &NavigationPolygon::get_border_size);
+ ClassDB::bind_method(D_METHOD("set_sample_partition_type", "sample_partition_type"), &NavigationPolygon::set_sample_partition_type);
+ ClassDB::bind_method(D_METHOD("get_sample_partition_type"), &NavigationPolygon::get_sample_partition_type);
+
ClassDB::bind_method(D_METHOD("set_parsed_geometry_type", "geometry_type"), &NavigationPolygon::set_parsed_geometry_type);
ClassDB::bind_method(D_METHOD("get_parsed_geometry_type"), &NavigationPolygon::get_parsed_geometry_type);
@@ -543,6 +591,8 @@ void NavigationPolygon::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "polygons", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_polygons", "_get_polygons");
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "outlines", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_outlines", "_get_outlines");
+ ADD_GROUP("Sampling", "sample_");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "sample_partition_type", PROPERTY_HINT_ENUM, "Convex Partition,Triangulate"), "set_sample_partition_type", "get_sample_partition_type");
ADD_GROUP("Geometry", "");
ADD_PROPERTY(PropertyInfo(Variant::INT, "parsed_geometry_type", PROPERTY_HINT_ENUM, "Mesh Instances,Static Colliders,Meshes and Static Colliders"), "set_parsed_geometry_type", "get_parsed_geometry_type");
ADD_PROPERTY(PropertyInfo(Variant::INT, "parsed_collision_mask", PROPERTY_HINT_LAYERS_2D_PHYSICS), "set_parsed_collision_mask", "get_parsed_collision_mask");
@@ -559,6 +609,10 @@ void NavigationPolygon::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::RECT2, "baking_rect"), "set_baking_rect", "get_baking_rect");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "baking_rect_offset"), "set_baking_rect_offset", "get_baking_rect_offset");
+ BIND_ENUM_CONSTANT(SAMPLE_PARTITION_CONVEX_PARTITION);
+ BIND_ENUM_CONSTANT(SAMPLE_PARTITION_TRIANGULATE);
+ BIND_ENUM_CONSTANT(SAMPLE_PARTITION_MAX);
+
BIND_ENUM_CONSTANT(PARSED_GEOMETRY_MESH_INSTANCES);
BIND_ENUM_CONSTANT(PARSED_GEOMETRY_STATIC_COLLIDERS);
BIND_ENUM_CONSTANT(PARSED_GEOMETRY_BOTH);
diff --git a/scene/resources/2d/navigation_polygon.h b/scene/resources/2d/navigation_polygon.h
index 4e99660b0e..86bda47ace 100644
--- a/scene/resources/2d/navigation_polygon.h
+++ b/scene/resources/2d/navigation_polygon.h
@@ -39,10 +39,7 @@ class NavigationPolygon : public Resource {
RWLock rwlock;
Vector<Vector2> vertices;
- struct Polygon {
- Vector<int> indices;
- };
- Vector<Polygon> polygons;
+ Vector<Vector<int>> polygons;
Vector<Vector<Vector2>> outlines;
Vector<Vector<Vector2>> baked_outlines;
@@ -74,6 +71,11 @@ public:
Rect2 _edit_get_rect() const;
bool _edit_is_selected_on_click(const Point2 &p_point, double p_tolerance) const;
#endif
+ enum SamplePartitionType {
+ SAMPLE_PARTITION_CONVEX_PARTITION = 0,
+ SAMPLE_PARTITION_TRIANGULATE,
+ SAMPLE_PARTITION_MAX
+ };
enum ParsedGeometryType {
PARSED_GEOMETRY_MESH_INSTANCES = 0,
@@ -91,6 +93,7 @@ public:
real_t agent_radius = 10.0f;
+ SamplePartitionType partition_type = SAMPLE_PARTITION_CONVEX_PARTITION;
ParsedGeometryType parsed_geometry_type = PARSED_GEOMETRY_BOTH;
uint32_t parsed_collision_mask = 0xFFFFFFFF;
@@ -109,6 +112,8 @@ public:
Vector<Vector2> get_outline(int p_idx) const;
void remove_outline(int p_idx);
int get_outline_count() const;
+ void set_outlines(const Vector<Vector<Vector2>> &p_outlines);
+ Vector<Vector<Vector2>> get_outlines() const;
void clear_outlines();
#ifndef DISABLE_DEPRECATED
@@ -116,10 +121,13 @@ public:
#endif // DISABLE_DEPRECATED
void set_polygons(const Vector<Vector<int>> &p_polygons);
- const Vector<Vector<int>> &get_polygons() const;
+ Vector<Vector<int>> get_polygons() const;
Vector<int> get_polygon(int p_idx);
void clear_polygons();
+ void set_sample_partition_type(SamplePartitionType p_value);
+ SamplePartitionType get_sample_partition_type() const;
+
void set_parsed_geometry_type(ParsedGeometryType p_geometry_type);
ParsedGeometryType get_parsed_geometry_type() const;
@@ -155,12 +163,15 @@ public:
void clear();
void set_data(const Vector<Vector2> &p_vertices, const Vector<Vector<int>> &p_polygons);
+ void set_data(const Vector<Vector2> &p_vertices, const Vector<Vector<int>> &p_polygons, const Vector<Vector<Vector2>> &p_outlines);
void get_data(Vector<Vector2> &r_vertices, Vector<Vector<int>> &r_polygons);
+ void get_data(Vector<Vector2> &r_vertices, Vector<Vector<int>> &r_polygons, Vector<Vector<Vector2>> &r_outlines);
NavigationPolygon() {}
~NavigationPolygon() {}
};
+VARIANT_ENUM_CAST(NavigationPolygon::SamplePartitionType);
VARIANT_ENUM_CAST(NavigationPolygon::ParsedGeometryType);
VARIANT_ENUM_CAST(NavigationPolygon::SourceGeometryMode);
diff --git a/scene/resources/2d/tile_set.cpp b/scene/resources/2d/tile_set.cpp
index d124577d25..dd6ae5096a 100644
--- a/scene/resources/2d/tile_set.cpp
+++ b/scene/resources/2d/tile_set.cpp
@@ -5275,11 +5275,26 @@ Rect2i TileSetAtlasSource::get_tile_texture_region(Vector2i p_atlas_coords, int
bool TileSetAtlasSource::is_position_in_tile_texture_region(const Vector2i p_atlas_coords, int p_alternative_tile, Vector2 p_position) const {
Size2 size = get_tile_texture_region(p_atlas_coords).size;
- Rect2 rect = Rect2(-size / 2 - get_tile_data(p_atlas_coords, p_alternative_tile)->get_texture_origin(), size);
+ TileData *tile_data = get_tile_data(p_atlas_coords, p_alternative_tile);
+ if (tile_data->get_transpose()) {
+ size = Size2(size.y, size.x);
+ }
+ Rect2 rect = Rect2(-size / 2 - tile_data->get_texture_origin(), size);
return rect.has_point(p_position);
}
+bool TileSetAtlasSource::is_rect_in_tile_texture_region(const Vector2i p_atlas_coords, int p_alternative_tile, Rect2 p_rect) const {
+ Size2 size = get_tile_texture_region(p_atlas_coords).size;
+ TileData *tile_data = get_tile_data(p_atlas_coords, p_alternative_tile);
+ if (tile_data->get_transpose()) {
+ size = Size2(size.y, size.x);
+ }
+ Rect2 rect = Rect2(-size / 2 - tile_data->get_texture_origin(), size);
+
+ return p_rect.intersection(rect) == p_rect;
+}
+
int TileSetAtlasSource::alternative_no_transform(int p_alternative_id) {
return p_alternative_id & ~(TRANSFORM_FLIP_H | TRANSFORM_FLIP_V | TRANSFORM_TRANSPOSE);
}
@@ -6487,18 +6502,19 @@ Ref<NavigationPolygon> TileData::get_navigation_polygon(int p_layer_id, bool p_f
transformed_polygon.instantiate();
PackedVector2Array new_points = get_transformed_vertices(layer_tile_data.navigation_polygon->get_vertices(), p_flip_h, p_flip_v, p_transpose);
- transformed_polygon->set_vertices(new_points);
- int num_polygons = layer_tile_data.navigation_polygon->get_polygon_count();
- for (int i = 0; i < num_polygons; ++i) {
- transformed_polygon->add_polygon(layer_tile_data.navigation_polygon->get_polygon(i));
- }
+ const Vector<Vector<Vector2>> outlines = layer_tile_data.navigation_polygon->get_outlines();
+ int outline_count = outlines.size();
- for (int i = 0; i < layer_tile_data.navigation_polygon->get_outline_count(); i++) {
- PackedVector2Array new_outline = get_transformed_vertices(layer_tile_data.navigation_polygon->get_outline(i), p_flip_h, p_flip_v, p_transpose);
- transformed_polygon->add_outline(new_outline);
+ Vector<Vector<Vector2>> new_outlines;
+ new_outlines.resize(outline_count);
+
+ for (int i = 0; i < outline_count; i++) {
+ new_outlines.write[i] = get_transformed_vertices(outlines[i], p_flip_h, p_flip_v, p_transpose);
}
+ transformed_polygon->set_data(new_points, layer_tile_data.navigation_polygon->get_polygons(), new_outlines);
+
layer_tile_data.transformed_navigation_polygon[key] = transformed_polygon;
return transformed_polygon;
} else {
diff --git a/scene/resources/2d/tile_set.h b/scene/resources/2d/tile_set.h
index e083fa45b9..51df972c8d 100644
--- a/scene/resources/2d/tile_set.h
+++ b/scene/resources/2d/tile_set.h
@@ -763,6 +763,7 @@ public:
Vector2i get_atlas_grid_size() const;
Rect2i get_tile_texture_region(Vector2i p_atlas_coords, int p_frame = 0) const;
bool is_position_in_tile_texture_region(const Vector2i p_atlas_coords, int p_alternative_tile, Vector2 p_position) const;
+ bool is_rect_in_tile_texture_region(const Vector2i p_atlas_coords, int p_alternative_tile, Rect2 p_rect) const;
static int alternative_no_transform(int p_alternative_id);
diff --git a/scene/resources/3d/importer_mesh.cpp b/scene/resources/3d/importer_mesh.cpp
index 91531699b4..b0633c06b9 100644
--- a/scene/resources/3d/importer_mesh.cpp
+++ b/scene/resources/3d/importer_mesh.cpp
@@ -256,6 +256,33 @@ void ImporterMesh::set_surface_material(int p_surface, const Ref<Material> &p_ma
mesh.unref();
}
+void ImporterMesh::optimize_indices_for_cache() {
+ if (!SurfaceTool::optimize_vertex_cache_func) {
+ return;
+ }
+
+ for (int i = 0; i < surfaces.size(); i++) {
+ if (surfaces[i].primitive != Mesh::PRIMITIVE_TRIANGLES) {
+ continue;
+ }
+
+ Vector<Vector3> vertices = surfaces[i].arrays[RS::ARRAY_VERTEX];
+ PackedInt32Array indices = surfaces[i].arrays[RS::ARRAY_INDEX];
+
+ unsigned int index_count = indices.size();
+ unsigned int vertex_count = vertices.size();
+
+ if (index_count == 0) {
+ continue;
+ }
+
+ 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;
+ }
+}
+
#define VERTEX_SKIN_FUNC(bone_count, vert_idx, read_array, write_array, transform_array, bone_array, weight_array) \
Vector3 transformed_vert; \
for (unsigned int weight_idx = 0; weight_idx < bone_count; weight_idx++) { \
@@ -822,6 +849,10 @@ void ImporterMesh::create_shadow_mesh() {
index_wptr[j] = vertex_remap[index];
}
+ if (SurfaceTool::optimize_vertex_cache_func) {
+ 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.
@@ -840,6 +871,10 @@ void ImporterMesh::create_shadow_mesh() {
index_wptr[k] = vertex_remap[index];
}
+ if (SurfaceTool::optimize_vertex_cache_func) {
+ 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;
}
}
@@ -1073,9 +1108,12 @@ Ref<NavigationMesh> ImporterMesh::create_navigation_mesh() {
}
HashMap<Vector3, int> unique_vertices;
- LocalVector<int> face_indices;
+ Vector<Vector<int>> face_polygons;
+ face_polygons.resize(faces.size());
for (int i = 0; i < faces.size(); i++) {
+ Vector<int> face_indices;
+ face_indices.resize(3);
for (int j = 0; j < 3; j++) {
Vector3 v = faces[i].vertex[j];
int idx;
@@ -1085,8 +1123,9 @@ Ref<NavigationMesh> ImporterMesh::create_navigation_mesh() {
idx = unique_vertices.size();
unique_vertices[v] = idx;
}
- face_indices.push_back(idx);
+ face_indices.write[j] = idx;
}
+ face_polygons.write[i] = face_indices;
}
Vector<Vector3> vertices;
@@ -1097,16 +1136,7 @@ Ref<NavigationMesh> ImporterMesh::create_navigation_mesh() {
Ref<NavigationMesh> nm;
nm.instantiate();
- nm->set_vertices(vertices);
-
- Vector<int> v3;
- v3.resize(3);
- for (uint32_t i = 0; i < face_indices.size(); i += 3) {
- v3.write[0] = face_indices[i + 0];
- v3.write[1] = face_indices[i + 1];
- v3.write[2] = face_indices[i + 2];
- nm->add_polygon(v3);
- }
+ nm->set_data(vertices, face_polygons);
return nm;
}
diff --git a/scene/resources/3d/importer_mesh.h b/scene/resources/3d/importer_mesh.h
index 777f936030..5eb4ee884e 100644
--- a/scene/resources/3d/importer_mesh.h
+++ b/scene/resources/3d/importer_mesh.h
@@ -114,6 +114,8 @@ public:
void set_surface_material(int p_surface, const Ref<Material> &p_material);
+ void optimize_indices_for_cache();
+
void generate_lods(float p_normal_merge_angle, float p_normal_split_angle, Array p_skin_pose_transform_array, bool p_raycast_normals = false);
void create_shadow_mesh();
diff --git a/scene/resources/3d/navigation_mesh_source_geometry_data_3d.cpp b/scene/resources/3d/navigation_mesh_source_geometry_data_3d.cpp
index 4c9f381aba..c55e25fcae 100644
--- a/scene/resources/3d/navigation_mesh_source_geometry_data_3d.cpp
+++ b/scene/resources/3d/navigation_mesh_source_geometry_data_3d.cpp
@@ -33,6 +33,7 @@
void NavigationMeshSourceGeometryData3D::set_vertices(const Vector<float> &p_vertices) {
RWLockWrite write_lock(geometry_rwlock);
vertices = p_vertices;
+ bounds_dirty = true;
}
const Vector<float> &NavigationMeshSourceGeometryData3D::get_vertices() const {
@@ -44,6 +45,7 @@ void NavigationMeshSourceGeometryData3D::set_indices(const Vector<int> &p_indice
ERR_FAIL_COND(vertices.size() < p_indices.size());
RWLockWrite write_lock(geometry_rwlock);
indices = p_indices;
+ bounds_dirty = true;
}
const Vector<int> &NavigationMeshSourceGeometryData3D::get_indices() const {
@@ -63,6 +65,7 @@ void NavigationMeshSourceGeometryData3D::append_arrays(const Vector<float> &p_ve
for (int64_t i = number_of_indices_before_merge; i < indices.size(); i++) {
indices.set(i, indices[i] + number_of_vertices_before_merge / 3);
}
+ bounds_dirty = true;
}
bool NavigationMeshSourceGeometryData3D::has_data() {
@@ -75,11 +78,13 @@ void NavigationMeshSourceGeometryData3D::clear() {
vertices.clear();
indices.clear();
_projected_obstructions.clear();
+ bounds_dirty = true;
}
void NavigationMeshSourceGeometryData3D::clear_projected_obstructions() {
RWLockWrite write_lock(geometry_rwlock);
_projected_obstructions.clear();
+ bounds_dirty = true;
}
void NavigationMeshSourceGeometryData3D::_add_vertex(const Vector3 &p_vec3) {
@@ -207,12 +212,14 @@ void NavigationMeshSourceGeometryData3D::add_mesh_array(const Array &p_mesh_arra
ERR_FAIL_COND(p_mesh_array.size() != Mesh::ARRAY_MAX);
RWLockWrite write_lock(geometry_rwlock);
_add_mesh_array(p_mesh_array, root_node_transform * p_xform);
+ bounds_dirty = true;
}
void NavigationMeshSourceGeometryData3D::add_faces(const PackedVector3Array &p_faces, const Transform3D &p_xform) {
ERR_FAIL_COND(p_faces.size() % 3 != 0);
RWLockWrite write_lock(geometry_rwlock);
_add_faces(p_faces, root_node_transform * p_xform);
+ bounds_dirty = true;
}
void NavigationMeshSourceGeometryData3D::merge(const Ref<NavigationMeshSourceGeometryData3D> &p_other_geometry) {
@@ -236,6 +243,7 @@ void NavigationMeshSourceGeometryData3D::merge(const Ref<NavigationMeshSourceGeo
}
_projected_obstructions.append_array(other_projected_obstructions);
+ bounds_dirty = true;
}
void NavigationMeshSourceGeometryData3D::add_projected_obstruction(const Vector<Vector3> &p_vertices, float p_elevation, float p_height, bool p_carve) {
@@ -259,6 +267,7 @@ void NavigationMeshSourceGeometryData3D::add_projected_obstruction(const Vector<
RWLockWrite write_lock(geometry_rwlock);
_projected_obstructions.push_back(projected_obstruction);
+ bounds_dirty = true;
}
void NavigationMeshSourceGeometryData3D::set_projected_obstructions(const Array &p_array) {
@@ -285,6 +294,7 @@ void NavigationMeshSourceGeometryData3D::set_projected_obstructions(const Array
RWLockWrite write_lock(geometry_rwlock);
_projected_obstructions.push_back(projected_obstruction);
+ bounds_dirty = true;
}
}
@@ -336,6 +346,7 @@ void NavigationMeshSourceGeometryData3D::set_data(const Vector<float> &p_vertice
vertices = p_vertices;
indices = p_indices;
_projected_obstructions = p_projected_obstructions;
+ bounds_dirty = true;
}
void NavigationMeshSourceGeometryData3D::get_data(Vector<float> &r_vertices, Vector<int> &r_indices, Vector<ProjectedObstruction> &r_projected_obstructions) {
@@ -345,6 +356,45 @@ void NavigationMeshSourceGeometryData3D::get_data(Vector<float> &r_vertices, Vec
r_projected_obstructions = _projected_obstructions;
}
+AABB NavigationMeshSourceGeometryData3D::get_bounds() {
+ geometry_rwlock.read_lock();
+
+ if (bounds_dirty) {
+ geometry_rwlock.read_unlock();
+ RWLockWrite write_lock(geometry_rwlock);
+
+ bounds_dirty = false;
+ bounds = AABB();
+ bool first_vertex = true;
+
+ for (int i = 0; i < vertices.size() / 3; i++) {
+ const Vector3 vertex = Vector3(vertices[i * 3], vertices[i * 3 + 1], vertices[i * 3 + 2]);
+ if (first_vertex) {
+ first_vertex = false;
+ bounds.position = vertex;
+ } else {
+ bounds.expand_to(vertex);
+ }
+ }
+ for (const ProjectedObstruction &projected_obstruction : _projected_obstructions) {
+ for (int i = 0; i < projected_obstruction.vertices.size() / 3; i++) {
+ const Vector3 vertex = Vector3(projected_obstruction.vertices[i * 3], projected_obstruction.vertices[i * 3 + 1], projected_obstruction.vertices[i * 3 + 2]);
+ if (first_vertex) {
+ first_vertex = false;
+ bounds.position = vertex;
+ } else {
+ bounds.expand_to(vertex);
+ }
+ }
+ }
+ } else {
+ geometry_rwlock.read_unlock();
+ }
+
+ RWLockRead read_lock(geometry_rwlock);
+ return bounds;
+}
+
void NavigationMeshSourceGeometryData3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_vertices", "vertices"), &NavigationMeshSourceGeometryData3D::set_vertices);
ClassDB::bind_method(D_METHOD("get_vertices"), &NavigationMeshSourceGeometryData3D::get_vertices);
@@ -367,6 +417,8 @@ void NavigationMeshSourceGeometryData3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_projected_obstructions", "projected_obstructions"), &NavigationMeshSourceGeometryData3D::set_projected_obstructions);
ClassDB::bind_method(D_METHOD("get_projected_obstructions"), &NavigationMeshSourceGeometryData3D::get_projected_obstructions);
+ ClassDB::bind_method(D_METHOD("get_bounds"), &NavigationMeshSourceGeometryData3D::get_bounds);
+
ADD_PROPERTY(PropertyInfo(Variant::PACKED_VECTOR3_ARRAY, "vertices", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "set_vertices", "get_vertices");
ADD_PROPERTY(PropertyInfo(Variant::PACKED_INT32_ARRAY, "indices", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "set_indices", "get_indices");
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "projected_obstructions", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "set_projected_obstructions", "get_projected_obstructions");
diff --git a/scene/resources/3d/navigation_mesh_source_geometry_data_3d.h b/scene/resources/3d/navigation_mesh_source_geometry_data_3d.h
index a8e613a51d..d7e3c3071c 100644
--- a/scene/resources/3d/navigation_mesh_source_geometry_data_3d.h
+++ b/scene/resources/3d/navigation_mesh_source_geometry_data_3d.h
@@ -41,6 +41,9 @@ class NavigationMeshSourceGeometryData3D : public Resource {
Vector<float> vertices;
Vector<int> indices;
+ AABB bounds;
+ bool bounds_dirty = true;
+
public:
struct ProjectedObstruction;
@@ -101,6 +104,8 @@ public:
void set_data(const Vector<float> &p_vertices, const Vector<int> &p_indices, Vector<ProjectedObstruction> &p_projected_obstructions);
void get_data(Vector<float> &r_vertices, Vector<int> &r_indices, Vector<ProjectedObstruction> &r_projected_obstructions);
+ AABB get_bounds();
+
NavigationMeshSourceGeometryData3D() {}
~NavigationMeshSourceGeometryData3D() { clear(); }
};
diff --git a/scene/resources/font.cpp b/scene/resources/font.cpp
index 104187775d..6b65ea4cfb 100644
--- a/scene/resources/font.cpp
+++ b/scene/resources/font.cpp
@@ -1482,8 +1482,8 @@ Error FontFile::_load_bitmap_font(const String &p_path, List<String> *r_image_fi
switch (block_type) {
case 1: /* info */ {
ERR_FAIL_COND_V_MSG(block_size < 15, ERR_CANT_CREATE, "Invalid BMFont info block size.");
- base_size = f->get_16();
- if (base_size <= 0) {
+ base_size = ABS(static_cast<int16_t>(f->get_16()));
+ if (base_size == 0) {
base_size = 16;
}
uint8_t flags = f->get_8();
@@ -1776,7 +1776,10 @@ Error FontFile::_load_bitmap_font(const String &p_path, List<String> *r_image_fi
if (type == "info") {
if (keys.has("size")) {
- base_size = keys["size"].to_int();
+ base_size = ABS(keys["size"].to_int());
+ if (base_size == 0) {
+ base_size = 16;
+ }
}
if (keys.has("outline")) {
outline = keys["outline"].to_int();
diff --git a/scene/resources/navigation_mesh.cpp b/scene/resources/navigation_mesh.cpp
index 2866ae7219..67ed65df0d 100644
--- a/scene/resources/navigation_mesh.cpp
+++ b/scene/resources/navigation_mesh.cpp
@@ -61,12 +61,12 @@ void NavigationMesh::create_from_mesh(const Ref<Mesh> &p_mesh) {
int rlen = iarr.size();
const int *r = iarr.ptr();
+ Vector<int> polygon;
for (int j = 0; j < rlen; j += 3) {
- Polygon polygon;
- polygon.indices.resize(3);
- polygon.indices.write[0] = r[j + 0] + from;
- polygon.indices.write[1] = r[j + 1] + from;
- polygon.indices.write[2] = r[j + 2] + from;
+ polygon.resize(3);
+ polygon.write[0] = r[j + 0] + from;
+ polygon.write[1] = r[j + 1] + from;
+ polygon.write[2] = r[j + 2] + from;
polygons.push_back(polygon);
}
}
@@ -318,7 +318,7 @@ void NavigationMesh::_set_polygons(const Array &p_array) {
RWLockWrite write_lock(rwlock);
polygons.resize(p_array.size());
for (int i = 0; i < p_array.size(); i++) {
- polygons.write[i].indices = p_array[i];
+ polygons.write[i] = p_array[i];
}
notify_property_list_changed();
}
@@ -328,17 +328,26 @@ Array NavigationMesh::_get_polygons() const {
Array ret;
ret.resize(polygons.size());
for (int i = 0; i < ret.size(); i++) {
- ret[i] = polygons[i].indices;
+ ret[i] = polygons[i];
}
return ret;
}
+void NavigationMesh::set_polygons(const Vector<Vector<int>> &p_polygons) {
+ RWLockWrite write_lock(rwlock);
+ polygons = p_polygons;
+ notify_property_list_changed();
+}
+
+Vector<Vector<int>> NavigationMesh::get_polygons() const {
+ RWLockRead read_lock(rwlock);
+ return polygons;
+}
+
void NavigationMesh::add_polygon(const Vector<int> &p_polygon) {
RWLockWrite write_lock(rwlock);
- Polygon polygon;
- polygon.indices = p_polygon;
- polygons.push_back(polygon);
+ polygons.push_back(p_polygon);
notify_property_list_changed();
}
@@ -350,7 +359,7 @@ int NavigationMesh::get_polygon_count() const {
Vector<int> NavigationMesh::get_polygon(int p_idx) {
RWLockRead read_lock(rwlock);
ERR_FAIL_INDEX_V(p_idx, polygons.size(), Vector<int>());
- return polygons[p_idx].indices;
+ return polygons[p_idx];
}
void NavigationMesh::clear_polygons() {
@@ -367,19 +376,13 @@ void NavigationMesh::clear() {
void NavigationMesh::set_data(const Vector<Vector3> &p_vertices, const Vector<Vector<int>> &p_polygons) {
RWLockWrite write_lock(rwlock);
vertices = p_vertices;
- polygons.resize(p_polygons.size());
- for (int i = 0; i < p_polygons.size(); i++) {
- polygons.write[i].indices = p_polygons[i];
- }
+ polygons = p_polygons;
}
void NavigationMesh::get_data(Vector<Vector3> &r_vertices, Vector<Vector<int>> &r_polygons) {
RWLockRead read_lock(rwlock);
r_vertices = vertices;
- r_polygons.resize(polygons.size());
- for (int i = 0; i < polygons.size(); i++) {
- r_polygons.write[i] = polygons[i].indices;
- }
+ r_polygons = polygons;
}
#ifdef DEBUG_ENABLED
diff --git a/scene/resources/navigation_mesh.h b/scene/resources/navigation_mesh.h
index 0ec2cc1bb1..741cea0791 100644
--- a/scene/resources/navigation_mesh.h
+++ b/scene/resources/navigation_mesh.h
@@ -39,10 +39,7 @@ class NavigationMesh : public Resource {
RWLock rwlock;
Vector<Vector3> vertices;
- struct Polygon {
- Vector<int> indices;
- };
- Vector<Polygon> polygons;
+ Vector<Vector<int>> polygons;
Ref<ArrayMesh> debug_mesh;
protected:
@@ -96,7 +93,7 @@ protected:
float detail_sample_max_error = 1.0f;
SamplePartitionType partition_type = SAMPLE_PARTITION_WATERSHED;
- ParsedGeometryType parsed_geometry_type = PARSED_GEOMETRY_MESH_INSTANCES;
+ ParsedGeometryType parsed_geometry_type = PARSED_GEOMETRY_BOTH;
uint32_t collision_mask = 0xFFFFFFFF;
SourceGeometryMode source_geometry_mode = SOURCE_GEOMETRY_ROOT_NODE_CHILDREN;
@@ -194,6 +191,8 @@ public:
int get_polygon_count() const;
Vector<int> get_polygon(int p_idx);
void clear_polygons();
+ void set_polygons(const Vector<Vector<int>> &p_polygons);
+ Vector<Vector<int>> get_polygons() const;
void clear();
diff --git a/scene/resources/visual_shader.cpp b/scene/resources/visual_shader.cpp
index fac3a2f663..1fa52b9c73 100644
--- a/scene/resources/visual_shader.cpp
+++ b/scene/resources/visual_shader.cpp
@@ -412,7 +412,6 @@ String VisualShaderNode::get_warning(Shader::Mode p_mode, VisualShader::Type p_t
}
VisualShaderNode::Category VisualShaderNode::get_category() const {
- WARN_PRINT(get_caption() + " is missing a category.");
return CATEGORY_NONE;
}
diff --git a/servers/audio/audio_stream.cpp b/servers/audio/audio_stream.cpp
index 7ab150c141..e2c8686911 100644
--- a/servers/audio/audio_stream.cpp
+++ b/servers/audio/audio_stream.cpp
@@ -342,12 +342,6 @@ bool AudioStreamMicrophone::is_monophonic() const {
return true;
}
-void AudioStreamMicrophone::_bind_methods() {
-}
-
-AudioStreamMicrophone::AudioStreamMicrophone() {
-}
-
int AudioStreamPlaybackMicrophone::_mix_internal(AudioFrame *p_buffer, int p_frames) {
AudioDriver::get_singleton()->lock();
diff --git a/servers/audio/audio_stream.h b/servers/audio/audio_stream.h
index 0ca4777d5c..d02dc6aae7 100644
--- a/servers/audio/audio_stream.h
+++ b/servers/audio/audio_stream.h
@@ -219,9 +219,6 @@ class AudioStreamMicrophone : public AudioStream {
HashSet<AudioStreamPlaybackMicrophone *> playbacks;
-protected:
- static void _bind_methods();
-
public:
virtual Ref<AudioStreamPlayback> instantiate_playback() override;
virtual String get_stream_name() const override;
@@ -229,8 +226,6 @@ public:
virtual double get_length() const override; //if supported, otherwise return 0
virtual bool is_monophonic() const override;
-
- AudioStreamMicrophone();
};
class AudioStreamPlaybackMicrophone : public AudioStreamPlaybackResampled {
diff --git a/servers/audio_server.cpp b/servers/audio_server.cpp
index 54840adcae..f0f894d03b 100644
--- a/servers/audio_server.cpp
+++ b/servers/audio_server.cpp
@@ -1379,6 +1379,12 @@ bool AudioServer::is_playback_active(Ref<AudioStreamPlayback> p_playback) {
float AudioServer::get_playback_position(Ref<AudioStreamPlayback> p_playback) {
ERR_FAIL_COND_V(p_playback.is_null(), 0);
+ // Samples.
+ if (p_playback->get_is_sample() && p_playback->get_sample_playback().is_valid()) {
+ Ref<AudioSamplePlayback> sample_playback = p_playback->get_sample_playback();
+ return AudioServer::get_singleton()->get_sample_playback_position(sample_playback);
+ }
+
AudioStreamPlaybackListNode *playback_node = _find_playback_list_node(p_playback);
if (!playback_node) {
return 0;
@@ -1847,6 +1853,11 @@ bool AudioServer::is_sample_playback_active(const Ref<AudioSamplePlayback> &p_pl
return AudioDriver::get_singleton()->is_sample_playback_active(p_playback);
}
+double AudioServer::get_sample_playback_position(const Ref<AudioSamplePlayback> &p_playback) {
+ ERR_FAIL_COND_V_MSG(p_playback.is_null(), false, "Parameter p_playback is null.");
+ return AudioDriver::get_singleton()->get_sample_playback_position(p_playback);
+}
+
void AudioServer::update_sample_playback_pitch_scale(const Ref<AudioSamplePlayback> &p_playback, float p_pitch_scale) {
ERR_FAIL_COND_MSG(p_playback.is_null(), "Parameter p_playback is null.");
return AudioDriver::get_singleton()->update_sample_playback_pitch_scale(p_playback, p_pitch_scale);
diff --git a/servers/audio_server.h b/servers/audio_server.h
index fd6cdb451e..2d6fc60860 100644
--- a/servers/audio_server.h
+++ b/servers/audio_server.h
@@ -141,6 +141,7 @@ public:
virtual void stop_sample_playback(const Ref<AudioSamplePlayback> &p_playback) {}
virtual void set_sample_playback_pause(const Ref<AudioSamplePlayback> &p_playback, bool p_paused) {}
virtual bool is_sample_playback_active(const Ref<AudioSamplePlayback> &p_playback) { return false; }
+ virtual double get_sample_playback_position(const Ref<AudioSamplePlayback> &p_playback) { return false; }
virtual void update_sample_playback_pitch_scale(const Ref<AudioSamplePlayback> &p_playback, float p_pitch_scale = 0.0f) {}
virtual void set_sample_playback_bus_volumes_linear(const Ref<AudioSamplePlayback> &p_playback, const HashMap<StringName, Vector<AudioFrame>> &p_bus_volumes) {}
@@ -484,6 +485,7 @@ public:
void stop_sample_playback(const Ref<AudioSamplePlayback> &p_playback);
void set_sample_playback_pause(const Ref<AudioSamplePlayback> &p_playback, bool p_paused);
bool is_sample_playback_active(const Ref<AudioSamplePlayback> &p_playback);
+ double get_sample_playback_position(const Ref<AudioSamplePlayback> &p_playback);
void update_sample_playback_pitch_scale(const Ref<AudioSamplePlayback> &p_playback, float p_pitch_scale = 0.0f);
AudioServer();
diff --git a/servers/movie_writer/movie_writer.cpp b/servers/movie_writer/movie_writer.cpp
index 9df05ba94a..aebed4b432 100644
--- a/servers/movie_writer/movie_writer.cpp
+++ b/servers/movie_writer/movie_writer.cpp
@@ -185,6 +185,10 @@ void MovieWriter::add_frame() {
RID main_vp_rid = RenderingServer::get_singleton()->viewport_find_from_screen_attachment(DisplayServer::MAIN_WINDOW_ID);
RID main_vp_texture = RenderingServer::get_singleton()->viewport_get_texture(main_vp_rid);
Ref<Image> vp_tex = RenderingServer::get_singleton()->texture_2d_get(main_vp_texture);
+ if (RenderingServer::get_singleton()->viewport_is_using_hdr_2d(main_vp_rid)) {
+ vp_tex->convert(Image::FORMAT_RGBA8);
+ vp_tex->linear_to_srgb();
+ }
RenderingServer::get_singleton()->viewport_set_measure_render_time(main_vp_rid, true);
cpu_time += RenderingServer::get_singleton()->viewport_get_measured_render_time_cpu(main_vp_rid);
diff --git a/servers/navigation_server_3d.cpp b/servers/navigation_server_3d.cpp
index b21c6b60f0..398ff1e1f3 100644
--- a/servers/navigation_server_3d.cpp
+++ b/servers/navigation_server_3d.cpp
@@ -216,6 +216,7 @@ void NavigationServer3D::_bind_methods() {
BIND_ENUM_CONSTANT(INFO_EDGE_MERGE_COUNT);
BIND_ENUM_CONSTANT(INFO_EDGE_CONNECTION_COUNT);
BIND_ENUM_CONSTANT(INFO_EDGE_FREE_COUNT);
+ BIND_ENUM_CONSTANT(INFO_OBSTACLE_COUNT);
}
NavigationServer3D *NavigationServer3D::get_singleton() {
diff --git a/servers/navigation_server_3d.h b/servers/navigation_server_3d.h
index 17c0771732..cdacf8e7e4 100644
--- a/servers/navigation_server_3d.h
+++ b/servers/navigation_server_3d.h
@@ -369,6 +369,7 @@ public:
INFO_EDGE_MERGE_COUNT,
INFO_EDGE_CONNECTION_COUNT,
INFO_EDGE_FREE_COUNT,
+ INFO_OBSTACLE_COUNT,
};
virtual int get_process_info(ProcessInfo p_info) const = 0;
diff --git a/servers/rendering/dummy/rasterizer_scene_dummy.h b/servers/rendering/dummy/rasterizer_scene_dummy.h
index 083493003f..a699a58b1f 100644
--- a/servers/rendering/dummy/rasterizer_scene_dummy.h
+++ b/servers/rendering/dummy/rasterizer_scene_dummy.h
@@ -186,6 +186,7 @@ public:
virtual void decals_set_filter(RS::DecalFilter p_filter) override {}
virtual void light_projectors_set_filter(RS::LightProjectorFilter p_filter) override {}
+ virtual void lightmaps_set_bicubic_filter(bool p_enable) override {}
RasterizerSceneDummy() {}
~RasterizerSceneDummy() {}
diff --git a/servers/rendering/dummy/storage/material_storage.cpp b/servers/rendering/dummy/storage/material_storage.cpp
index 64f6b55172..e8b553ca76 100644
--- a/servers/rendering/dummy/storage/material_storage.cpp
+++ b/servers/rendering/dummy/storage/material_storage.cpp
@@ -102,11 +102,7 @@ void MaterialStorage::get_shader_parameter_list(RID p_shader, List<PropertyInfo>
if (E.value.scope != ShaderLanguage::ShaderNode::Uniform::SCOPE_LOCAL) {
continue;
}
- if (E.value.texture_order >= 0) {
- filtered_uniforms.push_back(Pair<StringName, int>(E.key, E.value.texture_order + 100000));
- } else {
- filtered_uniforms.push_back(Pair<StringName, int>(E.key, E.value.order));
- }
+ filtered_uniforms.push_back(Pair<StringName, int>(E.key, E.value.prop_order));
}
int uniform_count = filtered_uniforms.size();
sorter.sort(filtered_uniforms.ptr(), uniform_count);
diff --git a/servers/rendering/renderer_canvas_cull.cpp b/servers/rendering/renderer_canvas_cull.cpp
index c4286dcc0c..0ec161d8cf 100644
--- a/servers/rendering/renderer_canvas_cull.cpp
+++ b/servers/rendering/renderer_canvas_cull.cpp
@@ -51,7 +51,7 @@ void RendererCanvasCull::_render_canvas_item_tree(RID p_to_render_target, Canvas
memset(z_last_list, 0, z_range * sizeof(RendererCanvasRender::Item *));
for (int i = 0; i < p_child_item_count; i++) {
- _cull_canvas_item(p_child_items[i].item, p_transform, p_clip_rect, Color(1, 1, 1, 1), 0, z_list, z_last_list, nullptr, nullptr, true, p_canvas_cull_mask, p_child_items[i].mirror, 1);
+ _cull_canvas_item(p_child_items[i].item, p_transform, p_clip_rect, Color(1, 1, 1, 1), 0, z_list, z_last_list, nullptr, nullptr, true, p_canvas_cull_mask, Point2(), 1, nullptr);
}
RendererCanvasRender::Item *list = nullptr;
@@ -97,6 +97,7 @@ void _collect_ysort_children(RendererCanvasCull::Item *p_canvas_item, const Tran
if (!child_items[i]->repeat_source) {
child_items[i]->repeat_size = p_canvas_item->repeat_size;
child_items[i]->repeat_times = p_canvas_item->repeat_times;
+ child_items[i]->repeat_source_item = p_canvas_item->repeat_source_item;
}
// Y sorted canvas items are flattened into r_items. Calculate their absolute z index to use when rendering r_items.
@@ -229,10 +230,13 @@ void RendererCanvasCull::_attach_canvas_item_for_draw(RendererCanvasCull::Item *
ci->visibility_notifier->visible_in_frame = RSG::rasterizer->get_frame_number();
}
+ } else if (ci->repeat_source) {
+ // If repeat source does not draw itself it still needs transform updated as its child items' repeat offsets are relative to it.
+ ci->final_transform = p_transform;
}
}
-void RendererCanvasCull::_cull_canvas_item(Item *p_canvas_item, const Transform2D &p_parent_xform, const Rect2 &p_clip_rect, const Color &p_modulate, int p_z, RendererCanvasRender::Item **r_z_list, RendererCanvasRender::Item **r_z_last_list, Item *p_canvas_clip, Item *p_material_owner, bool p_allow_y_sort, uint32_t p_canvas_cull_mask, const Point2 &p_repeat_size, int p_repeat_times) {
+void RendererCanvasCull::_cull_canvas_item(Item *p_canvas_item, const Transform2D &p_parent_xform, const Rect2 &p_clip_rect, const Color &p_modulate, int p_z, RendererCanvasRender::Item **r_z_list, RendererCanvasRender::Item **r_z_last_list, Item *p_canvas_clip, Item *p_material_owner, bool p_allow_y_sort, uint32_t p_canvas_cull_mask, const Point2 &p_repeat_size, int p_repeat_times, RendererCanvasRender::Item *p_repeat_source_item) {
Item *ci = p_canvas_item;
if (!ci->visible) {
@@ -268,19 +272,16 @@ void RendererCanvasCull::_cull_canvas_item(Item *p_canvas_item, const Transform2
Point2 repeat_size = p_repeat_size;
int repeat_times = p_repeat_times;
+ RendererCanvasRender::Item *repeat_source_item = p_repeat_source_item;
if (ci->repeat_source) {
repeat_size = ci->repeat_size;
repeat_times = ci->repeat_times;
+ repeat_source_item = ci;
} else {
ci->repeat_size = repeat_size;
ci->repeat_times = repeat_times;
- }
-
- if (repeat_size.x || repeat_size.y) {
- Size2 scale = final_xform.get_scale();
- rect.size += repeat_size * repeat_times / scale;
- rect.position -= repeat_size / scale * (repeat_times / 2);
+ ci->repeat_source_item = repeat_source_item;
}
if (snapping_2d_transforms_to_pixel) {
@@ -291,6 +292,25 @@ void RendererCanvasCull::_cull_canvas_item(Item *p_canvas_item, const Transform2
final_xform = parent_xform * final_xform;
Rect2 global_rect = final_xform.xform(rect);
+ if (repeat_source_item && (repeat_size.x || repeat_size.y)) {
+ // Top-left repeated rect.
+ Rect2 corner_rect = global_rect;
+ corner_rect.position -= repeat_source_item->final_transform.basis_xform((repeat_times / 2) * repeat_size);
+ global_rect = corner_rect;
+
+ // Plus top-right repeated rect.
+ Size2 size_x_offset = repeat_source_item->final_transform.basis_xform(repeat_times * Size2(repeat_size.x, 0));
+ corner_rect.position += size_x_offset;
+ global_rect = global_rect.merge(corner_rect);
+
+ // Plus bottom-right repeated rect.
+ corner_rect.position += repeat_source_item->final_transform.basis_xform(repeat_times * Size2(0, repeat_size.y));
+ global_rect = global_rect.merge(corner_rect);
+
+ // Plus bottom-left repeated rect.
+ corner_rect.position -= size_x_offset;
+ global_rect = global_rect.merge(corner_rect);
+ }
global_rect.position += p_clip_rect.position;
if (ci->use_parent_material && p_material_owner) {
@@ -357,7 +377,7 @@ void RendererCanvasCull::_cull_canvas_item(Item *p_canvas_item, const Transform2
sorter.sort(child_items, child_item_count);
for (i = 0; i < child_item_count; i++) {
- _cull_canvas_item(child_items[i], final_xform * child_items[i]->ysort_xform, p_clip_rect, modulate * child_items[i]->ysort_modulate, child_items[i]->ysort_parent_abs_z_index, r_z_list, r_z_last_list, (Item *)ci->final_clip_owner, (Item *)child_items[i]->material_owner, false, p_canvas_cull_mask, child_items[i]->repeat_size, child_items[i]->repeat_times);
+ _cull_canvas_item(child_items[i], final_xform * child_items[i]->ysort_xform, p_clip_rect, modulate * child_items[i]->ysort_modulate, child_items[i]->ysort_parent_abs_z_index, r_z_list, r_z_last_list, (Item *)ci->final_clip_owner, (Item *)child_items[i]->material_owner, false, p_canvas_cull_mask, child_items[i]->repeat_size, child_items[i]->repeat_times, child_items[i]->repeat_source_item);
}
} else {
RendererCanvasRender::Item *canvas_group_from = nullptr;
@@ -381,14 +401,14 @@ void RendererCanvasCull::_cull_canvas_item(Item *p_canvas_item, const Transform2
if (!child_items[i]->behind && !use_canvas_group) {
continue;
}
- _cull_canvas_item(child_items[i], final_xform, p_clip_rect, modulate, p_z, r_z_list, r_z_last_list, (Item *)ci->final_clip_owner, p_material_owner, true, p_canvas_cull_mask, repeat_size, repeat_times);
+ _cull_canvas_item(child_items[i], final_xform, p_clip_rect, modulate, p_z, r_z_list, r_z_last_list, (Item *)ci->final_clip_owner, p_material_owner, true, p_canvas_cull_mask, repeat_size, repeat_times, repeat_source_item);
}
_attach_canvas_item_for_draw(ci, p_canvas_clip, r_z_list, r_z_last_list, final_xform, p_clip_rect, global_rect, modulate, p_z, p_material_owner, use_canvas_group, canvas_group_from);
for (int i = 0; i < child_item_count; i++) {
if (child_items[i]->behind || use_canvas_group) {
continue;
}
- _cull_canvas_item(child_items[i], final_xform, p_clip_rect, modulate, p_z, r_z_list, r_z_last_list, (Item *)ci->final_clip_owner, p_material_owner, true, p_canvas_cull_mask, repeat_size, repeat_times);
+ _cull_canvas_item(child_items[i], final_xform, p_clip_rect, modulate, p_z, r_z_list, r_z_last_list, (Item *)ci->final_clip_owner, p_material_owner, true, p_canvas_cull_mask, repeat_size, repeat_times, repeat_source_item);
}
}
}
@@ -431,14 +451,22 @@ void RendererCanvasCull::canvas_set_item_mirroring(RID p_canvas, RID p_item, con
int idx = canvas->find_item(canvas_item);
ERR_FAIL_COND(idx == -1);
- canvas->child_items.write[idx].mirror = p_mirroring;
+
+ bool is_repeat_source = (p_mirroring.x || p_mirroring.y);
+ canvas_item->repeat_source = is_repeat_source;
+ canvas_item->repeat_source_item = is_repeat_source ? canvas_item : nullptr;
+ canvas_item->repeat_size = p_mirroring;
+ canvas_item->repeat_times = 1;
}
void RendererCanvasCull::canvas_set_item_repeat(RID p_item, const Point2 &p_repeat_size, int p_repeat_times) {
+ ERR_FAIL_COND(p_repeat_times < 0);
Item *canvas_item = canvas_item_owner.get_or_null(p_item);
ERR_FAIL_NULL(canvas_item);
- canvas_item->repeat_source = true;
+ bool is_repeat_source = (p_repeat_size.x || p_repeat_size.y) && p_repeat_times;
+ canvas_item->repeat_source = is_repeat_source;
+ canvas_item->repeat_source_item = is_repeat_source ? canvas_item : nullptr;
canvas_item->repeat_size = p_repeat_size;
canvas_item->repeat_times = p_repeat_times;
}
diff --git a/servers/rendering/renderer_canvas_cull.h b/servers/rendering/renderer_canvas_cull.h
index 9f8cbea2e9..91c03054f7 100644
--- a/servers/rendering/renderer_canvas_cull.h
+++ b/servers/rendering/renderer_canvas_cull.h
@@ -126,7 +126,6 @@ public:
struct Canvas : public RendererViewport::CanvasBase {
HashSet<RID> viewports;
struct ChildItem {
- Point2 mirror;
Item *item = nullptr;
bool operator<(const ChildItem &p_item) const {
return item->index < p_item.item->index;
@@ -188,7 +187,7 @@ public:
private:
void _render_canvas_item_tree(RID p_to_render_target, Canvas::ChildItem *p_child_items, int p_child_item_count, const Transform2D &p_transform, const Rect2 &p_clip_rect, const Color &p_modulate, RendererCanvasRender::Light *p_lights, RendererCanvasRender::Light *p_directional_lights, RS::CanvasItemTextureFilter p_default_filter, RS::CanvasItemTextureRepeat p_default_repeat, bool p_snap_2d_vertices_to_pixel, uint32_t p_canvas_cull_mask, RenderingMethod::RenderInfo *r_render_info = nullptr);
- void _cull_canvas_item(Item *p_canvas_item, const Transform2D &p_parent_xform, const Rect2 &p_clip_rect, const Color &p_modulate, int p_z, RendererCanvasRender::Item **r_z_list, RendererCanvasRender::Item **r_z_last_list, Item *p_canvas_clip, Item *p_material_owner, bool p_allow_y_sort, uint32_t p_canvas_cull_mask, const Point2 &p_repeat_size, int p_repeat_times);
+ void _cull_canvas_item(Item *p_canvas_item, const Transform2D &p_parent_xform, const Rect2 &p_clip_rect, const Color &p_modulate, int p_z, RendererCanvasRender::Item **r_z_list, RendererCanvasRender::Item **r_z_last_list, Item *p_canvas_clip, Item *p_material_owner, bool p_allow_y_sort, uint32_t p_canvas_cull_mask, const Point2 &p_repeat_size, int p_repeat_times, RendererCanvasRender::Item *p_repeat_source_item);
static constexpr int z_range = RS::CANVAS_ITEM_Z_MAX - RS::CANVAS_ITEM_Z_MIN + 1;
diff --git a/servers/rendering/renderer_canvas_render.h b/servers/rendering/renderer_canvas_render.h
index cb8180f989..c57abee165 100644
--- a/servers/rendering/renderer_canvas_render.h
+++ b/servers/rendering/renderer_canvas_render.h
@@ -364,6 +364,7 @@ public:
bool repeat_source;
Point2 repeat_size;
int repeat_times = 1;
+ Item *repeat_source_item = nullptr;
Rect2 global_rect_cache;
diff --git a/servers/rendering/renderer_rd/effects/fsr2.cpp b/servers/rendering/renderer_rd/effects/fsr2.cpp
index 925352a7d1..551ea5dd97 100644
--- a/servers/rendering/renderer_rd/effects/fsr2.cpp
+++ b/servers/rendering/renderer_rd/effects/fsr2.cpp
@@ -800,9 +800,6 @@ FSR2Effect::~FSR2Effect() {
RD::get_singleton()->free(device.linear_clamp_sampler);
for (uint32_t i = 0; i < FFX_FSR2_PASS_COUNT; i++) {
- if (device.passes[i].pipeline.pipeline_rid.is_valid()) {
- RD::get_singleton()->free(device.passes[i].pipeline.pipeline_rid);
- }
device.passes[i].shader->version_free(device.passes[i].shader_version);
}
}
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 ad19f36257..b97e38da4d 100644
--- a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp
+++ b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp
@@ -1103,9 +1103,17 @@ void RenderForwardClustered::_setup_lightmaps(const RenderDataRD *p_render_data,
RID lightmap = light_storage->lightmap_instance_get_lightmap(p_lightmaps[i]);
+ // Transform (for directional lightmaps).
Basis to_lm = light_storage->lightmap_instance_get_transform(p_lightmaps[i]).basis.inverse() * p_cam_transform.basis;
to_lm = to_lm.inverse().transposed(); //will transform normals
RendererRD::MaterialStorage::store_transform_3x3(to_lm, scene_state.lightmaps[i].normal_xform);
+
+ // Light texture size.
+ Vector2i lightmap_size = light_storage->lightmap_get_light_texture_size(lightmap);
+ scene_state.lightmaps[i].texture_size[0] = lightmap_size[0];
+ scene_state.lightmaps[i].texture_size[1] = lightmap_size[1];
+
+ // Exposure.
scene_state.lightmaps[i].exposure_normalization = 1.0;
if (p_render_data->camera_attributes.is_valid()) {
float baked_exposure = light_storage->lightmap_get_baked_exposure_normalization(lightmap);
@@ -1749,7 +1757,7 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co
int *render_info = p_render_data->render_info ? p_render_data->render_info->info[RS::VIEWPORT_RENDER_INFO_TYPE_VISIBLE] : (int *)nullptr;
_fill_instance_data(RENDER_LIST_OPAQUE, render_info);
_fill_instance_data(RENDER_LIST_MOTION, render_info);
- _fill_instance_data(RENDER_LIST_ALPHA);
+ _fill_instance_data(RENDER_LIST_ALPHA, render_info);
RD::get_singleton()->draw_command_end_label();
@@ -1827,7 +1835,7 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co
clear_color.r *= bg_energy_multiplier;
clear_color.g *= bg_energy_multiplier;
clear_color.b *= bg_energy_multiplier;
- if (!p_render_data->transparent_bg && rb->has_custom_data(RB_SCOPE_FOG) && environment_get_fog_enabled(p_render_data->environment)) {
+ if (!p_render_data->transparent_bg && (rb->has_custom_data(RB_SCOPE_FOG) || environment_get_fog_enabled(p_render_data->environment))) {
draw_sky_fog_only = true;
RendererRD::MaterialStorage::get_singleton()->material_set_param(sky.sky_scene_state.fog_material, "clear_color", Variant(clear_color.srgb_to_linear()));
}
@@ -1837,7 +1845,7 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co
clear_color.r *= bg_energy_multiplier;
clear_color.g *= bg_energy_multiplier;
clear_color.b *= bg_energy_multiplier;
- if (!p_render_data->transparent_bg && rb->has_custom_data(RB_SCOPE_FOG) && environment_get_fog_enabled(p_render_data->environment)) {
+ if (!p_render_data->transparent_bg && (rb->has_custom_data(RB_SCOPE_FOG) || environment_get_fog_enabled(p_render_data->environment))) {
draw_sky_fog_only = true;
RendererRD::MaterialStorage::get_singleton()->material_set_param(sky.sky_scene_state.fog_material, "clear_color", Variant(clear_color.srgb_to_linear()));
}
@@ -4242,6 +4250,11 @@ void RenderForwardClustered::_update_shader_quality_settings() {
spec_constants.push_back(sc);
+ sc.constant_id = SPEC_CONSTANT_USE_LIGHTMAP_BICUBIC_FILTER;
+ sc.bool_value = lightmap_filter_bicubic_get();
+
+ spec_constants.push_back(sc);
+
scene_shader.set_default_specialization_constants(spec_constants);
base_uniforms_changed(); //also need this
diff --git a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.h b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.h
index 0aa4a0667e..5d14653db6 100644
--- a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.h
+++ b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.h
@@ -73,6 +73,7 @@ class RenderForwardClustered : public RendererSceneRenderRD {
SPEC_CONSTANT_DECAL_FILTER = 10,
SPEC_CONSTANT_PROJECTOR_FILTER = 11,
SPEC_CONSTANT_USE_DEPTH_FOG = 12,
+ SPEC_CONSTANT_USE_LIGHTMAP_BICUBIC_FILTER = 13,
};
enum {
@@ -235,8 +236,9 @@ class RenderForwardClustered : public RendererSceneRenderRD {
struct LightmapData {
float normal_xform[12];
- float pad[3];
+ float texture_size[2];
float exposure_normalization;
+ float pad;
};
struct LightmapCaptureData {
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 c03dd96062..412fa643b1 100644
--- a/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp
+++ b/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp
@@ -563,9 +563,17 @@ void RenderForwardMobile::_setup_lightmaps(const RenderDataRD *p_render_data, co
RID lightmap = light_storage->lightmap_instance_get_lightmap(p_lightmaps[i]);
+ // Transform (for directional lightmaps).
Basis to_lm = light_storage->lightmap_instance_get_transform(p_lightmaps[i]).basis.inverse() * p_cam_transform.basis;
to_lm = to_lm.inverse().transposed(); //will transform normals
RendererRD::MaterialStorage::store_transform_3x3(to_lm, scene_state.lightmaps[i].normal_xform);
+
+ // Light texture size.
+ Vector2i lightmap_size = light_storage->lightmap_get_light_texture_size(lightmap);
+ scene_state.lightmaps[i].texture_size[0] = lightmap_size[0];
+ scene_state.lightmaps[i].texture_size[1] = lightmap_size[1];
+
+ // Exposure.
scene_state.lightmaps[i].exposure_normalization = 1.0;
if (p_render_data->camera_attributes.is_valid()) {
float baked_exposure = light_storage->lightmap_get_baked_exposure_normalization(lightmap);
@@ -2781,6 +2789,11 @@ void RenderForwardMobile::_update_shader_quality_settings() {
spec_constants.push_back(sc);
+ sc.constant_id = SPEC_CONSTANT_USE_LIGHTMAP_BICUBIC_FILTER;
+ sc.bool_value = lightmap_filter_bicubic_get();
+
+ spec_constants.push_back(sc);
+
scene_shader.set_default_specialization_constants(spec_constants);
base_uniforms_changed(); //also need this
diff --git a/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.h b/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.h
index 34260bd701..fc60c770e8 100644
--- a/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.h
+++ b/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.h
@@ -82,7 +82,7 @@ private:
SPEC_CONSTANT_DISABLE_FOG = 14,
SPEC_CONSTANT_USE_DEPTH_FOG = 16,
SPEC_CONSTANT_IS_MULTIMESH = 17,
-
+ SPEC_CONSTANT_USE_LIGHTMAP_BICUBIC_FILTER = 18,
};
enum {
@@ -208,8 +208,9 @@ private:
struct LightmapData {
float normal_xform[12];
- float pad[3];
+ float texture_size[2];
float exposure_normalization;
+ float pad;
};
struct LightmapCaptureData {
diff --git a/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp b/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp
index 5c68fb82b1..5e4721dfb5 100644
--- a/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp
+++ b/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp
@@ -407,7 +407,7 @@ _FORCE_INLINE_ static uint32_t _indices_to_primitives(RS::PrimitiveType p_primit
return (p_indices - subtractor[p_primitive]) / divisor[p_primitive];
}
-void RendererCanvasRenderRD::_render_item(RD::DrawListID p_draw_list, RID p_render_target, const Item *p_item, RD::FramebufferFormatID p_framebuffer_format, const Transform2D &p_canvas_transform_inverse, Item *&current_clip, Light *p_lights, PipelineVariants *p_pipeline_variants, bool &r_sdf_used, const Point2 &p_offset, RenderingMethod::RenderInfo *r_render_info) {
+void RendererCanvasRenderRD::_render_item(RD::DrawListID p_draw_list, RID p_render_target, const Item *p_item, RD::FramebufferFormatID p_framebuffer_format, const Transform2D &p_canvas_transform_inverse, Item *&current_clip, Light *p_lights, PipelineVariants *p_pipeline_variants, bool &r_sdf_used, const Point2 &p_repeat_offset, RenderingMethod::RenderInfo *r_render_info) {
//create an empty push constant
RendererRD::TextureStorage *texture_storage = RendererRD::TextureStorage::get_singleton();
RendererRD::MeshStorage *mesh_storage = RendererRD::MeshStorage::get_singleton();
@@ -425,11 +425,11 @@ void RendererCanvasRenderRD::_render_item(RD::DrawListID p_draw_list, RID p_rend
}
PushConstant push_constant;
- Transform2D base_transform = p_canvas_transform_inverse * p_item->final_transform;
-
- if (p_offset.x || p_offset.y) {
- base_transform *= Transform2D(0, p_offset / p_item->xform_curr.get_scale()); // TODO: Interpolate or explain why not needed.
+ Transform2D base_transform = p_item->final_transform;
+ if (p_item->repeat_source_item && (p_repeat_offset.x || p_repeat_offset.y)) {
+ base_transform.columns[2] += p_item->repeat_source_item->final_transform.basis_xform(p_repeat_offset);
}
+ base_transform = p_canvas_transform_inverse * base_transform;
Transform2D draw_transform;
_update_transform_2d_to_mat2x3(base_transform, push_constant.world);
@@ -1250,18 +1250,17 @@ void RendererCanvasRenderRD::_render_items(RID p_to_render_target, int p_item_co
_render_item(draw_list, p_to_render_target, ci, fb_format, canvas_transform_inverse, current_clip, p_lights, pipeline_variants, r_sdf_used, Point2(), r_render_info);
} else {
Point2 start_pos = ci->repeat_size * -(ci->repeat_times / 2);
- Point2 end_pos = ci->repeat_size * ci->repeat_times + ci->repeat_size + start_pos;
- Point2 pos = start_pos;
-
- do {
- do {
- _render_item(draw_list, p_to_render_target, ci, fb_format, canvas_transform_inverse, current_clip, p_lights, pipeline_variants, r_sdf_used, pos, r_render_info);
- pos.y += ci->repeat_size.y;
- } while (pos.y < end_pos.y);
-
- pos.x += ci->repeat_size.x;
- pos.y = start_pos.y;
- } while (pos.x < end_pos.x);
+ Point2 offset;
+
+ int repeat_times_x = ci->repeat_size.x ? ci->repeat_times : 0;
+ int repeat_times_y = ci->repeat_size.y ? ci->repeat_times : 0;
+ for (int ry = 0; ry <= repeat_times_y; ry++) {
+ offset.y = start_pos.y + ry * ci->repeat_size.y;
+ for (int rx = 0; rx <= repeat_times_x; rx++) {
+ offset.x = start_pos.x + rx * ci->repeat_size.x;
+ _render_item(draw_list, p_to_render_target, ci, fb_format, canvas_transform_inverse, current_clip, p_lights, pipeline_variants, r_sdf_used, offset, r_render_info);
+ }
+ }
}
prev_material = material;
diff --git a/servers/rendering/renderer_rd/renderer_canvas_render_rd.h b/servers/rendering/renderer_rd/renderer_canvas_render_rd.h
index a78ced271a..9deb4814c7 100644
--- a/servers/rendering/renderer_rd/renderer_canvas_render_rd.h
+++ b/servers/rendering/renderer_rd/renderer_canvas_render_rd.h
@@ -423,7 +423,7 @@ class RendererCanvasRenderRD : public RendererCanvasRender {
double debug_redraw_time = 1.0;
inline void _bind_canvas_texture(RD::DrawListID p_draw_list, RID p_texture, RS::CanvasItemTextureFilter p_base_filter, RS::CanvasItemTextureRepeat p_base_repeat, RID &r_last_texture, PushConstant &push_constant, Size2 &r_texpixel_size, bool p_texture_is_data = false); //recursive, so regular inline used instead.
- void _render_item(RenderingDevice::DrawListID p_draw_list, RID p_render_target, const Item *p_item, RenderingDevice::FramebufferFormatID p_framebuffer_format, const Transform2D &p_canvas_transform_inverse, Item *&current_clip, Light *p_lights, PipelineVariants *p_pipeline_variants, bool &r_sdf_used, const Point2 &p_offset, RenderingMethod::RenderInfo *r_render_info = nullptr);
+ void _render_item(RenderingDevice::DrawListID p_draw_list, RID p_render_target, const Item *p_item, RenderingDevice::FramebufferFormatID p_framebuffer_format, const Transform2D &p_canvas_transform_inverse, Item *&current_clip, Light *p_lights, PipelineVariants *p_pipeline_variants, bool &r_sdf_used, const Point2 &p_repeat_offset, RenderingMethod::RenderInfo *r_render_info = nullptr);
void _render_items(RID p_to_render_target, int p_item_count, const Transform2D &p_canvas_transform_inverse, Light *p_lights, bool &r_sdf_used, bool p_to_backbuffer = false, RenderingMethod::RenderInfo *r_render_info = nullptr);
_FORCE_INLINE_ void _update_transform_2d_to_mat2x4(const Transform2D &p_transform, float *p_mat2x4);
diff --git a/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp b/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp
index 0ebed49ee9..7d6d5018d0 100644
--- a/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp
+++ b/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp
@@ -1040,6 +1040,14 @@ void RendererSceneRenderRD::light_projectors_set_filter(RenderingServer::LightPr
_update_shader_quality_settings();
}
+void RendererSceneRenderRD::lightmaps_set_bicubic_filter(bool p_enable) {
+ if (lightmap_filter_bicubic == p_enable) {
+ return;
+ }
+ lightmap_filter_bicubic = p_enable;
+ _update_shader_quality_settings();
+}
+
int RendererSceneRenderRD::get_roughness_layers() const {
return sky.roughness_layers;
}
@@ -1483,6 +1491,7 @@ void RendererSceneRenderRD::init() {
decals_set_filter(RS::DecalFilter(int(GLOBAL_GET("rendering/textures/decals/filter"))));
light_projectors_set_filter(RS::LightProjectorFilter(int(GLOBAL_GET("rendering/textures/light_projectors/filter"))));
+ lightmaps_set_bicubic_filter(GLOBAL_GET("rendering/lightmapping/lightmap_gi/use_bicubic_filter"));
cull_argument.set_page_pool(&cull_argument_pool);
diff --git a/servers/rendering/renderer_rd/renderer_scene_render_rd.h b/servers/rendering/renderer_rd/renderer_scene_render_rd.h
index a8e8e638cd..022a4560f8 100644
--- a/servers/rendering/renderer_rd/renderer_scene_render_rd.h
+++ b/servers/rendering/renderer_rd/renderer_scene_render_rd.h
@@ -133,6 +133,7 @@ private:
float *directional_soft_shadow_kernel = nullptr;
float *penumbra_shadow_kernel = nullptr;
float *soft_shadow_kernel = nullptr;
+ bool lightmap_filter_bicubic = false;
int directional_penumbra_shadow_samples = 0;
int directional_soft_shadow_samples = 0;
int penumbra_shadow_samples = 0;
@@ -262,6 +263,7 @@ public:
virtual void decals_set_filter(RS::DecalFilter p_filter) override;
virtual void light_projectors_set_filter(RS::LightProjectorFilter p_filter) override;
+ virtual void lightmaps_set_bicubic_filter(bool p_enable) override;
_FORCE_INLINE_ RS::ShadowQuality shadows_quality_get() const {
return shadows_quality;
@@ -292,6 +294,9 @@ public:
_FORCE_INLINE_ int directional_penumbra_shadow_samples_get() const {
return directional_penumbra_shadow_samples;
}
+ _FORCE_INLINE_ bool lightmap_filter_bicubic_get() const {
+ return lightmap_filter_bicubic;
+ }
_FORCE_INLINE_ int directional_soft_shadow_samples_get() const {
return directional_soft_shadow_samples;
}
diff --git a/servers/rendering/renderer_rd/shaders/environment/sky.glsl b/servers/rendering/renderer_rd/shaders/environment/sky.glsl
index 35457a2482..5aa3735494 100644
--- a/servers/rendering/renderer_rd/shaders/environment/sky.glsl
+++ b/servers/rendering/renderer_rd/shaders/environment/sky.glsl
@@ -255,10 +255,6 @@ void main() {
frag_color.rgb = color;
frag_color.a = alpha;
- // For mobile renderer we're multiplying by 0.5 as we're using a UNORM buffer.
- // For both mobile and clustered, we also bake in the exposure value for the environment and camera.
- frag_color.rgb = frag_color.rgb * params.luminance_multiplier;
-
#if !defined(DISABLE_FOG) && !defined(USE_CUBEMAP_PASS)
// Draw "fixed" fog before volumetric fog to ensure volumetric fog can appear in front of the sky.
@@ -278,6 +274,10 @@ void main() {
#endif // DISABLE_FOG
+ // For mobile renderer we're multiplying by 0.5 as we're using a UNORM buffer.
+ // For both mobile and clustered, we also bake in the exposure value for the environment and camera.
+ frag_color.rgb = frag_color.rgb * params.luminance_multiplier;
+
// Blending is disabled for Sky, so alpha doesn't blend.
// Alpha is used for subsurface scattering so make sure it doesn't get applied to Sky.
if (!AT_CUBEMAP_PASS && !AT_HALF_RES_PASS && !AT_QUARTER_RES_PASS) {
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 4a630b0b0a..5706f670eb 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
@@ -657,6 +657,7 @@ layout(constant_id = 9) const uint sc_directional_penumbra_shadow_samples = 4;
layout(constant_id = 10) const bool sc_decal_use_mipmaps = true;
layout(constant_id = 11) const bool sc_projector_use_mipmaps = true;
layout(constant_id = 12) const bool sc_use_depth_fog = false;
+layout(constant_id = 13) const bool sc_use_lightmap_bicubic_filter = false;
// not used in clustered renderer but we share some code with the mobile renderer that requires this.
const float sc_luminance_multiplier = 1.0;
@@ -701,6 +702,67 @@ layout(location = 9) in float dp_clip;
layout(location = 10) in flat uint instance_index_interp;
+#ifdef USE_LIGHTMAP
+// w0, w1, w2, and w3 are the four cubic B-spline basis functions
+float w0(float a) {
+ return (1.0 / 6.0) * (a * (a * (-a + 3.0) - 3.0) + 1.0);
+}
+
+float w1(float a) {
+ return (1.0 / 6.0) * (a * a * (3.0 * a - 6.0) + 4.0);
+}
+
+float w2(float a) {
+ return (1.0 / 6.0) * (a * (a * (-3.0 * a + 3.0) + 3.0) + 1.0);
+}
+
+float w3(float a) {
+ return (1.0 / 6.0) * (a * a * a);
+}
+
+// g0 and g1 are the two amplitude functions
+float g0(float a) {
+ return w0(a) + w1(a);
+}
+
+float g1(float a) {
+ return w2(a) + w3(a);
+}
+
+// h0 and h1 are the two offset functions
+float h0(float a) {
+ return -1.0 + w1(a) / (w0(a) + w1(a));
+}
+
+float h1(float a) {
+ return 1.0 + w3(a) / (w2(a) + w3(a));
+}
+
+vec4 textureArray_bicubic(texture2DArray tex, vec3 uv, vec2 texture_size) {
+ vec2 texel_size = vec2(1.0) / texture_size;
+
+ uv.xy = uv.xy * texture_size + vec2(0.5);
+
+ vec2 iuv = floor(uv.xy);
+ vec2 fuv = fract(uv.xy);
+
+ float g0x = g0(fuv.x);
+ float g1x = g1(fuv.x);
+ float h0x = h0(fuv.x);
+ float h1x = h1(fuv.x);
+ float h0y = h0(fuv.y);
+ float h1y = h1(fuv.y);
+
+ vec2 p0 = (vec2(iuv.x + h0x, iuv.y + h0y) - vec2(0.5)) * texel_size;
+ vec2 p1 = (vec2(iuv.x + h1x, iuv.y + h0y) - vec2(0.5)) * texel_size;
+ vec2 p2 = (vec2(iuv.x + h0x, iuv.y + h1y) - vec2(0.5)) * texel_size;
+ vec2 p3 = (vec2(iuv.x + h1x, iuv.y + h1y) - vec2(0.5)) * texel_size;
+
+ return (g0(fuv.y) * (g0x * texture(sampler2DArray(tex, SAMPLER_LINEAR_CLAMP), vec3(p0, uv.z)) + g1x * texture(sampler2DArray(tex, SAMPLER_LINEAR_CLAMP), vec3(p1, uv.z)))) +
+ (g1(fuv.y) * (g0x * texture(sampler2DArray(tex, SAMPLER_LINEAR_CLAMP), vec3(p2, uv.z)) + g1x * texture(sampler2DArray(tex, SAMPLER_LINEAR_CLAMP), vec3(p3, uv.z))));
+}
+#endif //USE_LIGHTMAP
+
#ifdef USE_MULTIVIEW
#ifdef has_VK_KHR_multiview
#define ViewIndex gl_ViewIndex
@@ -1030,6 +1092,13 @@ void fragment_shader(in SceneData scene_data) {
vec3 light_vertex = vertex;
#endif //LIGHT_VERTEX_USED
+ mat3 model_normal_matrix;
+ if (bool(instances.data[instance_index].flags & INSTANCE_FLAGS_NON_UNIFORM_SCALE)) {
+ model_normal_matrix = transpose(inverse(mat3(read_model_matrix)));
+ } else {
+ model_normal_matrix = mat3(read_model_matrix);
+ }
+
mat4 read_view_matrix = scene_data.view_matrix;
vec2 read_viewport_size = scene_data.viewport_size;
{
@@ -1310,6 +1379,8 @@ void fragment_shader(in SceneData scene_data) {
#if !defined(MODE_RENDER_DEPTH) && !defined(MODE_UNSHADED)
+#ifndef AMBIENT_LIGHT_DISABLED
+
if (scene_data.use_reflection_cubemap) {
#ifdef LIGHT_ANISOTROPY_USED
// https://google.github.io/filament/Filament.html#lighting/imagebasedlights/anisotropy
@@ -1397,14 +1468,15 @@ void fragment_shader(in SceneData scene_data) {
#endif //USE_RADIANCE_CUBEMAP_ARRAY
specular_light += clearcoat_light * horizon * horizon * Fc * scene_data.ambient_light_color_energy.a;
}
-#endif
+#endif // LIGHT_CLEARCOAT_USED
+#endif // !AMBIENT_LIGHT_DISABLED
#endif //!defined(MODE_RENDER_DEPTH) && !defined(MODE_UNSHADED)
//radiance
/// GI ///
#if !defined(MODE_RENDER_DEPTH) && !defined(MODE_UNSHADED)
-
+#ifndef AMBIENT_LIGHT_DISABLED
#ifdef USE_LIGHTMAP
//lightmap
@@ -1439,10 +1511,23 @@ void fragment_shader(in SceneData scene_data) {
if (uses_sh) {
uvw.z *= 4.0; //SH textures use 4 times more data
- vec3 lm_light_l0 = textureLod(sampler2DArray(lightmap_textures[ofs], SAMPLER_LINEAR_CLAMP), uvw + vec3(0.0, 0.0, 0.0), 0.0).rgb;
- vec3 lm_light_l1n1 = textureLod(sampler2DArray(lightmap_textures[ofs], SAMPLER_LINEAR_CLAMP), uvw + vec3(0.0, 0.0, 1.0), 0.0).rgb;
- vec3 lm_light_l1_0 = textureLod(sampler2DArray(lightmap_textures[ofs], SAMPLER_LINEAR_CLAMP), uvw + vec3(0.0, 0.0, 2.0), 0.0).rgb;
- vec3 lm_light_l1p1 = textureLod(sampler2DArray(lightmap_textures[ofs], SAMPLER_LINEAR_CLAMP), uvw + vec3(0.0, 0.0, 3.0), 0.0).rgb;
+
+ vec3 lm_light_l0;
+ vec3 lm_light_l1n1;
+ vec3 lm_light_l1_0;
+ vec3 lm_light_l1p1;
+
+ if (sc_use_lightmap_bicubic_filter) {
+ lm_light_l0 = textureArray_bicubic(lightmap_textures[ofs], uvw + vec3(0.0, 0.0, 0.0), lightmaps.data[ofs].light_texture_size).rgb;
+ lm_light_l1n1 = textureArray_bicubic(lightmap_textures[ofs], uvw + vec3(0.0, 0.0, 1.0), lightmaps.data[ofs].light_texture_size).rgb;
+ lm_light_l1_0 = textureArray_bicubic(lightmap_textures[ofs], uvw + vec3(0.0, 0.0, 2.0), lightmaps.data[ofs].light_texture_size).rgb;
+ lm_light_l1p1 = textureArray_bicubic(lightmap_textures[ofs], uvw + vec3(0.0, 0.0, 3.0), lightmaps.data[ofs].light_texture_size).rgb;
+ } else {
+ lm_light_l0 = textureLod(sampler2DArray(lightmap_textures[ofs], SAMPLER_LINEAR_CLAMP), uvw + vec3(0.0, 0.0, 0.0), 0.0).rgb;
+ lm_light_l1n1 = textureLod(sampler2DArray(lightmap_textures[ofs], SAMPLER_LINEAR_CLAMP), uvw + vec3(0.0, 0.0, 1.0), 0.0).rgb;
+ lm_light_l1_0 = textureLod(sampler2DArray(lightmap_textures[ofs], SAMPLER_LINEAR_CLAMP), uvw + vec3(0.0, 0.0, 2.0), 0.0).rgb;
+ lm_light_l1p1 = textureLod(sampler2DArray(lightmap_textures[ofs], SAMPLER_LINEAR_CLAMP), uvw + vec3(0.0, 0.0, 3.0), 0.0).rgb;
+ }
vec3 n = normalize(lightmaps.data[ofs].normal_xform * normal);
float en = lightmaps.data[ofs].exposure_normalization;
@@ -1459,7 +1544,11 @@ void fragment_shader(in SceneData scene_data) {
}
} else {
- ambient_light += textureLod(sampler2DArray(lightmap_textures[ofs], SAMPLER_LINEAR_CLAMP), uvw, 0.0).rgb * lightmaps.data[ofs].exposure_normalization;
+ if (sc_use_lightmap_bicubic_filter) {
+ ambient_light += textureArray_bicubic(lightmap_textures[ofs], uvw, lightmaps.data[ofs].light_texture_size).rgb * lightmaps.data[ofs].exposure_normalization;
+ } else {
+ ambient_light += textureLod(sampler2DArray(lightmap_textures[ofs], SAMPLER_LINEAR_CLAMP), uvw, 0.0).rgb * lightmaps.data[ofs].exposure_normalization;
+ }
}
}
#else
@@ -1698,9 +1787,6 @@ void fragment_shader(in SceneData scene_data) {
//finalize ambient light here
{
-#if defined(AMBIENT_LIGHT_DISABLED)
- ambient_light = vec3(0.0, 0.0, 0.0);
-#else
ambient_light *= albedo.rgb;
ambient_light *= ao;
@@ -1713,15 +1799,14 @@ void fragment_shader(in SceneData scene_data) {
ambient_light *= 1.0 - ssil.a;
ambient_light += ssil.rgb * albedo.rgb;
}
-#endif // AMBIENT_LIGHT_DISABLED
}
-
+#endif // AMBIENT_LIGHT_DISABLED
// convert ao to direct light ao
ao = mix(1.0, ao, ao_light_affect);
//this saves some VGPRs
vec3 f0 = F0(metallic, specular, albedo);
-
+#ifndef AMBIENT_LIGHT_DISABLED
{
#if defined(DIFFUSE_TOON)
//simplify for toon, as
@@ -1743,6 +1828,7 @@ void fragment_shader(in SceneData scene_data) {
#endif
}
+#endif // !AMBIENT_LIGHT_DISABLED
#endif //GI !defined(MODE_RENDER_DEPTH) && !defined(MODE_UNSHADED)
#if !defined(MODE_RENDER_DEPTH)
@@ -2242,7 +2328,7 @@ void fragment_shader(in SceneData scene_data) {
alpha = min(alpha, clamp(length(ambient_light), 0.0, 1.0));
#if defined(ALPHA_SCISSOR_USED)
- if (alpha < alpha_scissor) {
+ if (alpha < alpha_scissor_threshold) {
discard;
}
#endif // ALPHA_SCISSOR_USED
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 441cf3c80c..18322a0619 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
@@ -94,8 +94,9 @@ directional_lights;
struct Lightmap {
mat3 normal_xform;
- vec3 pad;
+ vec2 light_texture_size;
float exposure_normalization;
+ vec2 pad;
};
layout(set = 0, binding = 7, std140) restrict readonly buffer Lightmaps {
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 530a7a37db..4e1da64151 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
@@ -521,6 +521,7 @@ layout(constant_id = 9) const bool sc_disable_omni_lights = false;
layout(constant_id = 10) const bool sc_disable_spot_lights = false;
layout(constant_id = 11) const bool sc_disable_reflection_probes = false;
layout(constant_id = 12) const bool sc_disable_directional_lights = false;
+layout(constant_id = 18) const bool sc_use_lightmap_bicubic_filter = false;
#endif //!MODE_UNSHADED
@@ -567,6 +568,67 @@ layout(location = 9) highp in float dp_clip;
#endif
+#ifdef USE_LIGHTMAP
+// w0, w1, w2, and w3 are the four cubic B-spline basis functions
+float w0(float a) {
+ return (1.0 / 6.0) * (a * (a * (-a + 3.0) - 3.0) + 1.0);
+}
+
+float w1(float a) {
+ return (1.0 / 6.0) * (a * a * (3.0 * a - 6.0) + 4.0);
+}
+
+float w2(float a) {
+ return (1.0 / 6.0) * (a * (a * (-3.0 * a + 3.0) + 3.0) + 1.0);
+}
+
+float w3(float a) {
+ return (1.0 / 6.0) * (a * a * a);
+}
+
+// g0 and g1 are the two amplitude functions
+float g0(float a) {
+ return w0(a) + w1(a);
+}
+
+float g1(float a) {
+ return w2(a) + w3(a);
+}
+
+// h0 and h1 are the two offset functions
+float h0(float a) {
+ return -1.0 + w1(a) / (w0(a) + w1(a));
+}
+
+float h1(float a) {
+ return 1.0 + w3(a) / (w2(a) + w3(a));
+}
+
+vec4 textureArray_bicubic(texture2DArray tex, vec3 uv, vec2 texture_size) {
+ vec2 texel_size = vec2(1.0) / texture_size;
+
+ uv.xy = uv.xy * texture_size + vec2(0.5);
+
+ vec2 iuv = floor(uv.xy);
+ vec2 fuv = fract(uv.xy);
+
+ float g0x = g0(fuv.x);
+ float g1x = g1(fuv.x);
+ float h0x = h0(fuv.x);
+ float h1x = h1(fuv.x);
+ float h0y = h0(fuv.y);
+ float h1y = h1(fuv.y);
+
+ vec2 p0 = (vec2(iuv.x + h0x, iuv.y + h0y) - vec2(0.5)) * texel_size;
+ vec2 p1 = (vec2(iuv.x + h1x, iuv.y + h0y) - vec2(0.5)) * texel_size;
+ vec2 p2 = (vec2(iuv.x + h0x, iuv.y + h1y) - vec2(0.5)) * texel_size;
+ vec2 p3 = (vec2(iuv.x + h1x, iuv.y + h1y) - vec2(0.5)) * texel_size;
+
+ return (g0(fuv.y) * (g0x * texture(sampler2DArray(tex, SAMPLER_LINEAR_CLAMP), vec3(p0, uv.z)) + g1x * texture(sampler2DArray(tex, SAMPLER_LINEAR_CLAMP), vec3(p1, uv.z)))) +
+ (g1(fuv.y) * (g0x * texture(sampler2DArray(tex, SAMPLER_LINEAR_CLAMP), vec3(p2, uv.z)) + g1x * texture(sampler2DArray(tex, SAMPLER_LINEAR_CLAMP), vec3(p3, uv.z))));
+}
+#endif //USE_LIGHTMAP
+
#ifdef USE_MULTIVIEW
#ifdef has_VK_KHR_multiview
#define ViewIndex gl_ViewIndex
@@ -839,6 +901,13 @@ void main() {
vec3 light_vertex = vertex;
#endif //LIGHT_VERTEX_USED
+ mat3 model_normal_matrix;
+ if (bool(instances.data[draw_call.instance_index].flags & INSTANCE_FLAGS_NON_UNIFORM_SCALE)) {
+ model_normal_matrix = transpose(inverse(mat3(read_model_matrix)));
+ } else {
+ model_normal_matrix = mat3(read_model_matrix);
+ }
+
mat4 read_view_matrix = scene_data.view_matrix;
vec2 read_viewport_size = scene_data.viewport_size;
@@ -1071,6 +1140,8 @@ void main() {
#if !defined(MODE_RENDER_DEPTH) && !defined(MODE_UNSHADED)
+#ifndef AMBIENT_LIGHT_DISABLED
+
if (scene_data.use_reflection_cubemap) {
#ifdef LIGHT_ANISOTROPY_USED
// https://google.github.io/filament/Filament.html#lighting/imagebasedlights/anisotropy
@@ -1158,13 +1229,14 @@ void main() {
#endif //USE_RADIANCE_CUBEMAP_ARRAY
specular_light += clearcoat_light * horizon * horizon * Fc * scene_data.ambient_light_color_energy.a;
}
-#endif
+#endif // LIGHT_CLEARCOAT_USED
+#endif // !AMBIENT_LIGHT_DISABLED
#endif //!defined(MODE_RENDER_DEPTH) && !defined(MODE_UNSHADED)
//radiance
#if !defined(MODE_RENDER_DEPTH) && !defined(MODE_UNSHADED)
-
+#ifndef AMBIENT_LIGHT_DISABLED
#ifdef USE_LIGHTMAP
//lightmap
@@ -1199,10 +1271,22 @@ void main() {
if (uses_sh) {
uvw.z *= 4.0; //SH textures use 4 times more data
- vec3 lm_light_l0 = textureLod(sampler2DArray(lightmap_textures[ofs], SAMPLER_LINEAR_CLAMP), uvw + vec3(0.0, 0.0, 0.0), 0.0).rgb;
- vec3 lm_light_l1n1 = textureLod(sampler2DArray(lightmap_textures[ofs], SAMPLER_LINEAR_CLAMP), uvw + vec3(0.0, 0.0, 1.0), 0.0).rgb;
- vec3 lm_light_l1_0 = textureLod(sampler2DArray(lightmap_textures[ofs], SAMPLER_LINEAR_CLAMP), uvw + vec3(0.0, 0.0, 2.0), 0.0).rgb;
- vec3 lm_light_l1p1 = textureLod(sampler2DArray(lightmap_textures[ofs], SAMPLER_LINEAR_CLAMP), uvw + vec3(0.0, 0.0, 3.0), 0.0).rgb;
+ vec3 lm_light_l0;
+ vec3 lm_light_l1n1;
+ vec3 lm_light_l1_0;
+ vec3 lm_light_l1p1;
+
+ if (sc_use_lightmap_bicubic_filter) {
+ lm_light_l0 = textureArray_bicubic(lightmap_textures[ofs], uvw + vec3(0.0, 0.0, 0.0), lightmaps.data[ofs].light_texture_size).rgb;
+ lm_light_l1n1 = textureArray_bicubic(lightmap_textures[ofs], uvw + vec3(0.0, 0.0, 1.0), lightmaps.data[ofs].light_texture_size).rgb;
+ lm_light_l1_0 = textureArray_bicubic(lightmap_textures[ofs], uvw + vec3(0.0, 0.0, 2.0), lightmaps.data[ofs].light_texture_size).rgb;
+ lm_light_l1p1 = textureArray_bicubic(lightmap_textures[ofs], uvw + vec3(0.0, 0.0, 3.0), lightmaps.data[ofs].light_texture_size).rgb;
+ } else {
+ lm_light_l0 = textureLod(sampler2DArray(lightmap_textures[ofs], SAMPLER_LINEAR_CLAMP), uvw + vec3(0.0, 0.0, 0.0), 0.0).rgb;
+ lm_light_l1n1 = textureLod(sampler2DArray(lightmap_textures[ofs], SAMPLER_LINEAR_CLAMP), uvw + vec3(0.0, 0.0, 1.0), 0.0).rgb;
+ lm_light_l1_0 = textureLod(sampler2DArray(lightmap_textures[ofs], SAMPLER_LINEAR_CLAMP), uvw + vec3(0.0, 0.0, 2.0), 0.0).rgb;
+ lm_light_l1p1 = textureLod(sampler2DArray(lightmap_textures[ofs], SAMPLER_LINEAR_CLAMP), uvw + vec3(0.0, 0.0, 3.0), 0.0).rgb;
+ }
vec3 n = normalize(lightmaps.data[ofs].normal_xform * normal);
float exposure_normalization = lightmaps.data[ofs].exposure_normalization;
@@ -1219,7 +1303,11 @@ void main() {
}
} else {
- ambient_light += textureLod(sampler2DArray(lightmap_textures[ofs], SAMPLER_LINEAR_CLAMP), uvw, 0.0).rgb * lightmaps.data[ofs].exposure_normalization;
+ if (sc_use_lightmap_bicubic_filter) {
+ ambient_light += textureArray_bicubic(lightmap_textures[ofs], uvw, lightmaps.data[ofs].light_texture_size).rgb * lightmaps.data[ofs].exposure_normalization;
+ } else {
+ ambient_light += textureLod(sampler2DArray(lightmap_textures[ofs], SAMPLER_LINEAR_CLAMP), uvw, 0.0).rgb * lightmaps.data[ofs].exposure_normalization;
+ }
}
}
@@ -1274,14 +1362,11 @@ void main() {
} //Reflection probes
// finalize ambient light here
- {
-#if defined(AMBIENT_LIGHT_DISABLED)
- ambient_light = vec3(0.0, 0.0, 0.0);
-#else
- ambient_light *= albedo.rgb;
- ambient_light *= ao;
-#endif // AMBIENT_LIGHT_DISABLED
- }
+
+ ambient_light *= albedo.rgb;
+ ambient_light *= ao;
+
+#endif // !AMBIENT_LIGHT_DISABLED
// convert ao to direct light ao
ao = mix(1.0, ao, ao_light_affect);
@@ -1289,6 +1374,7 @@ void main() {
//this saves some VGPRs
vec3 f0 = F0(metallic, specular, albedo);
+#ifndef AMBIENT_LIGHT_DISABLED
{
#if defined(DIFFUSE_TOON)
//simplify for toon, as
@@ -1310,6 +1396,7 @@ void main() {
#endif
}
+#endif // !AMBIENT_LIGHT_DISABLED
#endif // !defined(MODE_RENDER_DEPTH) && !defined(MODE_UNSHADED)
#if !defined(MODE_RENDER_DEPTH)
@@ -1765,7 +1852,7 @@ void main() {
alpha = min(alpha, clamp(length(ambient_light), 0.0, 1.0));
#if defined(ALPHA_SCISSOR_USED)
- if (alpha < alpha_scissor) {
+ if (alpha < alpha_scissor_threshold) {
discard;
}
#endif // !ALPHA_SCISSOR_USED
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 7674e905e1..d971ff04c5 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
@@ -71,8 +71,9 @@ directional_lights;
struct Lightmap {
mediump mat3 normal_xform;
- vec3 pad;
+ vec2 light_texture_size;
float exposure_normalization;
+ float pad;
};
layout(set = 0, binding = 7, std140) restrict readonly buffer Lightmaps {
diff --git a/servers/rendering/renderer_rd/storage_rd/light_storage.cpp b/servers/rendering/renderer_rd/storage_rd/light_storage.cpp
index 3d294ca8cb..c217c0fa9a 100644
--- a/servers/rendering/renderer_rd/storage_rd/light_storage.cpp
+++ b/servers/rendering/renderer_rd/storage_rd/light_storage.cpp
@@ -1813,6 +1813,7 @@ void LightStorage::lightmap_set_textures(RID p_lightmap, RID p_light, bool p_use
}
t->lightmap_users.insert(p_lightmap);
+ lm->light_texture_size = Vector2i(t->width, t->height);
if (using_lightmap_array) {
if (lm->array_index < 0) {
diff --git a/servers/rendering/renderer_rd/storage_rd/light_storage.h b/servers/rendering/renderer_rd/storage_rd/light_storage.h
index f152cc5dae..94ab219dc2 100644
--- a/servers/rendering/renderer_rd/storage_rd/light_storage.h
+++ b/servers/rendering/renderer_rd/storage_rd/light_storage.h
@@ -332,6 +332,7 @@ private:
bool interior = false;
AABB bounds = AABB(Vector3(), Vector3(1, 1, 1));
float baked_exposure = 1.0;
+ Vector2i light_texture_size;
int32_t array_index = -1; //unassigned
PackedVector3Array points;
PackedColorArray point_sh;
@@ -985,6 +986,10 @@ public:
const Lightmap *lm = lightmap_owner.get_or_null(p_lightmap);
return lm->uses_spherical_harmonics;
}
+ _FORCE_INLINE_ Vector2i lightmap_get_light_texture_size(RID p_lightmap) const {
+ const Lightmap *lm = lightmap_owner.get_or_null(p_lightmap);
+ return lm->light_texture_size;
+ }
_FORCE_INLINE_ uint64_t lightmap_array_get_version() const {
ERR_FAIL_COND_V(!using_lightmap_array, 0); //only for arrays
return lightmap_array_version;
diff --git a/servers/rendering/renderer_rd/storage_rd/material_storage.cpp b/servers/rendering/renderer_rd/storage_rd/material_storage.cpp
index 8b74ebfacd..63dc54e24c 100644
--- a/servers/rendering/renderer_rd/storage_rd/material_storage.cpp
+++ b/servers/rendering/renderer_rd/storage_rd/material_storage.cpp
@@ -580,11 +580,7 @@ void MaterialStorage::ShaderData::get_shader_uniform_list(List<PropertyInfo> *p_
if (E.value.scope != ShaderLanguage::ShaderNode::Uniform::SCOPE_LOCAL) {
continue;
}
- if (E.value.texture_order >= 0) {
- filtered_uniforms.push_back(Pair<StringName, int>(E.key, E.value.texture_order + 100000));
- } else {
- filtered_uniforms.push_back(Pair<StringName, int>(E.key, E.value.order));
- }
+ filtered_uniforms.push_back(Pair<StringName, int>(E.key, E.value.prop_order));
}
int uniform_count = filtered_uniforms.size();
sorter.sort(filtered_uniforms.ptr(), uniform_count);
@@ -634,7 +630,7 @@ bool MaterialStorage::ShaderData::is_parameter_texture(const StringName &p_param
return false;
}
- return uniforms[p_param].texture_order >= 0;
+ return uniforms[p_param].is_texture();
}
///////////////////////////////////////////////////////////////////////////
@@ -645,7 +641,7 @@ void MaterialStorage::MaterialData::update_uniform_buffer(const HashMap<StringNa
bool uses_global_buffer = false;
for (const KeyValue<StringName, ShaderLanguage::ShaderNode::Uniform> &E : p_uniforms) {
- if (E.value.order < 0) {
+ if (E.value.is_texture()) {
continue; // texture, does not go here
}
diff --git a/servers/rendering/renderer_rd/storage_rd/mesh_storage.cpp b/servers/rendering/renderer_rd/storage_rd/mesh_storage.cpp
index 4d0e7bcb04..539bdcbbd0 100644
--- a/servers/rendering/renderer_rd/storage_rd/mesh_storage.cpp
+++ b/servers/rendering/renderer_rd/storage_rd/mesh_storage.cpp
@@ -1056,8 +1056,9 @@ void MeshStorage::update_mesh_instances() {
mi->surfaces[i].previous_buffer = mi->surfaces[i].current_buffer;
- if (uses_motion_vectors && (frame - mi->surfaces[i].last_change) == 1) {
- // Previous buffer's data can only be one frame old to be able to use motion vectors.
+ if (uses_motion_vectors && mi->surfaces[i].last_change && (frame - mi->surfaces[i].last_change) <= 2) {
+ // Use a 2-frame tolerance so that stepped skeletal animations have correct motion vectors
+ // (stepped animation is common for distant NPCs).
uint32_t new_buffer_index = mi->surfaces[i].current_buffer ^ 1;
if (mi->surfaces[i].uniform_set[new_buffer_index].is_null()) {
diff --git a/servers/rendering/renderer_rd/storage_rd/render_data_rd.cpp b/servers/rendering/renderer_rd/storage_rd/render_data_rd.cpp
index ac4fbba75b..ddc4d09279 100644
--- a/servers/rendering/renderer_rd/storage_rd/render_data_rd.cpp
+++ b/servers/rendering/renderer_rd/storage_rd/render_data_rd.cpp
@@ -30,9 +30,6 @@
#include "render_data_rd.h"
-void RenderDataRD::_bind_methods() {
-}
-
Ref<RenderSceneBuffers> RenderDataRD::get_render_scene_buffers() const {
return render_buffers;
}
diff --git a/servers/rendering/renderer_rd/storage_rd/render_data_rd.h b/servers/rendering/renderer_rd/storage_rd/render_data_rd.h
index 3cd397b8ed..888527e1ef 100644
--- a/servers/rendering/renderer_rd/storage_rd/render_data_rd.h
+++ b/servers/rendering/renderer_rd/storage_rd/render_data_rd.h
@@ -38,9 +38,6 @@
class RenderDataRD : public RenderData {
GDCLASS(RenderDataRD, RenderData);
-protected:
- static void _bind_methods();
-
public:
// Access methods to expose data externally
virtual Ref<RenderSceneBuffers> get_render_scene_buffers() const override;
@@ -76,6 +73,8 @@ public:
uint32_t directional_light_count = 0;
bool directional_light_soft_shadows = false;
+ bool lightmap_bicubic_filter = false;
+
RenderingMethod::RenderInfo *render_info = nullptr;
/* Viewport data */
diff --git a/servers/rendering/renderer_rd/storage_rd/render_scene_data_rd.cpp b/servers/rendering/renderer_rd/storage_rd/render_scene_data_rd.cpp
index ba8aafda6d..148a556b46 100644
--- a/servers/rendering/renderer_rd/storage_rd/render_scene_data_rd.cpp
+++ b/servers/rendering/renderer_rd/storage_rd/render_scene_data_rd.cpp
@@ -34,9 +34,6 @@
#include "servers/rendering/renderer_rd/storage_rd/texture_storage.h"
#include "servers/rendering/rendering_server_default.h"
-void RenderSceneDataRD::_bind_methods() {
-}
-
Transform3D RenderSceneDataRD::get_cam_transform() const {
return cam_transform;
}
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 5579a97792..b2c93acd44 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
@@ -95,8 +95,6 @@ public:
virtual RID get_uniform_buffer() const override;
private:
- static void _bind_methods();
-
RID uniform_buffer; // loaded into this uniform buffer (supplied externally)
// This struct is loaded into Set 1 - Binding 0, populated at start of rendering a frame, must match with shader code
diff --git a/servers/rendering/renderer_scene_cull.h b/servers/rendering/renderer_scene_cull.h
index 53f1f2d246..972f66d325 100644
--- a/servers/rendering/renderer_scene_cull.h
+++ b/servers/rendering/renderer_scene_cull.h
@@ -1418,6 +1418,7 @@ public:
PASS1(decals_set_filter, RS::DecalFilter)
PASS1(light_projectors_set_filter, RS::LightProjectorFilter)
+ PASS1(lightmaps_set_bicubic_filter, bool)
virtual void update();
diff --git a/servers/rendering/renderer_scene_render.h b/servers/rendering/renderer_scene_render.h
index 719efa4df2..3446f5dd1b 100644
--- a/servers/rendering/renderer_scene_render.h
+++ b/servers/rendering/renderer_scene_render.h
@@ -338,6 +338,7 @@ public:
virtual void decals_set_filter(RS::DecalFilter p_filter) = 0;
virtual void light_projectors_set_filter(RS::LightProjectorFilter p_filter) = 0;
+ virtual void lightmaps_set_bicubic_filter(bool p_enable) = 0;
virtual void update() = 0;
virtual ~RendererSceneRender() {}
diff --git a/servers/rendering/renderer_viewport.cpp b/servers/rendering/renderer_viewport.cpp
index 1049cb63cd..7e45eba1de 100644
--- a/servers/rendering/renderer_viewport.cpp
+++ b/servers/rendering/renderer_viewport.cpp
@@ -1271,6 +1271,13 @@ void RendererViewport::viewport_set_use_hdr_2d(RID p_viewport, bool p_use_hdr_2d
RSG::texture_storage->render_target_set_use_hdr(viewport->render_target, p_use_hdr_2d);
}
+bool RendererViewport::viewport_is_using_hdr_2d(RID p_viewport) const {
+ Viewport *viewport = viewport_owner.get_or_null(p_viewport);
+ ERR_FAIL_NULL_V(viewport, false);
+
+ return viewport->use_hdr_2d;
+}
+
void RendererViewport::viewport_set_screen_space_aa(RID p_viewport, RS::ViewportScreenSpaceAA p_mode) {
Viewport *viewport = viewport_owner.get_or_null(p_viewport);
ERR_FAIL_NULL(viewport);
diff --git a/servers/rendering/renderer_viewport.h b/servers/rendering/renderer_viewport.h
index b36fc7f57f..bf97905f86 100644
--- a/servers/rendering/renderer_viewport.h
+++ b/servers/rendering/renderer_viewport.h
@@ -262,6 +262,8 @@ public:
void viewport_set_transparent_background(RID p_viewport, bool p_enabled);
void viewport_set_use_hdr_2d(RID p_viewport, bool p_use_hdr_2d);
+ bool viewport_is_using_hdr_2d(RID p_viewport) const;
+
void viewport_set_global_canvas_transform(RID p_viewport, const Transform2D &p_transform);
void viewport_set_canvas_stacking(RID p_viewport, RID p_canvas, int p_layer, int p_sublayer);
diff --git a/servers/rendering/rendering_method.h b/servers/rendering/rendering_method.h
index 57fbf97d8c..f6212faf08 100644
--- a/servers/rendering/rendering_method.h
+++ b/servers/rendering/rendering_method.h
@@ -349,6 +349,7 @@ public:
virtual void decals_set_filter(RS::DecalFilter p_filter) = 0;
virtual void light_projectors_set_filter(RS::LightProjectorFilter p_filter) = 0;
+ virtual void lightmaps_set_bicubic_filter(bool p_enable) = 0;
virtual bool free(RID p_rid) = 0;
diff --git a/servers/rendering/rendering_server_default.h b/servers/rendering/rendering_server_default.h
index a303bf96a8..60fa546e16 100644
--- a/servers/rendering/rendering_server_default.h
+++ b/servers/rendering/rendering_server_default.h
@@ -644,6 +644,7 @@ public:
FUNC3(viewport_set_canvas_transform, RID, RID, const Transform2D &)
FUNC2(viewport_set_transparent_background, RID, bool)
FUNC2(viewport_set_use_hdr_2d, RID, bool)
+ FUNC1RC(bool, viewport_is_using_hdr_2d, RID)
FUNC2(viewport_set_snap_2d_transforms_to_pixel, RID, bool)
FUNC2(viewport_set_snap_2d_vertices_to_pixel, RID, bool)
@@ -766,6 +767,7 @@ public:
FUNC1(directional_soft_shadow_filter_set_quality, ShadowQuality);
FUNC1(decals_set_filter, RS::DecalFilter);
FUNC1(light_projectors_set_filter, RS::LightProjectorFilter);
+ FUNC1(lightmaps_set_bicubic_filter, bool);
/* CAMERA ATTRIBUTES */
diff --git a/servers/rendering/shader_compiler.cpp b/servers/rendering/shader_compiler.cpp
index 2542f2eed7..49e005ca96 100644
--- a/servers/rendering/shader_compiler.cpp
+++ b/servers/rendering/shader_compiler.cpp
@@ -922,7 +922,7 @@ String ShaderCompiler::_dump_node_code(const SL::Node *p_node, int p_level, Gene
if (shader->uniforms.has(vnode->name)) {
//its a uniform!
const ShaderLanguage::ShaderNode::Uniform &u = shader->uniforms[vnode->name];
- if (u.texture_order >= 0) {
+ if (u.is_texture()) {
StringName name;
if (u.hint == ShaderLanguage::ShaderNode::Uniform::HINT_SCREEN_TEXTURE) {
name = "color_buffer";
@@ -1039,7 +1039,7 @@ String ShaderCompiler::_dump_node_code(const SL::Node *p_node, int p_level, Gene
if (shader->uniforms.has(anode->name)) {
//its a uniform!
const ShaderLanguage::ShaderNode::Uniform &u = shader->uniforms[anode->name];
- if (u.texture_order >= 0) {
+ if (u.is_texture()) {
code = _mkid(anode->name); //texture, use as is
} else {
//a scalar or vector
diff --git a/servers/rendering/shader_language.cpp b/servers/rendering/shader_language.cpp
index 431dca0ad4..1e54523775 100644
--- a/servers/rendering/shader_language.cpp
+++ b/servers/rendering/shader_language.cpp
@@ -898,6 +898,13 @@ bool ShaderLanguage::_lookup_next(Token &r_tk) {
return false;
}
+ShaderLanguage::Token ShaderLanguage::_peek() {
+ TkPos pre_pos = _get_tkpos();
+ Token tk = _get_token();
+ _set_tkpos(pre_pos);
+ return tk;
+}
+
String ShaderLanguage::token_debug(const String &p_code) {
clear();
@@ -8061,7 +8068,7 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const FunctionInfo &p_fun
if (!expr) {
return ERR_PARSE_ERROR;
}
- is_condition = expr->type == Node::NODE_TYPE_OPERATOR && expr->get_datatype() == TYPE_BOOL;
+ is_condition = expr->get_datatype() == TYPE_BOOL;
if (expr->type == Node::NODE_TYPE_OPERATOR) {
OperatorNode *op = static_cast<OperatorNode *>(expr);
@@ -8077,7 +8084,12 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const FunctionInfo &p_fun
if (p_block->block_type == BlockNode::BLOCK_TYPE_FOR_CONDITION) {
if (tk.type == TK_COMMA) {
if (!is_condition) {
- _set_error(RTR("The middle expression is expected to be a boolean operator."));
+ _set_error(RTR("The middle expression is expected to have a boolean data type."));
+ return ERR_PARSE_ERROR;
+ }
+ tk = _peek();
+ if (tk.type == TK_SEMICOLON) {
+ _set_error(vformat(RTR("Expected expression, found: '%s'."), get_token_text(tk)));
return ERR_PARSE_ERROR;
}
continue;
@@ -8088,6 +8100,11 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const FunctionInfo &p_fun
}
} else if (p_block->block_type == BlockNode::BLOCK_TYPE_FOR_EXPRESSION) {
if (tk.type == TK_COMMA) {
+ tk = _peek();
+ if (tk.type == TK_PARENTHESIS_CLOSE) {
+ _set_error(vformat(RTR("Expected expression, found: '%s'."), get_token_text(tk)));
+ return ERR_PARSE_ERROR;
+ }
continue;
}
if (tk.type != TK_PARENTHESIS_CLOSE) {
@@ -8106,7 +8123,7 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const FunctionInfo &p_fun
return ERR_PARSE_ERROR;
}
if (p_block->block_type == BlockNode::BLOCK_TYPE_FOR_CONDITION && !is_condition) {
- _set_error(RTR("The middle expression is expected to be a boolean operator."));
+ _set_error(RTR("The middle expression is expected to have a boolean data type."));
return ERR_PARSE_ERROR;
}
}
@@ -8211,6 +8228,7 @@ Error ShaderLanguage::_parse_shader(const HashMap<StringName, FunctionInfo> &p_f
int texture_binding = 0;
int uniforms = 0;
int instance_index = 0;
+ int prop_index = 0;
#ifdef DEBUG_ENABLED
uint64_t uniform_buffer_size = 0;
uint64_t max_uniform_buffer_size = 0;
@@ -8765,6 +8783,7 @@ Error ShaderLanguage::_parse_shader(const HashMap<StringName, FunctionInfo> &p_f
++texture_binding;
}
uniform.order = -1;
+ uniform.prop_order = prop_index++;
} else {
if (uniform_scope == ShaderNode::Uniform::SCOPE_INSTANCE && (type == TYPE_MAT2 || type == TYPE_MAT3 || type == TYPE_MAT4)) {
_set_error(vformat(RTR("The '%s' qualifier is not supported for matrix types."), "SCOPE_INSTANCE"));
@@ -8773,6 +8792,7 @@ Error ShaderLanguage::_parse_shader(const HashMap<StringName, FunctionInfo> &p_f
uniform.texture_order = -1;
if (uniform_scope != ShaderNode::Uniform::SCOPE_INSTANCE) {
uniform.order = uniforms++;
+ uniform.prop_order = prop_index++;
#ifdef DEBUG_ENABLED
if (check_device_limit_warnings) {
if (uniform.array_size > 0) {
diff --git a/servers/rendering/shader_language.h b/servers/rendering/shader_language.h
index 4a67c8f2d2..366cdd303f 100644
--- a/servers/rendering/shader_language.h
+++ b/servers/rendering/shader_language.h
@@ -648,6 +648,7 @@ public:
};
int order = 0;
+ int prop_order = 0;
int texture_order = 0;
int texture_binding = 0;
DataType type = TYPE_VOID;
@@ -664,6 +665,11 @@ public:
String group;
String subgroup;
+ _FORCE_INLINE_ bool is_texture() const {
+ // Order is assigned to -1 for texture uniforms.
+ return order < 0;
+ }
+
Uniform() {
hint_range[0] = 0.0f;
hint_range[1] = 1.0f;
@@ -1037,6 +1043,7 @@ private:
Token _make_token(TokenType p_type, const StringName &p_text = StringName());
Token _get_token();
bool _lookup_next(Token &r_tk);
+ Token _peek();
ShaderNode *shader = nullptr;
diff --git a/servers/rendering_server.cpp b/servers/rendering_server.cpp
index 7c10039622..0f0d15a52f 100644
--- a/servers/rendering_server.cpp
+++ b/servers/rendering_server.cpp
@@ -2484,6 +2484,7 @@ void RenderingServer::_bind_methods() {
ClassDB::bind_method(D_METHOD("light_directional_set_sky_mode", "light", "mode"), &RenderingServer::light_directional_set_sky_mode);
ClassDB::bind_method(D_METHOD("light_projectors_set_filter", "filter"), &RenderingServer::light_projectors_set_filter);
+ ClassDB::bind_method(D_METHOD("lightmaps_set_bicubic_filter", "enable"), &RenderingServer::lightmaps_set_bicubic_filter);
BIND_ENUM_CONSTANT(LIGHT_PROJECTOR_FILTER_NEAREST);
BIND_ENUM_CONSTANT(LIGHT_PROJECTOR_FILTER_LINEAR);
@@ -3625,6 +3626,7 @@ void RenderingServer::init() {
GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "rendering/lightmapping/probe_capture/update_speed", PROPERTY_HINT_RANGE, "0.001,256,0.001"), 15);
GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "rendering/lightmapping/primitive_meshes/texel_size", PROPERTY_HINT_RANGE, "0.001,100,0.001"), 0.2);
+ GLOBAL_DEF("rendering/lightmapping/lightmap_gi/use_bicubic_filter", true);
GLOBAL_DEF(PropertyInfo(Variant::INT, "rendering/global_illumination/sdfgi/probe_ray_count", PROPERTY_HINT_ENUM, "8 (Fastest),16,32,64,96,128 (Slowest)"), 1);
GLOBAL_DEF(PropertyInfo(Variant::INT, "rendering/global_illumination/sdfgi/frames_to_converge", PROPERTY_HINT_ENUM, "5 (Less Latency but Lower Quality),10,15,20,25,30 (More Latency but Higher Quality)"), 5);
diff --git a/servers/rendering_server.h b/servers/rendering_server.h
index 14d98c684b..d8b6651833 100644
--- a/servers/rendering_server.h
+++ b/servers/rendering_server.h
@@ -698,6 +698,8 @@ public:
virtual void lightmap_set_probe_capture_update_speed(float p_speed) = 0;
+ virtual void lightmaps_set_bicubic_filter(bool p_enable) = 0;
+
/* PARTICLES API */
virtual RID particles_create() = 0;
@@ -935,6 +937,7 @@ public:
virtual void viewport_set_canvas_transform(RID p_viewport, RID p_canvas, const Transform2D &p_offset) = 0;
virtual void viewport_set_transparent_background(RID p_viewport, bool p_enabled) = 0;
virtual void viewport_set_use_hdr_2d(RID p_viewport, bool p_use_hdr) = 0;
+ virtual bool viewport_is_using_hdr_2d(RID p_viewport) const = 0;
virtual void viewport_set_snap_2d_transforms_to_pixel(RID p_viewport, bool p_enabled) = 0;
virtual void viewport_set_snap_2d_vertices_to_pixel(RID p_viewport, bool p_enabled) = 0;
diff --git a/servers/text/text_server_dummy.h b/servers/text/text_server_dummy.h
index a5ab444f55..1a945ac221 100644
--- a/servers/text/text_server_dummy.h
+++ b/servers/text/text_server_dummy.h
@@ -88,6 +88,7 @@ public:
virtual int64_t font_get_char_from_glyph_index(const RID &p_font_rid, int64_t p_size, int64_t p_glyph_index) const override { return 0; }
virtual bool font_has_char(const RID &p_font_rid, int64_t p_char) const override { return false; }
virtual String font_get_supported_chars(const RID &p_font_rid) const override { return String(); }
+ virtual PackedInt32Array font_get_supported_glyphs(const RID &p_font_rid) const override { return PackedInt32Array(); };
virtual void font_draw_glyph(const RID &p_font_rid, const RID &p_canvas, int64_t p_size, const Vector2 &p_pos, int64_t p_index, const Color &p_color) const override {}
virtual void font_draw_glyph_outline(const RID &p_font_rid, const RID &p_canvas, int64_t p_size, int64_t p_outline_size, const Vector2 &p_pos, int64_t p_index, const Color &p_color) const override {}
diff --git a/servers/text/text_server_extension.cpp b/servers/text/text_server_extension.cpp
index 509d49a1e4..d387c8ff7e 100644
--- a/servers/text/text_server_extension.cpp
+++ b/servers/text/text_server_extension.cpp
@@ -196,6 +196,7 @@ void TextServerExtension::_bind_methods() {
GDVIRTUAL_BIND(_font_has_char, "font_rid", "char");
GDVIRTUAL_BIND(_font_get_supported_chars, "font_rid");
+ GDVIRTUAL_BIND(_font_get_supported_glyphs, "font_rid");
GDVIRTUAL_BIND(_font_render_range, "font_rid", "size", "start", "end");
GDVIRTUAL_BIND(_font_render_glyph, "font_rid", "size", "index");
@@ -927,6 +928,12 @@ String TextServerExtension::font_get_supported_chars(const RID &p_font_rid) cons
return ret;
}
+PackedInt32Array TextServerExtension::font_get_supported_glyphs(const RID &p_font_rid) const {
+ PackedInt32Array ret;
+ GDVIRTUAL_REQUIRED_CALL(_font_get_supported_glyphs, p_font_rid, ret);
+ return ret;
+}
+
void TextServerExtension::font_render_range(const RID &p_font_rid, const Vector2i &p_size, int64_t p_start, int64_t p_end) {
GDVIRTUAL_CALL(_font_render_range, p_font_rid, p_size, p_start, p_end);
}
diff --git a/servers/text/text_server_extension.h b/servers/text/text_server_extension.h
index 16a03b6592..52654c010c 100644
--- a/servers/text/text_server_extension.h
+++ b/servers/text/text_server_extension.h
@@ -323,8 +323,10 @@ public:
virtual bool font_has_char(const RID &p_font_rid, int64_t p_char) const override;
virtual String font_get_supported_chars(const RID &p_font_rid) const override;
+ virtual PackedInt32Array font_get_supported_glyphs(const RID &p_font_rid) const override;
GDVIRTUAL2RC(bool, _font_has_char, RID, int64_t);
GDVIRTUAL1RC(String, _font_get_supported_chars, RID);
+ GDVIRTUAL1RC(PackedInt32Array, _font_get_supported_glyphs, RID);
virtual void font_render_range(const RID &p_font, const Vector2i &p_size, int64_t p_start, int64_t p_end) override;
virtual void font_render_glyph(const RID &p_font_rid, const Vector2i &p_size, int64_t p_index) override;
diff --git a/servers/text_server.cpp b/servers/text_server.cpp
index e7a1511064..f391c79514 100644
--- a/servers/text_server.cpp
+++ b/servers/text_server.cpp
@@ -352,6 +352,7 @@ void TextServer::_bind_methods() {
ClassDB::bind_method(D_METHOD("font_has_char", "font_rid", "char"), &TextServer::font_has_char);
ClassDB::bind_method(D_METHOD("font_get_supported_chars", "font_rid"), &TextServer::font_get_supported_chars);
+ ClassDB::bind_method(D_METHOD("font_get_supported_glyphs", "font_rid"), &TextServer::font_get_supported_glyphs);
ClassDB::bind_method(D_METHOD("font_render_range", "font_rid", "size", "start", "end"), &TextServer::font_render_range);
ClassDB::bind_method(D_METHOD("font_render_glyph", "font_rid", "size", "index"), &TextServer::font_render_glyph);
@@ -1563,7 +1564,7 @@ int64_t TextServer::shaped_text_prev_grapheme_pos(const RID &p_shaped, int64_t p
int64_t TextServer::shaped_text_prev_character_pos(const RID &p_shaped, int64_t p_pos) const {
const PackedInt32Array &chars = shaped_text_get_character_breaks(p_shaped);
- int64_t prev = 0;
+ int64_t prev = shaped_text_get_range(p_shaped).x;
for (const int32_t &E : chars) {
if (E >= p_pos) {
return prev;
@@ -1575,7 +1576,7 @@ int64_t TextServer::shaped_text_prev_character_pos(const RID &p_shaped, int64_t
int64_t TextServer::shaped_text_next_character_pos(const RID &p_shaped, int64_t p_pos) const {
const PackedInt32Array &chars = shaped_text_get_character_breaks(p_shaped);
- int64_t prev = 0;
+ int64_t prev = shaped_text_get_range(p_shaped).x;
for (const int32_t &E : chars) {
if (E > p_pos) {
return E;
@@ -1587,7 +1588,7 @@ int64_t TextServer::shaped_text_next_character_pos(const RID &p_shaped, int64_t
int64_t TextServer::shaped_text_closest_character_pos(const RID &p_shaped, int64_t p_pos) const {
const PackedInt32Array &chars = shaped_text_get_character_breaks(p_shaped);
- int64_t prev = 0;
+ int64_t prev = shaped_text_get_range(p_shaped).x;
for (const int32_t &E : chars) {
if (E == p_pos) {
return E;
diff --git a/servers/text_server.h b/servers/text_server.h
index a77953e6f2..ba3fdaa35e 100644
--- a/servers/text_server.h
+++ b/servers/text_server.h
@@ -396,6 +396,7 @@ public:
virtual bool font_has_char(const RID &p_font_rid, int64_t p_char) const = 0;
virtual String font_get_supported_chars(const RID &p_font_rid) const = 0;
+ virtual PackedInt32Array font_get_supported_glyphs(const RID &p_font_rid) const = 0;
virtual void font_render_range(const RID &p_font, const Vector2i &p_size, int64_t p_start, int64_t p_end) = 0;
virtual void font_render_glyph(const RID &p_font_rid, const Vector2i &p_size, int64_t p_index) = 0;
diff --git a/tests/core/config/test_project_settings.h b/tests/core/config/test_project_settings.h
index 8fc2489f8b..0e1058a626 100644
--- a/tests/core/config/test_project_settings.h
+++ b/tests/core/config/test_project_settings.h
@@ -126,10 +126,9 @@ TEST_CASE("[ProjectSettings] localize_path") {
CHECK_EQ(ProjectSettings::get_singleton()->localize_path("path\\.\\filename"), "res://path/filename");
#endif
- // FIXME?: These checks pass, but that doesn't seems correct
- CHECK_EQ(ProjectSettings::get_singleton()->localize_path("../filename"), "res://filename");
- CHECK_EQ(ProjectSettings::get_singleton()->localize_path("../path/filename"), "res://path/filename");
- CHECK_EQ(ProjectSettings::get_singleton()->localize_path("..\\path\\filename"), "res://path/filename");
+ CHECK_EQ(ProjectSettings::get_singleton()->localize_path("../filename"), "../filename");
+ CHECK_EQ(ProjectSettings::get_singleton()->localize_path("../path/filename"), "../path/filename");
+ CHECK_EQ(ProjectSettings::get_singleton()->localize_path("..\\path\\filename"), "../path/filename");
CHECK_EQ(ProjectSettings::get_singleton()->localize_path("/testroot/filename"), "/testroot/filename");
CHECK_EQ(ProjectSettings::get_singleton()->localize_path("/testroot/path/filename"), "/testroot/path/filename");
diff --git a/tests/core/string/test_string.h b/tests/core/string/test_string.h
index cf57183a02..933eeff524 100644
--- a/tests/core/string/test_string.h
+++ b/tests/core/string/test_string.h
@@ -638,64 +638,90 @@ TEST_CASE("[String] Ends with") {
}
TEST_CASE("[String] Splitting") {
- String s = "Mars,Jupiter,Saturn,Uranus";
- const char *slices_l[3] = { "Mars", "Jupiter", "Saturn,Uranus" };
- MULTICHECK_SPLIT(s, split, ",", true, 2, slices_l, 3);
-
- const char *slices_r[3] = { "Mars,Jupiter", "Saturn", "Uranus" };
- MULTICHECK_SPLIT(s, rsplit, ",", true, 2, slices_r, 3);
-
- s = "test";
- const char *slices_3[4] = { "t", "e", "s", "t" };
- MULTICHECK_SPLIT(s, split, "", true, 0, slices_3, 4);
-
- s = "";
- const char *slices_4[1] = { "" };
- MULTICHECK_SPLIT(s, split, "", true, 0, slices_4, 1);
- MULTICHECK_SPLIT(s, split, "", false, 0, slices_4, 0);
-
- s = "Mars Jupiter Saturn Uranus";
- const char *slices_s[4] = { "Mars", "Jupiter", "Saturn", "Uranus" };
- Vector<String> l = s.split_spaces();
- for (int i = 0; i < l.size(); i++) {
- CHECK(l[i] == slices_s[i]);
+ {
+ const String s = "Mars,Jupiter,Saturn,Uranus";
+
+ const char *slices_l[3] = { "Mars", "Jupiter", "Saturn,Uranus" };
+ MULTICHECK_SPLIT(s, split, ",", true, 2, slices_l, 3);
+
+ const char *slices_r[3] = { "Mars,Jupiter", "Saturn", "Uranus" };
+ MULTICHECK_SPLIT(s, rsplit, ",", true, 2, slices_r, 3);
}
- s = "1.2;2.3 4.5";
- const double slices_d[3] = { 1.2, 2.3, 4.5 };
+ {
+ const String s = "test";
+ const char *slices[4] = { "t", "e", "s", "t" };
+ MULTICHECK_SPLIT(s, split, "", true, 0, slices, 4);
+ }
- Vector<double> d_arr;
- d_arr = s.split_floats(";");
- CHECK(d_arr.size() == 2);
- for (int i = 0; i < d_arr.size(); i++) {
- CHECK(ABS(d_arr[i] - slices_d[i]) <= 0.00001);
+ {
+ const String s = "";
+ const char *slices[1] = { "" };
+ MULTICHECK_SPLIT(s, split, "", true, 0, slices, 1);
+ MULTICHECK_SPLIT(s, split, "", false, 0, slices, 0);
}
- Vector<String> keys;
- keys.push_back(";");
- keys.push_back(" ");
-
- Vector<float> f_arr;
- f_arr = s.split_floats_mk(keys);
- CHECK(f_arr.size() == 3);
- for (int i = 0; i < f_arr.size(); i++) {
- CHECK(ABS(f_arr[i] - slices_d[i]) <= 0.00001);
+ {
+ const String s = "Mars Jupiter Saturn Uranus";
+ const char *slices[4] = { "Mars", "Jupiter", "Saturn", "Uranus" };
+ Vector<String> l = s.split_spaces();
+ for (int i = 0; i < l.size(); i++) {
+ CHECK(l[i] == slices[i]);
+ }
}
- s = "1;2 4";
- const int slices_i[3] = { 1, 2, 4 };
+ {
+ const String s = "1.2;2.3 4.5";
+ const double slices[3] = { 1.2, 2.3, 4.5 };
+
+ const Vector<double> d_arr = s.split_floats(";");
+ CHECK(d_arr.size() == 2);
+ for (int i = 0; i < d_arr.size(); i++) {
+ CHECK(ABS(d_arr[i] - slices[i]) <= 0.00001);
+ }
- Vector<int> ii;
- ii = s.split_ints(";");
- CHECK(ii.size() == 2);
- for (int i = 0; i < ii.size(); i++) {
- CHECK(ii[i] == slices_i[i]);
+ const Vector<String> keys = { ";", " " };
+ const Vector<float> f_arr = s.split_floats_mk(keys);
+ CHECK(f_arr.size() == 3);
+ for (int i = 0; i < f_arr.size(); i++) {
+ CHECK(ABS(f_arr[i] - slices[i]) <= 0.00001);
+ }
}
- ii = s.split_ints_mk(keys);
- CHECK(ii.size() == 3);
- for (int i = 0; i < ii.size(); i++) {
- CHECK(ii[i] == slices_i[i]);
+ {
+ const String s = " -2.0 5";
+ const double slices[10] = { 0, -2, 0, 0, 0, 0, 0, 0, 0, 5 };
+
+ const Vector<double> arr = s.split_floats(" ");
+ CHECK(arr.size() == 10);
+ for (int i = 0; i < arr.size(); i++) {
+ CHECK(ABS(arr[i] - slices[i]) <= 0.00001);
+ }
+
+ const Vector<String> keys = { ";", " " };
+ const Vector<float> mk = s.split_floats_mk(keys);
+ CHECK(mk.size() == 10);
+ for (int i = 0; i < mk.size(); i++) {
+ CHECK(mk[i] == slices[i]);
+ }
+ }
+
+ {
+ const String s = "1;2 4";
+ const int slices[3] = { 1, 2, 4 };
+
+ const Vector<int> arr = s.split_ints(";");
+ CHECK(arr.size() == 2);
+ for (int i = 0; i < arr.size(); i++) {
+ CHECK(arr[i] == slices[i]);
+ }
+
+ const Vector<String> keys = { ";", " " };
+ const Vector<int> mk = s.split_ints_mk(keys);
+ CHECK(mk.size() == 3);
+ for (int i = 0; i < mk.size(); i++) {
+ CHECK(mk[i] == slices[i]);
+ }
}
}
@@ -1594,7 +1620,7 @@ TEST_CASE("[String] Path functions") {
static const char *base_name[8] = { "C:\\Godot\\project\\test", "/Godot/project/test", "../Godot/project/test", "Godot\\test", "C:\\test", "res://test", "user://test", "/" };
static const char *ext[8] = { "tscn", "xscn", "scn", "doc", "", "", "", "test" };
static const char *file[8] = { "test.tscn", "test.xscn", "test.scn", "test.doc", "test.", "test", "test", ".test" };
- static const char *simplified[8] = { "C:/Godot/project/test.tscn", "/Godot/project/test.xscn", "Godot/project/test.scn", "Godot/test.doc", "C:/test.", "res://test", "user://test", "/.test" };
+ static const char *simplified[8] = { "C:/Godot/project/test.tscn", "/Godot/project/test.xscn", "../Godot/project/test.scn", "Godot/test.doc", "C:/test.", "res://test", "user://test", "/.test" };
static const bool abs[8] = { true, true, false, false, true, true, true, true };
for (int i = 0; i < 8; i++) {
diff --git a/tests/core/string/test_translation.h b/tests/core/string/test_translation.h
index acdd851b29..7c389191e3 100644
--- a/tests/core/string/test_translation.h
+++ b/tests/core/string/test_translation.h
@@ -34,6 +34,7 @@
#include "core/string/optimized_translation.h"
#include "core/string/translation.h"
#include "core/string/translation_po.h"
+#include "core/string/translation_server.h"
#ifdef TOOLS_ENABLED
#include "editor/import/resource_importer_csv_translation.h"
diff --git a/tests/core/string/test_translation_server.h b/tests/core/string/test_translation_server.h
index 2c20574309..ac1599f2e8 100644
--- a/tests/core/string/test_translation_server.h
+++ b/tests/core/string/test_translation_server.h
@@ -31,7 +31,7 @@
#ifndef TEST_TRANSLATION_SERVER_H
#define TEST_TRANSLATION_SERVER_H
-#include "core/string/translation.h"
+#include "core/string/translation_server.h"
#include "tests/test_macros.h"
diff --git a/tests/scene/test_code_edit.h b/tests/scene/test_code_edit.h
index a166002cdd..9ec1b812df 100644
--- a/tests/scene/test_code_edit.h
+++ b/tests/scene/test_code_edit.h
@@ -4609,6 +4609,26 @@ TEST_CASE("[SceneTree][CodeEdit] text manipulation") {
CHECK(code_edit->get_text() == "line 1\nline 2\nline 3");
CHECK(code_edit->get_caret_line() == 0);
CHECK(code_edit->get_caret_column() == 0);
+
+ // Unfold previous folded line on backspace if the caret is at the first column.
+ code_edit->set_line_folding_enabled(true);
+ code_edit->set_text("line 1\n\tline 2\nline 3");
+ code_edit->set_caret_line(2);
+ code_edit->set_caret_column(0);
+ code_edit->fold_line(0);
+ code_edit->backspace();
+ CHECK_FALSE(code_edit->is_line_folded(0));
+ code_edit->set_line_folding_enabled(false);
+
+ // Do not unfold previous line on backspace if the caret is not at the first column.
+ code_edit->set_line_folding_enabled(true);
+ code_edit->set_text("line 1\n\tline 2\nline 3");
+ code_edit->set_caret_line(2);
+ code_edit->set_caret_column(4);
+ code_edit->fold_line(0);
+ code_edit->backspace();
+ CHECK(code_edit->is_line_folded(0));
+ code_edit->set_line_folding_enabled(false);
}
SUBCASE("[TextEdit] cut") {
diff --git a/tests/scene/test_text_edit.h b/tests/scene/test_text_edit.h
index b2d9f5100e..69e27fe7a0 100644
--- a/tests/scene/test_text_edit.h
+++ b/tests/scene/test_text_edit.h
@@ -1763,6 +1763,28 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
CHECK_FALSE(text_edit->has_selection());
CHECK(text_edit->get_caret_line() == 0);
CHECK(text_edit->get_caret_column() == 4);
+
+ // Wrapped lines.
+ text_edit->set_line_wrapping_mode(TextEdit::LineWrappingMode::LINE_WRAPPING_BOUNDARY);
+ text_edit->set_text("this is some text\nfor selection");
+ text_edit->set_size(Size2(110, 100));
+ MessageQueue::get_singleton()->flush();
+
+ // Line 0 wraps: 'this is ', 'some text'.
+ // Line 1 wraps: 'for ', 'selection'.
+ CHECK(text_edit->is_line_wrapped(0));
+
+ // Select to the first character of a wrapped line.
+ SEND_GUI_MOUSE_BUTTON_EVENT(text_edit->get_rect_at_line_column(0, 11).get_center(), MouseButton::LEFT, MouseButtonMask::LEFT, Key::NONE);
+ SEND_GUI_MOUSE_MOTION_EVENT(text_edit->get_rect_at_line_column(0, 8).get_center(), MouseButtonMask::LEFT, Key::NONE);
+ CHECK(text_edit->has_selection());
+ CHECK(text_edit->get_selected_text() == "so");
+ CHECK(text_edit->get_selection_mode() == TextEdit::SELECTION_MODE_POINTER);
+ CHECK(text_edit->get_selection_origin_line() == 0);
+ CHECK(text_edit->get_selection_origin_column() == 10);
+ CHECK(text_edit->get_caret_line() == 0);
+ CHECK(text_edit->get_caret_column() == 8);
+ CHECK(text_edit->is_dragging_cursor());
}
SUBCASE("[TextEdit] mouse word select") {
@@ -5713,6 +5735,7 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
CHECK(text_edit->get_caret_count() == 2);
MessageQueue::get_singleton()->flush();
+ // Lines 0 and 4 are wrapped into 2 parts: 'this is ' and 'some'.
CHECK(text_edit->is_line_wrapped(0));
SIGNAL_DISCARD("text_set");
SIGNAL_DISCARD("text_changed");
@@ -5762,9 +5785,9 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
SIGNAL_CHECK("caret_changed", empty_signal_args);
SIGNAL_CHECK_FALSE("text_changed");
SIGNAL_CHECK_FALSE("lines_edited_from");
- text_edit->set_caret_column(12, false);
// Normal up over wrapped line to line 0.
+ text_edit->set_caret_column(12, false);
SEND_GUI_ACTION("ui_text_caret_up");
CHECK(text_edit->get_viewport()->is_input_handled());
CHECK(text_edit->get_caret_line() == 0);
@@ -5777,6 +5800,23 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
SIGNAL_CHECK("caret_changed", empty_signal_args);
SIGNAL_CHECK_FALSE("text_changed");
SIGNAL_CHECK_FALSE("lines_edited_from");
+
+ // Normal up from column 0 to a wrapped line.
+ text_edit->remove_secondary_carets();
+ text_edit->set_caret_line(5);
+ text_edit->set_caret_column(0);
+ SEND_GUI_ACTION("ui_text_caret_up");
+ CHECK(text_edit->get_viewport()->is_input_handled());
+ CHECK(text_edit->get_caret_line() == 4);
+ CHECK(text_edit->get_caret_column() == 8);
+ CHECK_FALSE(text_edit->has_selection(0));
+
+ // Normal up to column 0 of a wrapped line.
+ SEND_GUI_ACTION("ui_text_caret_up");
+ CHECK(text_edit->get_viewport()->is_input_handled());
+ CHECK(text_edit->get_caret_line() == 4);
+ CHECK(text_edit->get_caret_column() == 0);
+ CHECK_FALSE(text_edit->has_selection(0));
}
SUBCASE("[TextEdit] ui_text_caret_down") {
@@ -5792,6 +5832,7 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
MessageQueue::get_singleton()->flush();
+ // Lines 3 and 7 are wrapped into 2 parts: 'this is ' and 'some'.
CHECK(text_edit->is_line_wrapped(3));
SIGNAL_DISCARD("text_set");
SIGNAL_DISCARD("text_changed");
@@ -5841,9 +5882,9 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
SIGNAL_CHECK("caret_changed", empty_signal_args);
SIGNAL_CHECK_FALSE("text_changed");
SIGNAL_CHECK_FALSE("lines_edited_from");
- text_edit->set_caret_column(7, false);
// Normal down over wrapped line to last wrapped line.
+ text_edit->set_caret_column(7, false);
SEND_GUI_ACTION("ui_text_caret_down");
CHECK(text_edit->get_viewport()->is_input_handled());
CHECK(text_edit->get_caret_line() == 3);
@@ -5856,6 +5897,23 @@ TEST_CASE("[SceneTree][TextEdit] text entry") {
SIGNAL_CHECK("caret_changed", empty_signal_args);
SIGNAL_CHECK_FALSE("text_changed");
SIGNAL_CHECK_FALSE("lines_edited_from");
+
+ // Normal down to column 0 of a wrapped line.
+ text_edit->remove_secondary_carets();
+ text_edit->set_caret_line(3);
+ text_edit->set_caret_column(0);
+ SEND_GUI_ACTION("ui_text_caret_down");
+ CHECK(text_edit->get_viewport()->is_input_handled());
+ CHECK(text_edit->get_caret_line() == 3);
+ CHECK(text_edit->get_caret_column() == 8);
+ CHECK_FALSE(text_edit->has_selection(0));
+
+ // Normal down out of visual column 0 of a wrapped line moves to start of next line.
+ SEND_GUI_ACTION("ui_text_caret_down");
+ CHECK(text_edit->get_viewport()->is_input_handled());
+ CHECK(text_edit->get_caret_line() == 4);
+ CHECK(text_edit->get_caret_column() == 0);
+ CHECK_FALSE(text_edit->has_selection(0));
}
SUBCASE("[TextEdit] ui_text_caret_document_start") {
@@ -7162,7 +7220,7 @@ TEST_CASE("[SceneTree][TextEdit] multicaret") {
CHECK(text_edit->get_caret_line(0) == 2);
CHECK(text_edit->get_caret_column(0) == 5);
CHECK(text_edit->get_caret_line(1) == 2);
- CHECK(text_edit->get_caret_column(1) == 10);
+ CHECK(text_edit->get_caret_column(1) == 6);
// Cannot add caret below from last line last line wrap.
text_edit->add_caret_at_carets(true);
@@ -7171,7 +7229,7 @@ TEST_CASE("[SceneTree][TextEdit] multicaret") {
CHECK(text_edit->get_caret_line(0) == 2);
CHECK(text_edit->get_caret_column(0) == 5);
CHECK(text_edit->get_caret_line(1) == 2);
- CHECK(text_edit->get_caret_column(1) == 10);
+ CHECK(text_edit->get_caret_column(1) == 6);
// Add caret above from not first line wrap.
text_edit->remove_secondary_carets();
diff --git a/thirdparty/README.md b/thirdparty/README.md
index 47618d675b..bc2964c392 100644
--- a/thirdparty/README.md
+++ b/thirdparty/README.md
@@ -748,7 +748,7 @@ with the provided patch.
## openxr
- Upstream: https://github.com/KhronosGroup/OpenXR-SDK
-- Version: 1.0.34 (288d3a7ebc1ad959f62d51da75baa3d27438c499, 2024)
+- Version: 1.1.38 (f90488c4fb1537f4256d09d4a4d3ad5543ebaf24, 2024)
- License: Apache 2.0
Files extracted from upstream source:
diff --git a/thirdparty/openxr/include/openxr/openxr.h b/thirdparty/openxr/include/openxr/openxr.h
index c0c826b981..9ac66d8c69 100644
--- a/thirdparty/openxr/include/openxr/openxr.h
+++ b/thirdparty/openxr/include/openxr/openxr.h
@@ -19,13 +19,17 @@ extern "C" {
+// XR_VERSION_1_0 is a preprocessor guard. Do not pass it to API calls.
#define XR_VERSION_1_0 1
#include "openxr_platform_defines.h"
#define XR_MAKE_VERSION(major, minor, patch) \
((((major) & 0xffffULL) << 48) | (((minor) & 0xffffULL) << 32) | ((patch) & 0xffffffffULL))
// OpenXR current version number.
-#define XR_CURRENT_API_VERSION XR_MAKE_VERSION(1, 0, 34)
+#define XR_CURRENT_API_VERSION XR_MAKE_VERSION(1, 1, 38)
+
+// OpenXR 1.0 version number
+#define XR_API_VERSION_1_0 XR_MAKE_VERSION(1, 0, XR_VERSION_PATCH(XR_CURRENT_API_VERSION))
#define XR_VERSION_MAJOR(version) (uint16_t)(((uint64_t)(version) >> 48)& 0xffffULL)
#define XR_VERSION_MINOR(version) (uint16_t)(((uint64_t)(version) >> 32) & 0xffffULL)
@@ -41,7 +45,6 @@ extern "C" {
#define XR_NULL_HANDLE 0
#endif
#endif
-
#define XR_NULL_SYSTEM_ID 0
@@ -96,13 +99,20 @@ extern "C" {
#define XR_DEFINE_HANDLE(object) typedef uint64_t object;
#endif
#endif
-
+
+
+#if !defined(XR_DEFINE_OPAQUE_64)
+ #if (XR_PTR_SIZE == 8)
+ #define XR_DEFINE_OPAQUE_64(object) typedef struct object##_T* object;
+ #else
+ #define XR_DEFINE_OPAQUE_64(object) typedef uint64_t object;
+ #endif
+#endif
#if !defined(XR_DEFINE_ATOM)
#define XR_DEFINE_ATOM(object) typedef uint64_t object;
#endif
-
typedef uint64_t XrVersion;
typedef uint64_t XrFlags64;
@@ -190,6 +200,8 @@ typedef enum XrResult {
XR_ERROR_LOCALIZED_NAME_INVALID = -49,
XR_ERROR_GRAPHICS_REQUIREMENTS_CALL_MISSING = -50,
XR_ERROR_RUNTIME_UNAVAILABLE = -51,
+ XR_ERROR_EXTENSION_DEPENDENCY_NOT_ENABLED = -1000710001,
+ XR_ERROR_PERMISSION_INSUFFICIENT = -1000710000,
XR_ERROR_ANDROID_THREAD_SETTINGS_ID_INVALID_KHR = -1000003000,
XR_ERROR_ANDROID_THREAD_SETTINGS_FAILURE_KHR = -1000003001,
XR_ERROR_CREATE_SPATIAL_ANCHOR_FAILED_MSFT = -1000039001,
@@ -239,10 +251,15 @@ typedef enum XrResult {
XR_ERROR_SPACE_NETWORK_REQUEST_FAILED_FB = -1000169003,
XR_ERROR_SPACE_CLOUD_STORAGE_DISABLED_FB = -1000169004,
XR_ERROR_PASSTHROUGH_COLOR_LUT_BUFFER_SIZE_MISMATCH_META = -1000266000,
+ XR_ENVIRONMENT_DEPTH_NOT_AVAILABLE_META = 1000291000,
XR_ERROR_HINT_ALREADY_SET_QCOM = -1000306000,
XR_ERROR_NOT_AN_ANCHOR_HTC = -1000319000,
XR_ERROR_SPACE_NOT_LOCATABLE_EXT = -1000429000,
XR_ERROR_PLANE_DETECTION_PERMISSION_DENIED_EXT = -1000429001,
+ XR_ERROR_FUTURE_PENDING_EXT = -1000469001,
+ XR_ERROR_FUTURE_INVALID_EXT = -1000469002,
+ XR_ERROR_EXTENSION_DEPENDENCY_NOT_ENABLED_KHR = XR_ERROR_EXTENSION_DEPENDENCY_NOT_ENABLED,
+ XR_ERROR_PERMISSION_INSUFFICIENT_KHR = XR_ERROR_PERMISSION_INSUFFICIENT,
XR_RESULT_MAX_ENUM = 0x7FFFFFFF
} XrResult;
@@ -297,6 +314,9 @@ typedef enum XrStructureType {
XR_TYPE_ACTIONS_SYNC_INFO = 61,
XR_TYPE_BOUND_SOURCES_FOR_ACTION_ENUMERATE_INFO = 62,
XR_TYPE_INPUT_SOURCE_LOCALIZED_NAME_GET_INFO = 63,
+ XR_TYPE_SPACES_LOCATE_INFO = 1000471000,
+ XR_TYPE_SPACE_LOCATIONS = 1000471001,
+ XR_TYPE_SPACE_VELOCITIES = 1000471002,
XR_TYPE_COMPOSITION_LAYER_CUBE_KHR = 1000006000,
XR_TYPE_INSTANCE_CREATE_INFO_ANDROID_KHR = 1000008000,
XR_TYPE_COMPOSITION_LAYER_DEPTH_INFO_KHR = 1000010000,
@@ -562,6 +582,14 @@ typedef enum XrStructureType {
XR_TYPE_FACE_TRACKER_CREATE_INFO2_FB = 1000287014,
XR_TYPE_FACE_EXPRESSION_INFO2_FB = 1000287015,
XR_TYPE_FACE_EXPRESSION_WEIGHTS2_FB = 1000287016,
+ XR_TYPE_ENVIRONMENT_DEPTH_PROVIDER_CREATE_INFO_META = 1000291000,
+ XR_TYPE_ENVIRONMENT_DEPTH_SWAPCHAIN_CREATE_INFO_META = 1000291001,
+ XR_TYPE_ENVIRONMENT_DEPTH_SWAPCHAIN_STATE_META = 1000291002,
+ XR_TYPE_ENVIRONMENT_DEPTH_IMAGE_ACQUIRE_INFO_META = 1000291003,
+ XR_TYPE_ENVIRONMENT_DEPTH_IMAGE_VIEW_META = 1000291004,
+ XR_TYPE_ENVIRONMENT_DEPTH_IMAGE_META = 1000291005,
+ XR_TYPE_ENVIRONMENT_DEPTH_HAND_REMOVAL_SET_INFO_META = 1000291006,
+ XR_TYPE_SYSTEM_ENVIRONMENT_DEPTH_PROPERTIES_META = 1000291007,
XR_TYPE_PASSTHROUGH_CREATE_INFO_HTC = 1000317001,
XR_TYPE_PASSTHROUGH_COLOR_HTC = 1000317002,
XR_TYPE_PASSTHROUGH_MESH_TRANSFORM_INFO_HTC = 1000317003,
@@ -583,12 +611,19 @@ typedef enum XrStructureType {
XR_TYPE_PLANE_DETECTOR_LOCATION_EXT = 1000429005,
XR_TYPE_PLANE_DETECTOR_POLYGON_BUFFER_EXT = 1000429006,
XR_TYPE_SYSTEM_PLANE_DETECTION_PROPERTIES_EXT = 1000429007,
+ XR_TYPE_FUTURE_CANCEL_INFO_EXT = 1000469000,
+ XR_TYPE_FUTURE_POLL_INFO_EXT = 1000469001,
+ XR_TYPE_FUTURE_COMPLETION_EXT = 1000469002,
+ XR_TYPE_FUTURE_POLL_RESULT_EXT = 1000469003,
XR_TYPE_EVENT_DATA_USER_PRESENCE_CHANGED_EXT = 1000470000,
XR_TYPE_SYSTEM_USER_PRESENCE_PROPERTIES_EXT = 1000470001,
XR_TYPE_GRAPHICS_BINDING_VULKAN2_KHR = XR_TYPE_GRAPHICS_BINDING_VULKAN_KHR,
XR_TYPE_SWAPCHAIN_IMAGE_VULKAN2_KHR = XR_TYPE_SWAPCHAIN_IMAGE_VULKAN_KHR,
XR_TYPE_GRAPHICS_REQUIREMENTS_VULKAN2_KHR = XR_TYPE_GRAPHICS_REQUIREMENTS_VULKAN_KHR,
XR_TYPE_DEVICE_PCM_SAMPLE_RATE_GET_INFO_FB = XR_TYPE_DEVICE_PCM_SAMPLE_RATE_STATE_FB,
+ XR_TYPE_SPACES_LOCATE_INFO_KHR = XR_TYPE_SPACES_LOCATE_INFO,
+ XR_TYPE_SPACE_LOCATIONS_KHR = XR_TYPE_SPACE_LOCATIONS,
+ XR_TYPE_SPACE_VELOCITIES_KHR = XR_TYPE_SPACE_VELOCITIES,
XR_STRUCTURE_TYPE_MAX_ENUM = 0x7FFFFFFF
} XrStructureType;
@@ -601,8 +636,9 @@ typedef enum XrFormFactor {
typedef enum XrViewConfigurationType {
XR_VIEW_CONFIGURATION_TYPE_PRIMARY_MONO = 1,
XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO = 2,
- XR_VIEW_CONFIGURATION_TYPE_PRIMARY_QUAD_VARJO = 1000037000,
+ XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO_WITH_FOVEATED_INSET = 1000037000,
XR_VIEW_CONFIGURATION_TYPE_SECONDARY_MONO_FIRST_PERSON_OBSERVER_MSFT = 1000054000,
+ XR_VIEW_CONFIGURATION_TYPE_PRIMARY_QUAD_VARJO = XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO_WITH_FOVEATED_INSET,
XR_VIEW_CONFIGURATION_TYPE_MAX_ENUM = 0x7FFFFFFF
} XrViewConfigurationType;
@@ -617,10 +653,11 @@ typedef enum XrReferenceSpaceType {
XR_REFERENCE_SPACE_TYPE_VIEW = 1,
XR_REFERENCE_SPACE_TYPE_LOCAL = 2,
XR_REFERENCE_SPACE_TYPE_STAGE = 3,
+ XR_REFERENCE_SPACE_TYPE_LOCAL_FLOOR = 1000426000,
XR_REFERENCE_SPACE_TYPE_UNBOUNDED_MSFT = 1000038000,
XR_REFERENCE_SPACE_TYPE_COMBINED_EYE_VARJO = 1000121000,
XR_REFERENCE_SPACE_TYPE_LOCALIZATION_MAP_ML = 1000139000,
- XR_REFERENCE_SPACE_TYPE_LOCAL_FLOOR_EXT = 1000426000,
+ XR_REFERENCE_SPACE_TYPE_LOCAL_FLOOR_EXT = XR_REFERENCE_SPACE_TYPE_LOCAL_FLOOR,
XR_REFERENCE_SPACE_TYPE_MAX_ENUM = 0x7FFFFFFF
} XrReferenceSpaceType;
@@ -683,6 +720,8 @@ typedef enum XrObjectType {
XR_OBJECT_TYPE_SPACE_USER_FB = 1000241000,
XR_OBJECT_TYPE_PASSTHROUGH_COLOR_LUT_META = 1000266000,
XR_OBJECT_TYPE_FACE_TRACKER2_FB = 1000287012,
+ XR_OBJECT_TYPE_ENVIRONMENT_DEPTH_PROVIDER_META = 1000291000,
+ XR_OBJECT_TYPE_ENVIRONMENT_DEPTH_SWAPCHAIN_META = 1000291001,
XR_OBJECT_TYPE_PASSTHROUGH_HTC = 1000317000,
XR_OBJECT_TYPE_PLANE_DETECTOR_EXT = 1000429000,
XR_OBJECT_TYPE_MAX_ENUM = 0x7FFFFFFF
@@ -734,6 +773,7 @@ typedef XrFlags64 XrCompositionLayerFlags;
static const XrCompositionLayerFlags XR_COMPOSITION_LAYER_CORRECT_CHROMATIC_ABERRATION_BIT = 0x00000001;
static const XrCompositionLayerFlags XR_COMPOSITION_LAYER_BLEND_TEXTURE_SOURCE_ALPHA_BIT = 0x00000002;
static const XrCompositionLayerFlags XR_COMPOSITION_LAYER_UNPREMULTIPLIED_ALPHA_BIT = 0x00000004;
+static const XrCompositionLayerFlags XR_COMPOSITION_LAYER_INVERTED_ALPHA_BIT_EXT = 0x00000008;
typedef XrFlags64 XrViewStateFlags;
@@ -1595,6 +1635,91 @@ XRAPI_ATTR XrResult XRAPI_CALL xrStopHapticFeedback(
#endif /* !XR_NO_PROTOTYPES */
+// XR_VERSION_1_1 is a preprocessor guard. Do not pass it to API calls.
+#define XR_VERSION_1_1 1
+// OpenXR 1.1 version number
+#define XR_API_VERSION_1_1 XR_MAKE_VERSION(1, 1, XR_VERSION_PATCH(XR_CURRENT_API_VERSION))
+
+#define XR_UUID_SIZE 16
+typedef struct XrColor3f {
+ float r;
+ float g;
+ float b;
+} XrColor3f;
+
+typedef struct XrExtent3Df {
+ float width;
+ float height;
+ float depth;
+} XrExtent3Df;
+
+typedef struct XrSpheref {
+ XrPosef center;
+ float radius;
+} XrSpheref;
+
+typedef struct XrBoxf {
+ XrPosef center;
+ XrExtent3Df extents;
+} XrBoxf;
+
+typedef struct XrFrustumf {
+ XrPosef pose;
+ XrFovf fov;
+ float nearZ;
+ float farZ;
+} XrFrustumf;
+
+typedef struct XrUuid {
+ uint8_t data[XR_UUID_SIZE];
+} XrUuid;
+
+typedef struct XrSpacesLocateInfo {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrSpace baseSpace;
+ XrTime time;
+ uint32_t spaceCount;
+ const XrSpace* spaces;
+} XrSpacesLocateInfo;
+
+typedef struct XrSpaceLocationData {
+ XrSpaceLocationFlags locationFlags;
+ XrPosef pose;
+} XrSpaceLocationData;
+
+typedef struct XrSpaceLocations {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ uint32_t locationCount;
+ XrSpaceLocationData* locations;
+} XrSpaceLocations;
+
+typedef struct XrSpaceVelocityData {
+ XrSpaceVelocityFlags velocityFlags;
+ XrVector3f linearVelocity;
+ XrVector3f angularVelocity;
+} XrSpaceVelocityData;
+
+// XrSpaceVelocities extends XrSpaceLocations
+typedef struct XrSpaceVelocities {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ uint32_t velocityCount;
+ XrSpaceVelocityData* velocities;
+} XrSpaceVelocities;
+
+typedef XrResult (XRAPI_PTR *PFN_xrLocateSpaces)(XrSession session, const XrSpacesLocateInfo* locateInfo, XrSpaceLocations* spaceLocations);
+
+#ifndef XR_NO_PROTOTYPES
+XRAPI_ATTR XrResult XRAPI_CALL xrLocateSpaces(
+ XrSession session,
+ const XrSpacesLocateInfo* locateInfo,
+ XrSpaceLocations* spaceLocations);
+#endif /* !XR_NO_PROTOTYPES */
+
+
+// XR_KHR_composition_layer_cube is a preprocessor guard. Do not pass it to API calls.
#define XR_KHR_composition_layer_cube 1
#define XR_KHR_composition_layer_cube_SPEC_VERSION 8
#define XR_KHR_COMPOSITION_LAYER_CUBE_EXTENSION_NAME "XR_KHR_composition_layer_cube"
@@ -1611,6 +1736,7 @@ typedef struct XrCompositionLayerCubeKHR {
+// XR_KHR_composition_layer_depth is a preprocessor guard. Do not pass it to API calls.
#define XR_KHR_composition_layer_depth 1
#define XR_KHR_composition_layer_depth_SPEC_VERSION 6
#define XR_KHR_COMPOSITION_LAYER_DEPTH_EXTENSION_NAME "XR_KHR_composition_layer_depth"
@@ -1627,6 +1753,7 @@ typedef struct XrCompositionLayerDepthInfoKHR {
+// XR_KHR_composition_layer_cylinder is a preprocessor guard. Do not pass it to API calls.
#define XR_KHR_composition_layer_cylinder 1
#define XR_KHR_composition_layer_cylinder_SPEC_VERSION 4
#define XR_KHR_COMPOSITION_LAYER_CYLINDER_EXTENSION_NAME "XR_KHR_composition_layer_cylinder"
@@ -1645,6 +1772,7 @@ typedef struct XrCompositionLayerCylinderKHR {
+// XR_KHR_composition_layer_equirect is a preprocessor guard. Do not pass it to API calls.
#define XR_KHR_composition_layer_equirect 1
#define XR_KHR_composition_layer_equirect_SPEC_VERSION 3
#define XR_KHR_COMPOSITION_LAYER_EQUIRECT_EXTENSION_NAME "XR_KHR_composition_layer_equirect"
@@ -1663,6 +1791,7 @@ typedef struct XrCompositionLayerEquirectKHR {
+// XR_KHR_visibility_mask is a preprocessor guard. Do not pass it to API calls.
#define XR_KHR_visibility_mask 1
#define XR_KHR_visibility_mask_SPEC_VERSION 2
#define XR_KHR_VISIBILITY_MASK_EXTENSION_NAME "XR_KHR_visibility_mask"
@@ -1706,6 +1835,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrGetVisibilityMaskKHR(
#endif /* !XR_NO_PROTOTYPES */
+// XR_KHR_composition_layer_color_scale_bias is a preprocessor guard. Do not pass it to API calls.
#define XR_KHR_composition_layer_color_scale_bias 1
#define XR_KHR_composition_layer_color_scale_bias_SPEC_VERSION 5
#define XR_KHR_COMPOSITION_LAYER_COLOR_SCALE_BIAS_EXTENSION_NAME "XR_KHR_composition_layer_color_scale_bias"
@@ -1719,6 +1849,7 @@ typedef struct XrCompositionLayerColorScaleBiasKHR {
+// XR_KHR_loader_init is a preprocessor guard. Do not pass it to API calls.
#define XR_KHR_loader_init 1
#define XR_KHR_loader_init_SPEC_VERSION 2
#define XR_KHR_LOADER_INIT_EXTENSION_NAME "XR_KHR_loader_init"
@@ -1737,6 +1868,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrInitializeLoaderKHR(
#endif /* !XR_NO_PROTOTYPES */
+// XR_KHR_composition_layer_equirect2 is a preprocessor guard. Do not pass it to API calls.
#define XR_KHR_composition_layer_equirect2 1
#define XR_KHR_composition_layer_equirect2_SPEC_VERSION 1
#define XR_KHR_COMPOSITION_LAYER_EQUIRECT2_EXTENSION_NAME "XR_KHR_composition_layer_equirect2"
@@ -1756,6 +1888,7 @@ typedef struct XrCompositionLayerEquirect2KHR {
+// XR_KHR_binding_modification is a preprocessor guard. Do not pass it to API calls.
#define XR_KHR_binding_modification 1
#define XR_KHR_binding_modification_SPEC_VERSION 1
#define XR_KHR_BINDING_MODIFICATION_EXTENSION_NAME "XR_KHR_binding_modification"
@@ -1774,11 +1907,55 @@ typedef struct XrBindingModificationsKHR {
+// XR_KHR_swapchain_usage_input_attachment_bit is a preprocessor guard. Do not pass it to API calls.
#define XR_KHR_swapchain_usage_input_attachment_bit 1
#define XR_KHR_swapchain_usage_input_attachment_bit_SPEC_VERSION 3
#define XR_KHR_SWAPCHAIN_USAGE_INPUT_ATTACHMENT_BIT_EXTENSION_NAME "XR_KHR_swapchain_usage_input_attachment_bit"
+// XR_KHR_locate_spaces is a preprocessor guard. Do not pass it to API calls.
+#define XR_KHR_locate_spaces 1
+#define XR_KHR_locate_spaces_SPEC_VERSION 1
+#define XR_KHR_LOCATE_SPACES_EXTENSION_NAME "XR_KHR_locate_spaces"
+typedef XrSpacesLocateInfo XrSpacesLocateInfoKHR;
+
+typedef XrSpaceLocationData XrSpaceLocationDataKHR;
+
+typedef XrSpaceLocations XrSpaceLocationsKHR;
+
+typedef XrSpaceVelocityData XrSpaceVelocityDataKHR;
+
+typedef XrSpaceVelocities XrSpaceVelocitiesKHR;
+
+typedef XrResult (XRAPI_PTR *PFN_xrLocateSpacesKHR)(XrSession session, const XrSpacesLocateInfo* locateInfo, XrSpaceLocations* spaceLocations);
+
+#ifndef XR_NO_PROTOTYPES
+#ifdef XR_EXTENSION_PROTOTYPES
+XRAPI_ATTR XrResult XRAPI_CALL xrLocateSpacesKHR(
+ XrSession session,
+ const XrSpacesLocateInfo* locateInfo,
+ XrSpaceLocations* spaceLocations);
+#endif /* XR_EXTENSION_PROTOTYPES */
+#endif /* !XR_NO_PROTOTYPES */
+
+
+// XR_KHR_maintenance1 is a preprocessor guard. Do not pass it to API calls.
+#define XR_KHR_maintenance1 1
+#define XR_KHR_maintenance1_SPEC_VERSION 1
+#define XR_KHR_MAINTENANCE1_EXTENSION_NAME "XR_KHR_maintenance1"
+typedef XrColor3f XrColor3fKHR;
+
+typedef XrExtent3Df XrExtent3DfKHR;
+
+typedef XrSpheref XrSpherefKHR;
+
+typedef XrBoxf XrBoxfKHR;
+
+typedef XrFrustumf XrFrustumfKHR;
+
+
+
+// XR_EXT_performance_settings is a preprocessor guard. Do not pass it to API calls.
#define XR_EXT_performance_settings 1
#define XR_EXT_performance_settings_SPEC_VERSION 4
#define XR_EXT_PERFORMANCE_SETTINGS_EXTENSION_NAME "XR_EXT_performance_settings"
@@ -1831,6 +2008,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrPerfSettingsSetPerformanceLevelEXT(
#endif /* !XR_NO_PROTOTYPES */
+// XR_EXT_thermal_query is a preprocessor guard. Do not pass it to API calls.
#define XR_EXT_thermal_query 1
#define XR_EXT_thermal_query_SPEC_VERSION 2
#define XR_EXT_THERMAL_QUERY_EXTENSION_NAME "XR_EXT_thermal_query"
@@ -1848,6 +2026,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrThermalGetTemperatureTrendEXT(
#endif /* !XR_NO_PROTOTYPES */
+// XR_EXT_debug_utils is a preprocessor guard. Do not pass it to API calls.
#define XR_EXT_debug_utils 1
XR_DEFINE_HANDLE(XrDebugUtilsMessengerEXT)
#define XR_EXT_debug_utils_SPEC_VERSION 5
@@ -1953,6 +2132,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrSessionInsertDebugUtilsLabelEXT(
#endif /* !XR_NO_PROTOTYPES */
+// XR_EXT_eye_gaze_interaction is a preprocessor guard. Do not pass it to API calls.
#define XR_EXT_eye_gaze_interaction 1
#define XR_EXT_eye_gaze_interaction_SPEC_VERSION 2
#define XR_EXT_EYE_GAZE_INTERACTION_EXTENSION_NAME "XR_EXT_eye_gaze_interaction"
@@ -1972,6 +2152,7 @@ typedef struct XrEyeGazeSampleTimeEXT {
+// XR_EXTX_overlay is a preprocessor guard. Do not pass it to API calls.
#define XR_EXTX_overlay 1
#define XR_EXTX_overlay_SPEC_VERSION 5
#define XR_EXTX_OVERLAY_EXTENSION_NAME "XR_EXTX_overlay"
@@ -2001,16 +2182,19 @@ typedef struct XrEventDataMainSessionVisibilityChangedEXTX {
+// XR_VARJO_quad_views is a preprocessor guard. Do not pass it to API calls.
#define XR_VARJO_quad_views 1
#define XR_VARJO_quad_views_SPEC_VERSION 1
#define XR_VARJO_QUAD_VIEWS_EXTENSION_NAME "XR_VARJO_quad_views"
+// XR_MSFT_unbounded_reference_space is a preprocessor guard. Do not pass it to API calls.
#define XR_MSFT_unbounded_reference_space 1
#define XR_MSFT_unbounded_reference_space_SPEC_VERSION 1
#define XR_MSFT_UNBOUNDED_REFERENCE_SPACE_EXTENSION_NAME "XR_MSFT_unbounded_reference_space"
+// XR_MSFT_spatial_anchor is a preprocessor guard. Do not pass it to API calls.
#define XR_MSFT_spatial_anchor 1
XR_DEFINE_HANDLE(XrSpatialAnchorMSFT)
#define XR_MSFT_spatial_anchor_SPEC_VERSION 2
@@ -2052,6 +2236,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrDestroySpatialAnchorMSFT(
#endif /* !XR_NO_PROTOTYPES */
+// XR_FB_composition_layer_image_layout is a preprocessor guard. Do not pass it to API calls.
#define XR_FB_composition_layer_image_layout 1
#define XR_FB_composition_layer_image_layout_SPEC_VERSION 1
#define XR_FB_COMPOSITION_LAYER_IMAGE_LAYOUT_EXTENSION_NAME "XR_FB_composition_layer_image_layout"
@@ -2069,8 +2254,9 @@ typedef struct XrCompositionLayerImageLayoutFB {
+// XR_FB_composition_layer_alpha_blend is a preprocessor guard. Do not pass it to API calls.
#define XR_FB_composition_layer_alpha_blend 1
-#define XR_FB_composition_layer_alpha_blend_SPEC_VERSION 2
+#define XR_FB_composition_layer_alpha_blend_SPEC_VERSION 3
#define XR_FB_COMPOSITION_LAYER_ALPHA_BLEND_EXTENSION_NAME "XR_FB_composition_layer_alpha_blend"
typedef enum XrBlendFactorFB {
@@ -2094,16 +2280,19 @@ typedef struct XrCompositionLayerAlphaBlendFB {
+// XR_MND_headless is a preprocessor guard. Do not pass it to API calls.
#define XR_MND_headless 1
#define XR_MND_headless_SPEC_VERSION 2
#define XR_MND_HEADLESS_EXTENSION_NAME "XR_MND_headless"
+// XR_OCULUS_android_session_state_enable is a preprocessor guard. Do not pass it to API calls.
#define XR_OCULUS_android_session_state_enable 1
#define XR_OCULUS_android_session_state_enable_SPEC_VERSION 1
#define XR_OCULUS_ANDROID_SESSION_STATE_ENABLE_EXTENSION_NAME "XR_OCULUS_android_session_state_enable"
+// XR_EXT_view_configuration_depth_range is a preprocessor guard. Do not pass it to API calls.
#define XR_EXT_view_configuration_depth_range 1
#define XR_EXT_view_configuration_depth_range_SPEC_VERSION 1
#define XR_EXT_VIEW_CONFIGURATION_DEPTH_RANGE_EXTENSION_NAME "XR_EXT_view_configuration_depth_range"
@@ -2119,6 +2308,7 @@ typedef struct XrViewConfigurationDepthRangeEXT {
+// XR_EXT_conformance_automation is a preprocessor guard. Do not pass it to API calls.
#define XR_EXT_conformance_automation 1
#define XR_EXT_conformance_automation_SPEC_VERSION 3
#define XR_EXT_CONFORMANCE_AUTOMATION_EXTENSION_NAME "XR_EXT_conformance_automation"
@@ -2164,6 +2354,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrSetInputDeviceLocationEXT(
#endif /* !XR_NO_PROTOTYPES */
+// XR_MSFT_spatial_graph_bridge is a preprocessor guard. Do not pass it to API calls.
#define XR_MSFT_spatial_graph_bridge 1
XR_DEFINE_HANDLE(XrSpatialGraphNodeBindingMSFT)
#define XR_GUID_SIZE_MSFT 16
@@ -2231,11 +2422,13 @@ XRAPI_ATTR XrResult XRAPI_CALL xrGetSpatialGraphNodeBindingPropertiesMSFT(
#endif /* !XR_NO_PROTOTYPES */
+// XR_MSFT_hand_interaction is a preprocessor guard. Do not pass it to API calls.
#define XR_MSFT_hand_interaction 1
#define XR_MSFT_hand_interaction_SPEC_VERSION 1
#define XR_MSFT_HAND_INTERACTION_EXTENSION_NAME "XR_MSFT_hand_interaction"
+// XR_EXT_hand_tracking is a preprocessor guard. Do not pass it to API calls.
#define XR_EXT_hand_tracking 1
#define XR_HAND_JOINT_COUNT_EXT 26
@@ -2356,6 +2549,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrLocateHandJointsEXT(
#endif /* !XR_NO_PROTOTYPES */
+// XR_MSFT_hand_tracking_mesh is a preprocessor guard. Do not pass it to API calls.
#define XR_MSFT_hand_tracking_mesh 1
#define XR_MSFT_hand_tracking_mesh_SPEC_VERSION 4
#define XR_MSFT_HAND_TRACKING_MESH_EXTENSION_NAME "XR_MSFT_hand_tracking_mesh"
@@ -2442,6 +2636,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrUpdateHandMeshMSFT(
#endif /* !XR_NO_PROTOTYPES */
+// XR_MSFT_secondary_view_configuration is a preprocessor guard. Do not pass it to API calls.
#define XR_MSFT_secondary_view_configuration 1
#define XR_MSFT_secondary_view_configuration_SPEC_VERSION 1
#define XR_MSFT_SECONDARY_VIEW_CONFIGURATION_EXTENSION_NAME "XR_MSFT_secondary_view_configuration"
@@ -2494,11 +2689,13 @@ typedef struct XrSecondaryViewConfigurationSwapchainCreateInfoMSFT {
+// XR_MSFT_first_person_observer is a preprocessor guard. Do not pass it to API calls.
#define XR_MSFT_first_person_observer 1
#define XR_MSFT_first_person_observer_SPEC_VERSION 1
#define XR_MSFT_FIRST_PERSON_OBSERVER_EXTENSION_NAME "XR_MSFT_first_person_observer"
+// XR_MSFT_controller_model is a preprocessor guard. Do not pass it to API calls.
#define XR_MSFT_controller_model 1
#define XR_NULL_CONTROLLER_MODEL_KEY_MSFT 0
@@ -2574,11 +2771,13 @@ XRAPI_ATTR XrResult XRAPI_CALL xrGetControllerModelStateMSFT(
#endif /* !XR_NO_PROTOTYPES */
+// XR_EXT_win32_appcontainer_compatible is a preprocessor guard. Do not pass it to API calls.
#define XR_EXT_win32_appcontainer_compatible 1
#define XR_EXT_win32_appcontainer_compatible_SPEC_VERSION 1
#define XR_EXT_WIN32_APPCONTAINER_COMPATIBLE_EXTENSION_NAME "XR_EXT_win32_appcontainer_compatible"
+// XR_EPIC_view_configuration_fov is a preprocessor guard. Do not pass it to API calls.
#define XR_EPIC_view_configuration_fov 1
#define XR_EPIC_view_configuration_fov_SPEC_VERSION 2
#define XR_EPIC_VIEW_CONFIGURATION_FOV_EXTENSION_NAME "XR_EPIC_view_configuration_fov"
@@ -2592,6 +2791,7 @@ typedef struct XrViewConfigurationViewFovEPIC {
+// XR_MSFT_composition_layer_reprojection is a preprocessor guard. Do not pass it to API calls.
#define XR_MSFT_composition_layer_reprojection 1
#define XR_MSFT_composition_layer_reprojection_SPEC_VERSION 1
#define XR_MSFT_COMPOSITION_LAYER_REPROJECTION_EXTENSION_NAME "XR_MSFT_composition_layer_reprojection"
@@ -2634,11 +2834,13 @@ XRAPI_ATTR XrResult XRAPI_CALL xrEnumerateReprojectionModesMSFT(
#endif /* !XR_NO_PROTOTYPES */
+// XR_HUAWEI_controller_interaction is a preprocessor guard. Do not pass it to API calls.
#define XR_HUAWEI_controller_interaction 1
#define XR_HUAWEI_controller_interaction_SPEC_VERSION 1
#define XR_HUAWEI_CONTROLLER_INTERACTION_EXTENSION_NAME "XR_HUAWEI_controller_interaction"
+// XR_FB_swapchain_update_state is a preprocessor guard. Do not pass it to API calls.
#define XR_FB_swapchain_update_state 1
#define XR_FB_swapchain_update_state_SPEC_VERSION 3
#define XR_FB_SWAPCHAIN_UPDATE_STATE_EXTENSION_NAME "XR_FB_swapchain_update_state"
@@ -2663,6 +2865,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrGetSwapchainStateFB(
#endif /* !XR_NO_PROTOTYPES */
+// XR_FB_composition_layer_secure_content is a preprocessor guard. Do not pass it to API calls.
#define XR_FB_composition_layer_secure_content 1
#define XR_FB_composition_layer_secure_content_SPEC_VERSION 1
#define XR_FB_COMPOSITION_LAYER_SECURE_CONTENT_EXTENSION_NAME "XR_FB_composition_layer_secure_content"
@@ -2681,6 +2884,7 @@ typedef struct XrCompositionLayerSecureContentFB {
+// XR_FB_body_tracking is a preprocessor guard. Do not pass it to API calls.
#define XR_FB_body_tracking 1
XR_DEFINE_HANDLE(XrBodyTrackerFB)
#define XR_FB_body_tracking_SPEC_VERSION 1
@@ -2842,6 +3046,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrGetBodySkeletonFB(
#endif /* !XR_NO_PROTOTYPES */
+// XR_EXT_dpad_binding is a preprocessor guard. Do not pass it to API calls.
#define XR_EXT_dpad_binding 1
#define XR_EXT_dpad_binding_SPEC_VERSION 1
#define XR_EXT_DPAD_BINDING_EXTENSION_NAME "XR_EXT_dpad_binding"
@@ -2861,6 +3066,7 @@ typedef struct XrInteractionProfileDpadBindingEXT {
+// XR_VALVE_analog_threshold is a preprocessor guard. Do not pass it to API calls.
#define XR_VALVE_analog_threshold 1
#define XR_VALVE_analog_threshold_SPEC_VERSION 2
#define XR_VALVE_ANALOG_THRESHOLD_EXTENSION_NAME "XR_VALVE_analog_threshold"
@@ -2877,6 +3083,7 @@ typedef struct XrInteractionProfileAnalogThresholdVALVE {
+// XR_EXT_hand_joints_motion_range is a preprocessor guard. Do not pass it to API calls.
#define XR_EXT_hand_joints_motion_range 1
#define XR_EXT_hand_joints_motion_range_SPEC_VERSION 1
#define XR_EXT_HAND_JOINTS_MOTION_RANGE_EXTENSION_NAME "XR_EXT_hand_joints_motion_range"
@@ -2895,21 +3102,25 @@ typedef struct XrHandJointsMotionRangeInfoEXT {
+// XR_EXT_samsung_odyssey_controller is a preprocessor guard. Do not pass it to API calls.
#define XR_EXT_samsung_odyssey_controller 1
#define XR_EXT_samsung_odyssey_controller_SPEC_VERSION 1
#define XR_EXT_SAMSUNG_ODYSSEY_CONTROLLER_EXTENSION_NAME "XR_EXT_samsung_odyssey_controller"
+// XR_EXT_hp_mixed_reality_controller is a preprocessor guard. Do not pass it to API calls.
#define XR_EXT_hp_mixed_reality_controller 1
#define XR_EXT_hp_mixed_reality_controller_SPEC_VERSION 1
#define XR_EXT_HP_MIXED_REALITY_CONTROLLER_EXTENSION_NAME "XR_EXT_hp_mixed_reality_controller"
+// XR_MND_swapchain_usage_input_attachment_bit is a preprocessor guard. Do not pass it to API calls.
#define XR_MND_swapchain_usage_input_attachment_bit 1
#define XR_MND_swapchain_usage_input_attachment_bit_SPEC_VERSION 2
#define XR_MND_SWAPCHAIN_USAGE_INPUT_ATTACHMENT_BIT_EXTENSION_NAME "XR_MND_swapchain_usage_input_attachment_bit"
+// XR_MSFT_scene_understanding is a preprocessor guard. Do not pass it to API calls.
#define XR_MSFT_scene_understanding 1
XR_DEFINE_HANDLE(XrSceneObserverMSFT)
@@ -3240,6 +3451,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrGetSceneMeshBuffersMSFT(
#endif /* !XR_NO_PROTOTYPES */
+// XR_MSFT_scene_understanding_serialization is a preprocessor guard. Do not pass it to API calls.
#define XR_MSFT_scene_understanding_serialization 1
#define XR_MSFT_scene_understanding_serialization_SPEC_VERSION 2
#define XR_MSFT_SCENE_UNDERSTANDING_SERIALIZATION_EXTENSION_NAME "XR_MSFT_scene_understanding_serialization"
@@ -3280,6 +3492,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrGetSerializedSceneFragmentDataMSFT(
#endif /* !XR_NO_PROTOTYPES */
+// XR_FB_display_refresh_rate is a preprocessor guard. Do not pass it to API calls.
#define XR_FB_display_refresh_rate 1
#define XR_FB_display_refresh_rate_SPEC_VERSION 1
#define XR_FB_DISPLAY_REFRESH_RATE_EXTENSION_NAME "XR_FB_display_refresh_rate"
@@ -3313,11 +3526,13 @@ XRAPI_ATTR XrResult XRAPI_CALL xrRequestDisplayRefreshRateFB(
#endif /* !XR_NO_PROTOTYPES */
+// XR_HTC_vive_cosmos_controller_interaction is a preprocessor guard. Do not pass it to API calls.
#define XR_HTC_vive_cosmos_controller_interaction 1
#define XR_HTC_vive_cosmos_controller_interaction_SPEC_VERSION 1
#define XR_HTC_VIVE_COSMOS_CONTROLLER_INTERACTION_EXTENSION_NAME "XR_HTC_vive_cosmos_controller_interaction"
+// XR_HTCX_vive_tracker_interaction is a preprocessor guard. Do not pass it to API calls.
#define XR_HTCX_vive_tracker_interaction 1
#define XR_HTCX_vive_tracker_interaction_SPEC_VERSION 3
#define XR_HTCX_VIVE_TRACKER_INTERACTION_EXTENSION_NAME "XR_HTCX_vive_tracker_interaction"
@@ -3347,6 +3562,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrEnumerateViveTrackerPathsHTCX(
#endif /* !XR_NO_PROTOTYPES */
+// XR_HTC_facial_tracking is a preprocessor guard. Do not pass it to API calls.
#define XR_HTC_facial_tracking 1
#define XR_FACIAL_EXPRESSION_EYE_COUNT_HTC 14
@@ -3466,21 +3682,25 @@ XRAPI_ATTR XrResult XRAPI_CALL xrGetFacialExpressionsHTC(
#endif /* !XR_NO_PROTOTYPES */
+// XR_HTC_vive_focus3_controller_interaction is a preprocessor guard. Do not pass it to API calls.
#define XR_HTC_vive_focus3_controller_interaction 1
#define XR_HTC_vive_focus3_controller_interaction_SPEC_VERSION 2
#define XR_HTC_VIVE_FOCUS3_CONTROLLER_INTERACTION_EXTENSION_NAME "XR_HTC_vive_focus3_controller_interaction"
+// XR_HTC_hand_interaction is a preprocessor guard. Do not pass it to API calls.
#define XR_HTC_hand_interaction 1
#define XR_HTC_hand_interaction_SPEC_VERSION 1
#define XR_HTC_HAND_INTERACTION_EXTENSION_NAME "XR_HTC_hand_interaction"
+// XR_HTC_vive_wrist_tracker_interaction is a preprocessor guard. Do not pass it to API calls.
#define XR_HTC_vive_wrist_tracker_interaction 1
#define XR_HTC_vive_wrist_tracker_interaction_SPEC_VERSION 1
#define XR_HTC_VIVE_WRIST_TRACKER_INTERACTION_EXTENSION_NAME "XR_HTC_vive_wrist_tracker_interaction"
+// XR_FB_color_space is a preprocessor guard. Do not pass it to API calls.
#define XR_FB_color_space 1
#define XR_FB_color_space_SPEC_VERSION 3
#define XR_FB_COLOR_SPACE_EXTENSION_NAME "XR_FB_color_space"
@@ -3521,6 +3741,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrSetColorSpaceFB(
#endif /* !XR_NO_PROTOTYPES */
+// XR_FB_hand_tracking_mesh is a preprocessor guard. Do not pass it to API calls.
#define XR_FB_hand_tracking_mesh 1
#define XR_FB_hand_tracking_mesh_SPEC_VERSION 3
#define XR_FB_HAND_TRACKING_MESH_EXTENSION_NAME "XR_FB_hand_tracking_mesh"
@@ -3572,6 +3793,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrGetHandMeshFB(
#endif /* !XR_NO_PROTOTYPES */
+// XR_FB_hand_tracking_aim is a preprocessor guard. Do not pass it to API calls.
#define XR_FB_hand_tracking_aim 1
#define XR_FB_hand_tracking_aim_SPEC_VERSION 2
#define XR_FB_HAND_TRACKING_AIM_EXTENSION_NAME "XR_FB_hand_tracking_aim"
@@ -3602,6 +3824,7 @@ typedef struct XrHandTrackingAimStateFB {
+// XR_FB_hand_tracking_capsules is a preprocessor guard. Do not pass it to API calls.
#define XR_FB_hand_tracking_capsules 1
#define XR_HAND_TRACKING_CAPSULE_POINT_COUNT_FB 2
#define XR_HAND_TRACKING_CAPSULE_COUNT_FB 19
@@ -3624,9 +3847,9 @@ typedef struct XrHandTrackingCapsulesStateFB {
+// XR_FB_spatial_entity is a preprocessor guard. Do not pass it to API calls.
#define XR_FB_spatial_entity 1
XR_DEFINE_ATOM(XrAsyncRequestIdFB)
-#define XR_UUID_SIZE_EXT 16
#define XR_FB_spatial_entity_SPEC_VERSION 3
#define XR_FB_SPATIAL_ENTITY_EXTENSION_NAME "XR_FB_spatial_entity"
@@ -3672,9 +3895,7 @@ typedef struct XrSpaceComponentStatusFB {
XrBool32 changePending;
} XrSpaceComponentStatusFB;
-typedef struct XrUuidEXT {
- uint8_t data[XR_UUID_SIZE_EXT];
-} XrUuidEXT;
+typedef XrUuid XrUuidEXT;
typedef struct XrEventDataSpatialAnchorCreateCompleteFB {
XrStructureType type;
@@ -3732,6 +3953,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrGetSpaceComponentStatusFB(
#endif /* !XR_NO_PROTOTYPES */
+// XR_FB_foveation is a preprocessor guard. Do not pass it to API calls.
#define XR_FB_foveation 1
XR_DEFINE_HANDLE(XrFoveationProfileFB)
#define XR_FB_foveation_SPEC_VERSION 1
@@ -3781,6 +4003,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrDestroyFoveationProfileFB(
#endif /* !XR_NO_PROTOTYPES */
+// XR_FB_foveation_configuration is a preprocessor guard. Do not pass it to API calls.
#define XR_FB_foveation_configuration 1
#define XR_FB_foveation_configuration_SPEC_VERSION 1
#define XR_FB_FOVEATION_CONFIGURATION_EXTENSION_NAME "XR_FB_foveation_configuration"
@@ -3809,6 +4032,7 @@ typedef struct XrFoveationLevelProfileCreateInfoFB {
+// XR_FB_keyboard_tracking is a preprocessor guard. Do not pass it to API calls.
#define XR_FB_keyboard_tracking 1
#define XR_MAX_KEYBOARD_TRACKING_NAME_SIZE_FB 128
#define XR_FB_keyboard_tracking_SPEC_VERSION 1
@@ -3871,6 +4095,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrCreateKeyboardSpaceFB(
#endif /* !XR_NO_PROTOTYPES */
+// XR_FB_triangle_mesh is a preprocessor guard. Do not pass it to API calls.
#define XR_FB_triangle_mesh 1
XR_DEFINE_HANDLE(XrTriangleMeshFB)
#define XR_FB_triangle_mesh_SPEC_VERSION 2
@@ -3943,12 +4168,13 @@ XRAPI_ATTR XrResult XRAPI_CALL xrTriangleMeshEndVertexBufferUpdateFB(
#endif /* !XR_NO_PROTOTYPES */
+// XR_FB_passthrough is a preprocessor guard. Do not pass it to API calls.
#define XR_FB_passthrough 1
XR_DEFINE_HANDLE(XrPassthroughFB)
XR_DEFINE_HANDLE(XrPassthroughLayerFB)
XR_DEFINE_HANDLE(XrGeometryInstanceFB)
#define XR_PASSTHROUGH_COLOR_MAP_MONO_SIZE_FB 256
-#define XR_FB_passthrough_SPEC_VERSION 3
+#define XR_FB_passthrough_SPEC_VERSION 4
#define XR_FB_PASSTHROUGH_EXTENSION_NAME "XR_FB_passthrough"
typedef enum XrPassthroughLayerPurposeFB {
@@ -4007,7 +4233,6 @@ typedef struct XrPassthroughLayerCreateInfoFB {
XrPassthroughLayerPurposeFB purpose;
} XrPassthroughLayerCreateInfoFB;
-// XrCompositionLayerPassthroughFB extends XrCompositionLayerBaseHeader
typedef struct XrCompositionLayerPassthroughFB {
XrStructureType type;
const void* XR_MAY_ALIAS next;
@@ -4133,6 +4358,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrGeometryInstanceSetTransformFB(
#endif /* !XR_NO_PROTOTYPES */
+// XR_FB_render_model is a preprocessor guard. Do not pass it to API calls.
#define XR_FB_render_model 1
#define XR_NULL_RENDER_MODEL_KEY_FB 0
@@ -4216,6 +4442,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrLoadRenderModelFB(
#endif /* !XR_NO_PROTOTYPES */
+// XR_VARJO_foveated_rendering is a preprocessor guard. Do not pass it to API calls.
#define XR_VARJO_foveated_rendering 1
#define XR_VARJO_foveated_rendering_SPEC_VERSION 3
#define XR_VARJO_FOVEATED_RENDERING_EXTENSION_NAME "XR_VARJO_foveated_rendering"
@@ -4242,6 +4469,7 @@ typedef struct XrSystemFoveatedRenderingPropertiesVARJO {
+// XR_VARJO_composition_layer_depth_test is a preprocessor guard. Do not pass it to API calls.
#define XR_VARJO_composition_layer_depth_test 1
#define XR_VARJO_composition_layer_depth_test_SPEC_VERSION 2
#define XR_VARJO_COMPOSITION_LAYER_DEPTH_TEST_EXTENSION_NAME "XR_VARJO_composition_layer_depth_test"
@@ -4255,6 +4483,7 @@ typedef struct XrCompositionLayerDepthTestVARJO {
+// XR_VARJO_environment_depth_estimation is a preprocessor guard. Do not pass it to API calls.
#define XR_VARJO_environment_depth_estimation 1
#define XR_VARJO_environment_depth_estimation_SPEC_VERSION 1
#define XR_VARJO_ENVIRONMENT_DEPTH_ESTIMATION_EXTENSION_NAME "XR_VARJO_environment_depth_estimation"
@@ -4269,6 +4498,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrSetEnvironmentDepthEstimationVARJO(
#endif /* !XR_NO_PROTOTYPES */
+// XR_VARJO_marker_tracking is a preprocessor guard. Do not pass it to API calls.
#define XR_VARJO_marker_tracking 1
#define XR_VARJO_marker_tracking_SPEC_VERSION 1
#define XR_VARJO_MARKER_TRACKING_EXTENSION_NAME "XR_VARJO_marker_tracking"
@@ -4330,6 +4560,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrCreateMarkerSpaceVARJO(
#endif /* !XR_NO_PROTOTYPES */
+// XR_VARJO_view_offset is a preprocessor guard. Do not pass it to API calls.
#define XR_VARJO_view_offset 1
#define XR_VARJO_view_offset_SPEC_VERSION 1
#define XR_VARJO_VIEW_OFFSET_EXTENSION_NAME "XR_VARJO_view_offset"
@@ -4344,16 +4575,19 @@ XRAPI_ATTR XrResult XRAPI_CALL xrSetViewOffsetVARJO(
#endif /* !XR_NO_PROTOTYPES */
+// XR_VARJO_xr4_controller_interaction is a preprocessor guard. Do not pass it to API calls.
#define XR_VARJO_xr4_controller_interaction 1
#define XR_VARJO_xr4_controller_interaction_SPEC_VERSION 1
#define XR_VARJO_XR4_CONTROLLER_INTERACTION_EXTENSION_NAME "XR_VARJO_xr4_controller_interaction"
+// XR_ML_ml2_controller_interaction is a preprocessor guard. Do not pass it to API calls.
#define XR_ML_ml2_controller_interaction 1
#define XR_ML_ml2_controller_interaction_SPEC_VERSION 1
#define XR_ML_ML2_CONTROLLER_INTERACTION_EXTENSION_NAME "XR_ML_ml2_controller_interaction"
+// XR_ML_frame_end_info is a preprocessor guard. Do not pass it to API calls.
#define XR_ML_frame_end_info 1
#define XR_ML_frame_end_info_SPEC_VERSION 1
#define XR_ML_FRAME_END_INFO_EXTENSION_NAME "XR_ML_frame_end_info"
@@ -4373,6 +4607,7 @@ typedef struct XrFrameEndInfoML {
+// XR_ML_global_dimmer is a preprocessor guard. Do not pass it to API calls.
#define XR_ML_global_dimmer 1
#define XR_ML_global_dimmer_SPEC_VERSION 1
#define XR_ML_GLOBAL_DIMMER_EXTENSION_NAME "XR_ML_global_dimmer"
@@ -4391,6 +4626,7 @@ typedef struct XrGlobalDimmerFrameEndInfoML {
+// XR_ML_marker_understanding is a preprocessor guard. Do not pass it to API calls.
#define XR_ML_marker_understanding 1
XR_DEFINE_ATOM(XrMarkerML)
XR_DEFINE_HANDLE(XrMarkerDetectorML)
@@ -4619,6 +4855,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrCreateMarkerSpaceML(
#endif /* !XR_NO_PROTOTYPES */
+// XR_ML_localization_map is a preprocessor guard. Do not pass it to API calls.
#define XR_ML_localization_map 1
XR_DEFINE_HANDLE(XrExportedLocalizationMapML)
#define XR_MAX_LOCALIZATION_MAP_NAME_LENGTH_ML 64
@@ -4745,6 +4982,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrGetExportedLocalizationMapDataML(
#endif /* !XR_NO_PROTOTYPES */
+// XR_MSFT_spatial_anchor_persistence is a preprocessor guard. Do not pass it to API calls.
#define XR_MSFT_spatial_anchor_persistence 1
XR_DEFINE_HANDLE(XrSpatialAnchorStoreConnectionMSFT)
#define XR_MAX_SPATIAL_ANCHOR_NAME_SIZE_MSFT 256
@@ -4810,6 +5048,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrClearSpatialAnchorStoreMSFT(
#endif /* !XR_NO_PROTOTYPES */
+// XR_MSFT_scene_marker is a preprocessor guard. Do not pass it to API calls.
#define XR_MSFT_scene_marker 1
#define XR_MSFT_scene_marker_SPEC_VERSION 1
#define XR_MSFT_SCENE_MARKER_EXTENSION_NAME "XR_MSFT_scene_marker"
@@ -4882,6 +5121,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrGetSceneMarkerDecodedStringMSFT(
#endif /* !XR_NO_PROTOTYPES */
+// XR_ULTRALEAP_hand_tracking_forearm is a preprocessor guard. Do not pass it to API calls.
#define XR_ULTRALEAP_hand_tracking_forearm 1
#define XR_HAND_FOREARM_JOINT_COUNT_ULTRALEAP 27
@@ -4921,6 +5161,7 @@ typedef enum XrHandForearmJointULTRALEAP {
} XrHandForearmJointULTRALEAP;
+// XR_FB_spatial_entity_query is a preprocessor guard. Do not pass it to API calls.
#define XR_FB_spatial_entity_query 1
#define XR_FB_spatial_entity_query_SPEC_VERSION 1
#define XR_FB_SPATIAL_ENTITY_QUERY_EXTENSION_NAME "XR_FB_spatial_entity_query"
@@ -5020,6 +5261,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrRetrieveSpaceQueryResultsFB(
#endif /* !XR_NO_PROTOTYPES */
+// XR_FB_spatial_entity_storage is a preprocessor guard. Do not pass it to API calls.
#define XR_FB_spatial_entity_storage 1
#define XR_FB_spatial_entity_storage_SPEC_VERSION 1
#define XR_FB_SPATIAL_ENTITY_STORAGE_EXTENSION_NAME "XR_FB_spatial_entity_storage"
@@ -5082,11 +5324,13 @@ XRAPI_ATTR XrResult XRAPI_CALL xrEraseSpaceFB(
#endif /* !XR_NO_PROTOTYPES */
+// XR_FB_touch_controller_pro is a preprocessor guard. Do not pass it to API calls.
#define XR_FB_touch_controller_pro 1
#define XR_FB_touch_controller_pro_SPEC_VERSION 1
#define XR_FB_TOUCH_CONTROLLER_PRO_EXTENSION_NAME "XR_FB_touch_controller_pro"
+// XR_FB_spatial_entity_sharing is a preprocessor guard. Do not pass it to API calls.
#define XR_FB_spatial_entity_sharing 1
XR_DEFINE_HANDLE(XrSpaceUserFB)
#define XR_FB_spatial_entity_sharing_SPEC_VERSION 1
@@ -5119,6 +5363,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrShareSpacesFB(
#endif /* !XR_NO_PROTOTYPES */
+// XR_FB_space_warp is a preprocessor guard. Do not pass it to API calls.
#define XR_FB_space_warp 1
#define XR_FB_space_warp_SPEC_VERSION 2
#define XR_FB_SPACE_WARP_EXTENSION_NAME "XR_FB_space_warp"
@@ -5151,6 +5396,7 @@ typedef struct XrSystemSpaceWarpPropertiesFB {
+// XR_FB_haptic_amplitude_envelope is a preprocessor guard. Do not pass it to API calls.
#define XR_FB_haptic_amplitude_envelope 1
#define XR_MAX_HAPTIC_AMPLITUDE_ENVELOPE_SAMPLES_FB 4000u
@@ -5167,6 +5413,7 @@ typedef struct XrHapticAmplitudeEnvelopeVibrationFB {
+// XR_FB_scene is a preprocessor guard. Do not pass it to API calls.
#define XR_FB_scene 1
#define XR_FB_scene_SPEC_VERSION 4
#define XR_FB_SCENE_EXTENSION_NAME "XR_FB_scene"
@@ -5177,11 +5424,7 @@ static const XrSemanticLabelsSupportFlagsFB XR_SEMANTIC_LABELS_SUPPORT_MULTIPLE_
static const XrSemanticLabelsSupportFlagsFB XR_SEMANTIC_LABELS_SUPPORT_ACCEPT_DESK_TO_TABLE_MIGRATION_BIT_FB = 0x00000002;
static const XrSemanticLabelsSupportFlagsFB XR_SEMANTIC_LABELS_SUPPORT_ACCEPT_INVISIBLE_WALL_FACE_BIT_FB = 0x00000004;
-typedef struct XrExtent3DfFB {
- float width;
- float height;
- float depth;
-} XrExtent3DfFB;
+typedef XrExtent3Df XrExtent3DfFB;
typedef struct XrOffset3DfFB {
float x;
@@ -5263,11 +5506,13 @@ XRAPI_ATTR XrResult XRAPI_CALL xrGetSpaceRoomLayoutFB(
#endif /* !XR_NO_PROTOTYPES */
+// XR_EXT_palm_pose is a preprocessor guard. Do not pass it to API calls.
#define XR_EXT_palm_pose 1
-#define XR_EXT_palm_pose_SPEC_VERSION 2
+#define XR_EXT_palm_pose_SPEC_VERSION 3
#define XR_EXT_PALM_POSE_EXTENSION_NAME "XR_EXT_palm_pose"
+// XR_ALMALENCE_digital_lens_control is a preprocessor guard. Do not pass it to API calls.
#define XR_ALMALENCE_digital_lens_control 1
#define XR_ALMALENCE_digital_lens_control_SPEC_VERSION 1
#define XR_ALMALENCE_DIGITAL_LENS_CONTROL_EXTENSION_NAME "XR_ALMALENCE_digital_lens_control"
@@ -5293,6 +5538,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrSetDigitalLensControlALMALENCE(
#endif /* !XR_NO_PROTOTYPES */
+// XR_FB_scene_capture is a preprocessor guard. Do not pass it to API calls.
#define XR_FB_scene_capture 1
#define XR_FB_scene_capture_SPEC_VERSION 1
#define XR_FB_SCENE_CAPTURE_EXTENSION_NAME "XR_FB_scene_capture"
@@ -5322,6 +5568,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrRequestSceneCaptureFB(
#endif /* !XR_NO_PROTOTYPES */
+// XR_FB_spatial_entity_container is a preprocessor guard. Do not pass it to API calls.
#define XR_FB_spatial_entity_container 1
#define XR_FB_spatial_entity_container_SPEC_VERSION 2
#define XR_FB_SPATIAL_ENTITY_CONTAINER_EXTENSION_NAME "XR_FB_spatial_entity_container"
@@ -5345,6 +5592,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrGetSpaceContainerFB(
#endif /* !XR_NO_PROTOTYPES */
+// XR_META_foveation_eye_tracked is a preprocessor guard. Do not pass it to API calls.
#define XR_META_foveation_eye_tracked 1
#define XR_FOVEATION_CENTER_SIZE_META 2
#define XR_META_foveation_eye_tracked_SPEC_VERSION 1
@@ -5390,6 +5638,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrGetFoveationEyeTrackedStateMETA(
#endif /* !XR_NO_PROTOTYPES */
+// XR_FB_face_tracking is a preprocessor guard. Do not pass it to API calls.
#define XR_FB_face_tracking 1
#define XR_FACE_EXPRESSSION_SET_DEFAULT_FB XR_FACE_EXPRESSION_SET_DEFAULT_FB
@@ -5534,6 +5783,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrGetFaceExpressionWeightsFB(
#endif /* !XR_NO_PROTOTYPES */
+// XR_FB_eye_tracking_social is a preprocessor guard. Do not pass it to API calls.
#define XR_FB_eye_tracking_social 1
XR_DEFINE_HANDLE(XrEyeTrackerFB)
#define XR_FB_eye_tracking_social_SPEC_VERSION 1
@@ -5599,6 +5849,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrGetEyeGazesFB(
#endif /* !XR_NO_PROTOTYPES */
+// XR_FB_passthrough_keyboard_hands is a preprocessor guard. Do not pass it to API calls.
#define XR_FB_passthrough_keyboard_hands 1
#define XR_FB_passthrough_keyboard_hands_SPEC_VERSION 2
#define XR_FB_PASSTHROUGH_KEYBOARD_HANDS_EXTENSION_NAME "XR_FB_passthrough_keyboard_hands"
@@ -5620,6 +5871,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrPassthroughLayerSetKeyboardHandsIntensityFB(
#endif /* !XR_NO_PROTOTYPES */
+// XR_FB_composition_layer_settings is a preprocessor guard. Do not pass it to API calls.
#define XR_FB_composition_layer_settings 1
#define XR_FB_composition_layer_settings_SPEC_VERSION 1
#define XR_FB_COMPOSITION_LAYER_SETTINGS_EXTENSION_NAME "XR_FB_composition_layer_settings"
@@ -5641,11 +5893,13 @@ typedef struct XrCompositionLayerSettingsFB {
+// XR_FB_touch_controller_proximity is a preprocessor guard. Do not pass it to API calls.
#define XR_FB_touch_controller_proximity 1
#define XR_FB_touch_controller_proximity_SPEC_VERSION 1
#define XR_FB_TOUCH_CONTROLLER_PROXIMITY_EXTENSION_NAME "XR_FB_touch_controller_proximity"
+// XR_FB_haptic_pcm is a preprocessor guard. Do not pass it to API calls.
#define XR_FB_haptic_pcm 1
#define XR_MAX_HAPTIC_PCM_BUFFER_SIZE_FB 4000
@@ -5682,6 +5936,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrGetDeviceSampleRateFB(
#endif /* !XR_NO_PROTOTYPES */
+// XR_FB_composition_layer_depth_test is a preprocessor guard. Do not pass it to API calls.
#define XR_FB_composition_layer_depth_test 1
#define XR_FB_composition_layer_depth_test_SPEC_VERSION 1
#define XR_FB_COMPOSITION_LAYER_DEPTH_TEST_EXTENSION_NAME "XR_FB_composition_layer_depth_test"
@@ -5707,6 +5962,7 @@ typedef struct XrCompositionLayerDepthTestFB {
+// XR_META_local_dimming is a preprocessor guard. Do not pass it to API calls.
#define XR_META_local_dimming 1
#define XR_META_local_dimming_SPEC_VERSION 1
#define XR_META_LOCAL_DIMMING_EXTENSION_NAME "XR_META_local_dimming"
@@ -5725,6 +5981,7 @@ typedef struct XrLocalDimmingFrameEndInfoMETA {
+// XR_META_passthrough_preferences is a preprocessor guard. Do not pass it to API calls.
#define XR_META_passthrough_preferences 1
#define XR_META_passthrough_preferences_SPEC_VERSION 1
#define XR_META_PASSTHROUGH_PREFERENCES_EXTENSION_NAME "XR_META_passthrough_preferences"
@@ -5750,6 +6007,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrGetPassthroughPreferencesMETA(
#endif /* !XR_NO_PROTOTYPES */
+// XR_META_virtual_keyboard is a preprocessor guard. Do not pass it to API calls.
#define XR_META_virtual_keyboard 1
XR_DEFINE_HANDLE(XrVirtualKeyboardMETA)
#define XR_MAX_VIRTUAL_KEYBOARD_COMMIT_TEXT_SIZE_META 3992
@@ -5952,6 +6210,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrChangeVirtualKeyboardTextContextMETA(
#endif /* !XR_NO_PROTOTYPES */
+// XR_OCULUS_external_camera is a preprocessor guard. Do not pass it to API calls.
#define XR_OCULUS_external_camera 1
#define XR_MAX_EXTERNAL_CAMERA_NAME_SIZE_OCULUS 32
#define XR_OCULUS_external_camera_SPEC_VERSION 1
@@ -6009,6 +6268,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrEnumerateExternalCamerasOCULUS(
#endif /* !XR_NO_PROTOTYPES */
+// XR_META_performance_metrics is a preprocessor guard. Do not pass it to API calls.
#define XR_META_performance_metrics 1
#define XR_META_performance_metrics_SPEC_VERSION 2
#define XR_META_PERFORMANCE_METRICS_EXTENSION_NAME "XR_META_performance_metrics"
@@ -6072,6 +6332,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrQueryPerformanceMetricsCounterMETA(
#endif /* !XR_NO_PROTOTYPES */
+// XR_FB_spatial_entity_storage_batch is a preprocessor guard. Do not pass it to API calls.
#define XR_FB_spatial_entity_storage_batch 1
#define XR_FB_spatial_entity_storage_batch_SPEC_VERSION 1
#define XR_FB_SPATIAL_ENTITY_STORAGE_BATCH_EXTENSION_NAME "XR_FB_spatial_entity_storage_batch"
@@ -6102,6 +6363,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrSaveSpaceListFB(
#endif /* !XR_NO_PROTOTYPES */
+// XR_FB_spatial_entity_user is a preprocessor guard. Do not pass it to API calls.
#define XR_FB_spatial_entity_user 1
typedef uint64_t XrSpaceUserIdFB;
#define XR_FB_spatial_entity_user_SPEC_VERSION 1
@@ -6133,6 +6395,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrDestroySpaceUserFB(
#endif /* !XR_NO_PROTOTYPES */
+// XR_META_headset_id is a preprocessor guard. Do not pass it to API calls.
#define XR_META_headset_id 1
#define XR_META_headset_id_SPEC_VERSION 2
#define XR_META_HEADSET_ID_EXTENSION_NAME "XR_META_headset_id"
@@ -6145,6 +6408,7 @@ typedef struct XrSystemHeadsetIdPropertiesMETA {
+// XR_META_recommended_layer_resolution is a preprocessor guard. Do not pass it to API calls.
#define XR_META_recommended_layer_resolution 1
#define XR_META_recommended_layer_resolution_SPEC_VERSION 1
#define XR_META_RECOMMENDED_LAYER_RESOLUTION_EXTENSION_NAME "XR_META_recommended_layer_resolution"
@@ -6174,6 +6438,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrGetRecommendedLayerResolutionMETA(
#endif /* !XR_NO_PROTOTYPES */
+// XR_META_passthrough_color_lut is a preprocessor guard. Do not pass it to API calls.
#define XR_META_passthrough_color_lut 1
XR_DEFINE_HANDLE(XrPassthroughColorLutMETA)
#define XR_META_passthrough_color_lut_SPEC_VERSION 1
@@ -6248,6 +6513,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrUpdatePassthroughColorLutMETA(
#endif /* !XR_NO_PROTOTYPES */
+// XR_META_spatial_entity_mesh is a preprocessor guard. Do not pass it to API calls.
#define XR_META_spatial_entity_mesh 1
#define XR_META_spatial_entity_mesh_SPEC_VERSION 1
#define XR_META_SPATIAL_ENTITY_MESH_EXTENSION_NAME "XR_META_spatial_entity_mesh"
@@ -6279,16 +6545,19 @@ XRAPI_ATTR XrResult XRAPI_CALL xrGetSpaceTriangleMeshMETA(
#endif /* !XR_NO_PROTOTYPES */
+// XR_META_automatic_layer_filter is a preprocessor guard. Do not pass it to API calls.
#define XR_META_automatic_layer_filter 1
#define XR_META_automatic_layer_filter_SPEC_VERSION 1
#define XR_META_AUTOMATIC_LAYER_FILTER_EXTENSION_NAME "XR_META_automatic_layer_filter"
+// XR_META_touch_controller_plus is a preprocessor guard. Do not pass it to API calls.
#define XR_META_touch_controller_plus 1
#define XR_META_touch_controller_plus_SPEC_VERSION 1
#define XR_META_TOUCH_CONTROLLER_PLUS_EXTENSION_NAME "XR_META_touch_controller_plus"
+// XR_FB_face_tracking2 is a preprocessor guard. Do not pass it to API calls.
#define XR_FB_face_tracking2 1
XR_DEFINE_HANDLE(XrFaceTracker2FB)
#define XR_FB_face_tracking2_SPEC_VERSION 1
@@ -6443,16 +6712,147 @@ XRAPI_ATTR XrResult XRAPI_CALL xrGetFaceExpressionWeights2FB(
#endif /* !XR_NO_PROTOTYPES */
+// XR_META_environment_depth is a preprocessor guard. Do not pass it to API calls.
+#define XR_META_environment_depth 1
+XR_DEFINE_HANDLE(XrEnvironmentDepthProviderMETA)
+XR_DEFINE_HANDLE(XrEnvironmentDepthSwapchainMETA)
+#define XR_META_environment_depth_SPEC_VERSION 1
+#define XR_META_ENVIRONMENT_DEPTH_EXTENSION_NAME "XR_META_environment_depth"
+typedef XrFlags64 XrEnvironmentDepthProviderCreateFlagsMETA;
+
+// Flag bits for XrEnvironmentDepthProviderCreateFlagsMETA
+
+typedef XrFlags64 XrEnvironmentDepthSwapchainCreateFlagsMETA;
+
+// Flag bits for XrEnvironmentDepthSwapchainCreateFlagsMETA
+
+typedef struct XrEnvironmentDepthProviderCreateInfoMETA {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrEnvironmentDepthProviderCreateFlagsMETA createFlags;
+} XrEnvironmentDepthProviderCreateInfoMETA;
+
+typedef struct XrEnvironmentDepthSwapchainCreateInfoMETA {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrEnvironmentDepthSwapchainCreateFlagsMETA createFlags;
+} XrEnvironmentDepthSwapchainCreateInfoMETA;
+
+typedef struct XrEnvironmentDepthSwapchainStateMETA {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ uint32_t width;
+ uint32_t height;
+} XrEnvironmentDepthSwapchainStateMETA;
+
+typedef struct XrEnvironmentDepthImageAcquireInfoMETA {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrSpace space;
+ XrTime displayTime;
+} XrEnvironmentDepthImageAcquireInfoMETA;
+
+typedef struct XrEnvironmentDepthImageViewMETA {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrFovf fov;
+ XrPosef pose;
+} XrEnvironmentDepthImageViewMETA;
+
+typedef struct XrEnvironmentDepthImageMETA {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ uint32_t swapchainIndex;
+ float nearZ;
+ float farZ;
+ XrEnvironmentDepthImageViewMETA views[2];
+} XrEnvironmentDepthImageMETA;
+
+typedef struct XrEnvironmentDepthHandRemovalSetInfoMETA {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrBool32 enabled;
+} XrEnvironmentDepthHandRemovalSetInfoMETA;
+
+// XrSystemEnvironmentDepthPropertiesMETA extends XrSystemProperties
+typedef struct XrSystemEnvironmentDepthPropertiesMETA {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrBool32 supportsEnvironmentDepth;
+ XrBool32 supportsHandRemoval;
+} XrSystemEnvironmentDepthPropertiesMETA;
+
+typedef XrResult (XRAPI_PTR *PFN_xrCreateEnvironmentDepthProviderMETA)(XrSession session, const XrEnvironmentDepthProviderCreateInfoMETA* createInfo, XrEnvironmentDepthProviderMETA* environmentDepthProvider);
+typedef XrResult (XRAPI_PTR *PFN_xrDestroyEnvironmentDepthProviderMETA)(XrEnvironmentDepthProviderMETA environmentDepthProvider);
+typedef XrResult (XRAPI_PTR *PFN_xrStartEnvironmentDepthProviderMETA)(XrEnvironmentDepthProviderMETA environmentDepthProvider);
+typedef XrResult (XRAPI_PTR *PFN_xrStopEnvironmentDepthProviderMETA)(XrEnvironmentDepthProviderMETA environmentDepthProvider);
+typedef XrResult (XRAPI_PTR *PFN_xrCreateEnvironmentDepthSwapchainMETA)(XrEnvironmentDepthProviderMETA environmentDepthProvider, const XrEnvironmentDepthSwapchainCreateInfoMETA* createInfo, XrEnvironmentDepthSwapchainMETA* swapchain);
+typedef XrResult (XRAPI_PTR *PFN_xrDestroyEnvironmentDepthSwapchainMETA)(XrEnvironmentDepthSwapchainMETA swapchain);
+typedef XrResult (XRAPI_PTR *PFN_xrEnumerateEnvironmentDepthSwapchainImagesMETA)(XrEnvironmentDepthSwapchainMETA swapchain, uint32_t imageCapacityInput, uint32_t* imageCountOutput, XrSwapchainImageBaseHeader* images);
+typedef XrResult (XRAPI_PTR *PFN_xrGetEnvironmentDepthSwapchainStateMETA)(XrEnvironmentDepthSwapchainMETA swapchain, XrEnvironmentDepthSwapchainStateMETA* state);
+typedef XrResult (XRAPI_PTR *PFN_xrAcquireEnvironmentDepthImageMETA)(XrEnvironmentDepthProviderMETA environmentDepthProvider, const XrEnvironmentDepthImageAcquireInfoMETA* acquireInfo, XrEnvironmentDepthImageMETA* environmentDepthImage);
+typedef XrResult (XRAPI_PTR *PFN_xrSetEnvironmentDepthHandRemovalMETA)(XrEnvironmentDepthProviderMETA environmentDepthProvider, const XrEnvironmentDepthHandRemovalSetInfoMETA* setInfo);
+
+#ifndef XR_NO_PROTOTYPES
+#ifdef XR_EXTENSION_PROTOTYPES
+XRAPI_ATTR XrResult XRAPI_CALL xrCreateEnvironmentDepthProviderMETA(
+ XrSession session,
+ const XrEnvironmentDepthProviderCreateInfoMETA* createInfo,
+ XrEnvironmentDepthProviderMETA* environmentDepthProvider);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrDestroyEnvironmentDepthProviderMETA(
+ XrEnvironmentDepthProviderMETA environmentDepthProvider);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrStartEnvironmentDepthProviderMETA(
+ XrEnvironmentDepthProviderMETA environmentDepthProvider);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrStopEnvironmentDepthProviderMETA(
+ XrEnvironmentDepthProviderMETA environmentDepthProvider);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrCreateEnvironmentDepthSwapchainMETA(
+ XrEnvironmentDepthProviderMETA environmentDepthProvider,
+ const XrEnvironmentDepthSwapchainCreateInfoMETA* createInfo,
+ XrEnvironmentDepthSwapchainMETA* swapchain);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrDestroyEnvironmentDepthSwapchainMETA(
+ XrEnvironmentDepthSwapchainMETA swapchain);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrEnumerateEnvironmentDepthSwapchainImagesMETA(
+ XrEnvironmentDepthSwapchainMETA swapchain,
+ uint32_t imageCapacityInput,
+ uint32_t* imageCountOutput,
+ XrSwapchainImageBaseHeader* images);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrGetEnvironmentDepthSwapchainStateMETA(
+ XrEnvironmentDepthSwapchainMETA swapchain,
+ XrEnvironmentDepthSwapchainStateMETA* state);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrAcquireEnvironmentDepthImageMETA(
+ XrEnvironmentDepthProviderMETA environmentDepthProvider,
+ const XrEnvironmentDepthImageAcquireInfoMETA* acquireInfo,
+ XrEnvironmentDepthImageMETA* environmentDepthImage);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrSetEnvironmentDepthHandRemovalMETA(
+ XrEnvironmentDepthProviderMETA environmentDepthProvider,
+ const XrEnvironmentDepthHandRemovalSetInfoMETA* setInfo);
+#endif /* XR_EXTENSION_PROTOTYPES */
+#endif /* !XR_NO_PROTOTYPES */
+
+
+// XR_EXT_uuid is a preprocessor guard. Do not pass it to API calls.
#define XR_EXT_uuid 1
#define XR_EXT_uuid_SPEC_VERSION 1
#define XR_EXT_UUID_EXTENSION_NAME "XR_EXT_uuid"
+#define XR_UUID_SIZE_EXT 16
+// XR_EXT_hand_interaction is a preprocessor guard. Do not pass it to API calls.
#define XR_EXT_hand_interaction 1
#define XR_EXT_hand_interaction_SPEC_VERSION 1
#define XR_EXT_HAND_INTERACTION_EXTENSION_NAME "XR_EXT_hand_interaction"
+// XR_QCOM_tracking_optimization_settings is a preprocessor guard. Do not pass it to API calls.
#define XR_QCOM_tracking_optimization_settings 1
#define XR_QCOM_tracking_optimization_settings_SPEC_VERSION 1
#define XR_QCOM_TRACKING_OPTIMIZATION_SETTINGS_EXTENSION_NAME "XR_QCOM_tracking_optimization_settings"
@@ -6482,6 +6882,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrSetTrackingOptimizationSettingsHintQCOM(
#endif /* !XR_NO_PROTOTYPES */
+// XR_HTC_passthrough is a preprocessor guard. Do not pass it to API calls.
#define XR_HTC_passthrough 1
XR_DEFINE_HANDLE(XrPassthroughHTC)
#define XR_HTC_passthrough_SPEC_VERSION 1
@@ -6543,6 +6944,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrDestroyPassthroughHTC(
#endif /* !XR_NO_PROTOTYPES */
+// XR_HTC_foveation is a preprocessor guard. Do not pass it to API calls.
#define XR_HTC_foveation 1
#define XR_HTC_foveation_SPEC_VERSION 1
#define XR_HTC_FOVEATION_EXTENSION_NAME "XR_HTC_foveation"
@@ -6609,6 +7011,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrApplyFoveationHTC(
#endif /* !XR_NO_PROTOTYPES */
+// XR_HTC_anchor is a preprocessor guard. Do not pass it to API calls.
#define XR_HTC_anchor 1
#define XR_MAX_SPATIAL_ANCHOR_NAME_SIZE_HTC 256
#define XR_HTC_anchor_SPEC_VERSION 1
@@ -6649,6 +7052,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrGetSpatialAnchorNameHTC(
#endif /* !XR_NO_PROTOTYPES */
+// XR_EXT_active_action_set_priority is a preprocessor guard. Do not pass it to API calls.
#define XR_EXT_active_action_set_priority 1
#define XR_EXT_active_action_set_priority_SPEC_VERSION 1
#define XR_EXT_ACTIVE_ACTION_SET_PRIORITY_EXTENSION_NAME "XR_EXT_active_action_set_priority"
@@ -6667,6 +7071,7 @@ typedef struct XrActiveActionSetPrioritiesEXT {
+// XR_MNDX_force_feedback_curl is a preprocessor guard. Do not pass it to API calls.
#define XR_MNDX_force_feedback_curl 1
#define XR_MNDX_force_feedback_curl_SPEC_VERSION 1
#define XR_MNDX_FORCE_FEEDBACK_CURL_EXTENSION_NAME "XR_MNDX_force_feedback_curl"
@@ -6709,16 +7114,19 @@ XRAPI_ATTR XrResult XRAPI_CALL xrApplyForceFeedbackCurlMNDX(
#endif /* !XR_NO_PROTOTYPES */
+// XR_BD_controller_interaction is a preprocessor guard. Do not pass it to API calls.
#define XR_BD_controller_interaction 1
#define XR_BD_controller_interaction_SPEC_VERSION 2
#define XR_BD_CONTROLLER_INTERACTION_EXTENSION_NAME "XR_BD_controller_interaction"
+// XR_EXT_local_floor is a preprocessor guard. Do not pass it to API calls.
#define XR_EXT_local_floor 1
#define XR_EXT_local_floor_SPEC_VERSION 1
#define XR_EXT_LOCAL_FLOOR_EXTENSION_NAME "XR_EXT_local_floor"
+// XR_EXT_hand_tracking_data_source is a preprocessor guard. Do not pass it to API calls.
#define XR_EXT_hand_tracking_data_source 1
#define XR_EXT_hand_tracking_data_source_SPEC_VERSION 1
#define XR_EXT_HAND_TRACKING_DATA_SOURCE_EXTENSION_NAME "XR_EXT_hand_tracking_data_source"
@@ -6746,9 +7154,10 @@ typedef struct XrHandTrackingDataSourceStateEXT {
+// XR_EXT_plane_detection is a preprocessor guard. Do not pass it to API calls.
#define XR_EXT_plane_detection 1
XR_DEFINE_HANDLE(XrPlaneDetectorEXT)
-#define XR_EXT_plane_detection_SPEC_VERSION 1
+#define XR_EXT_plane_detection_SPEC_VERSION 2
#define XR_EXT_PLANE_DETECTION_EXTENSION_NAME "XR_EXT_plane_detection"
typedef enum XrPlaneDetectorOrientationEXT {
@@ -6805,11 +7214,7 @@ typedef struct XrPlaneDetectorCreateInfoEXT {
XrPlaneDetectorFlagsEXT flags;
} XrPlaneDetectorCreateInfoEXT;
-typedef struct XrExtent3DfEXT {
- float width;
- float height;
- float depth;
-} XrExtent3DfEXT;
+typedef XrExtent3Df XrExtent3DfEXT;
typedef struct XrPlaneDetectorBeginInfoEXT {
XrStructureType type;
@@ -6900,11 +7305,72 @@ XRAPI_ATTR XrResult XRAPI_CALL xrGetPlanePolygonBufferEXT(
#endif /* !XR_NO_PROTOTYPES */
+// XR_OPPO_controller_interaction is a preprocessor guard. Do not pass it to API calls.
#define XR_OPPO_controller_interaction 1
#define XR_OPPO_controller_interaction_SPEC_VERSION 1
#define XR_OPPO_CONTROLLER_INTERACTION_EXTENSION_NAME "XR_OPPO_controller_interaction"
+// XR_EXT_future is a preprocessor guard. Do not pass it to API calls.
+#define XR_EXT_future 1
+XR_DEFINE_OPAQUE_64(XrFutureEXT)
+#define XR_EXT_future_SPEC_VERSION 1
+#define XR_EXT_FUTURE_EXTENSION_NAME "XR_EXT_future"
+#define XR_NULL_FUTURE_EXT 0
+
+typedef enum XrFutureStateEXT {
+ XR_FUTURE_STATE_PENDING_EXT = 1,
+ XR_FUTURE_STATE_READY_EXT = 2,
+ XR_FUTURE_STATE_MAX_ENUM_EXT = 0x7FFFFFFF
+} XrFutureStateEXT;
+typedef struct XrFutureCancelInfoEXT {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrFutureEXT future;
+} XrFutureCancelInfoEXT;
+
+typedef struct XrFuturePollInfoEXT {
+ XrStructureType type;
+ const void* XR_MAY_ALIAS next;
+ XrFutureEXT future;
+} XrFuturePollInfoEXT;
+
+typedef struct XR_MAY_ALIAS XrFutureCompletionBaseHeaderEXT {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrResult futureResult;
+} XrFutureCompletionBaseHeaderEXT;
+
+typedef struct XrFutureCompletionEXT {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrResult futureResult;
+} XrFutureCompletionEXT;
+
+typedef struct XrFuturePollResultEXT {
+ XrStructureType type;
+ void* XR_MAY_ALIAS next;
+ XrFutureStateEXT state;
+} XrFuturePollResultEXT;
+
+typedef XrResult (XRAPI_PTR *PFN_xrPollFutureEXT)(XrInstance instance, const XrFuturePollInfoEXT* pollInfo, XrFuturePollResultEXT* pollResult);
+typedef XrResult (XRAPI_PTR *PFN_xrCancelFutureEXT)(XrInstance instance, const XrFutureCancelInfoEXT* cancelInfo);
+
+#ifndef XR_NO_PROTOTYPES
+#ifdef XR_EXTENSION_PROTOTYPES
+XRAPI_ATTR XrResult XRAPI_CALL xrPollFutureEXT(
+ XrInstance instance,
+ const XrFuturePollInfoEXT* pollInfo,
+ XrFuturePollResultEXT* pollResult);
+
+XRAPI_ATTR XrResult XRAPI_CALL xrCancelFutureEXT(
+ XrInstance instance,
+ const XrFutureCancelInfoEXT* cancelInfo);
+#endif /* XR_EXTENSION_PROTOTYPES */
+#endif /* !XR_NO_PROTOTYPES */
+
+
+// XR_EXT_user_presence is a preprocessor guard. Do not pass it to API calls.
#define XR_EXT_user_presence 1
#define XR_EXT_user_presence_SPEC_VERSION 1
#define XR_EXT_USER_PRESENCE_EXTENSION_NAME "XR_EXT_user_presence"
@@ -6924,6 +7390,7 @@ typedef struct XrSystemUserPresencePropertiesEXT {
+// XR_ML_user_calibration is a preprocessor guard. Do not pass it to API calls.
#define XR_ML_user_calibration 1
#define XR_ML_user_calibration_SPEC_VERSION 1
#define XR_ML_USER_CALIBRATION_EXTENSION_NAME "XR_ML_user_calibration"
@@ -6973,10 +7440,17 @@ XRAPI_ATTR XrResult XRAPI_CALL xrEnableUserCalibrationEventsML(
#endif /* !XR_NO_PROTOTYPES */
+// XR_YVR_controller_interaction is a preprocessor guard. Do not pass it to API calls.
#define XR_YVR_controller_interaction 1
#define XR_YVR_controller_interaction_SPEC_VERSION 1
#define XR_YVR_CONTROLLER_INTERACTION_EXTENSION_NAME "XR_YVR_controller_interaction"
+
+// XR_EXT_composition_layer_inverted_alpha is a preprocessor guard. Do not pass it to API calls.
+#define XR_EXT_composition_layer_inverted_alpha 1
+#define XR_EXT_composition_layer_inverted_alpha_SPEC_VERSION 1
+#define XR_EXT_COMPOSITION_LAYER_INVERTED_ALPHA_EXTENSION_NAME "XR_EXT_composition_layer_inverted_alpha"
+
#ifdef __cplusplus
}
#endif
diff --git a/thirdparty/openxr/include/openxr/openxr_loader_negotiation.h b/thirdparty/openxr/include/openxr/openxr_loader_negotiation.h
index 619b226760..19c18cc2a3 100644
--- a/thirdparty/openxr/include/openxr/openxr_loader_negotiation.h
+++ b/thirdparty/openxr/include/openxr/openxr_loader_negotiation.h
@@ -20,6 +20,7 @@ extern "C" {
+// XR_LOADER_VERSION_1_0 is a preprocessor guard. Do not pass it to API calls.
#define XR_LOADER_VERSION_1_0 1
#define XR_CURRENT_LOADER_API_LAYER_VERSION 1
diff --git a/thirdparty/openxr/include/openxr/openxr_platform.h b/thirdparty/openxr/include/openxr/openxr_platform.h
index 1f47428676..cbe1008906 100644
--- a/thirdparty/openxr/include/openxr/openxr_platform.h
+++ b/thirdparty/openxr/include/openxr/openxr_platform.h
@@ -21,6 +21,7 @@ extern "C" {
#ifdef XR_USE_PLATFORM_ANDROID
+// XR_KHR_android_thread_settings is a preprocessor guard. Do not pass it to API calls.
#define XR_KHR_android_thread_settings 1
#define XR_KHR_android_thread_settings_SPEC_VERSION 6
#define XR_KHR_ANDROID_THREAD_SETTINGS_EXTENSION_NAME "XR_KHR_android_thread_settings"
@@ -46,6 +47,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrSetAndroidApplicationThreadKHR(
#ifdef XR_USE_PLATFORM_ANDROID
+// XR_KHR_android_surface_swapchain is a preprocessor guard. Do not pass it to API calls.
#define XR_KHR_android_surface_swapchain 1
#define XR_KHR_android_surface_swapchain_SPEC_VERSION 4
#define XR_KHR_ANDROID_SURFACE_SWAPCHAIN_EXTENSION_NAME "XR_KHR_android_surface_swapchain"
@@ -64,6 +66,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrCreateSwapchainAndroidSurfaceKHR(
#ifdef XR_USE_PLATFORM_ANDROID
+// XR_KHR_android_create_instance is a preprocessor guard. Do not pass it to API calls.
#define XR_KHR_android_create_instance 1
#define XR_KHR_android_create_instance_SPEC_VERSION 3
#define XR_KHR_ANDROID_CREATE_INSTANCE_EXTENSION_NAME "XR_KHR_android_create_instance"
@@ -79,6 +82,7 @@ typedef struct XrInstanceCreateInfoAndroidKHR {
#ifdef XR_USE_GRAPHICS_API_VULKAN
+// XR_KHR_vulkan_swapchain_format_list is a preprocessor guard. Do not pass it to API calls.
#define XR_KHR_vulkan_swapchain_format_list 1
#define XR_KHR_vulkan_swapchain_format_list_SPEC_VERSION 4
#define XR_KHR_VULKAN_SWAPCHAIN_FORMAT_LIST_EXTENSION_NAME "XR_KHR_vulkan_swapchain_format_list"
@@ -93,6 +97,7 @@ typedef struct XrVulkanSwapchainFormatListCreateInfoKHR {
#ifdef XR_USE_GRAPHICS_API_OPENGL
+// XR_KHR_opengl_enable is a preprocessor guard. Do not pass it to API calls.
#define XR_KHR_opengl_enable 1
#define XR_KHR_opengl_enable_SPEC_VERSION 10
#define XR_KHR_OPENGL_ENABLE_EXTENSION_NAME "XR_KHR_opengl_enable"
@@ -169,6 +174,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrGetOpenGLGraphicsRequirementsKHR(
#ifdef XR_USE_GRAPHICS_API_OPENGL_ES
+// XR_KHR_opengl_es_enable is a preprocessor guard. Do not pass it to API calls.
#define XR_KHR_opengl_es_enable 1
#define XR_KHR_opengl_es_enable_SPEC_VERSION 8
#define XR_KHR_OPENGL_ES_ENABLE_EXTENSION_NAME "XR_KHR_opengl_es_enable"
@@ -210,6 +216,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrGetOpenGLESGraphicsRequirementsKHR(
#ifdef XR_USE_GRAPHICS_API_VULKAN
+// XR_KHR_vulkan_enable is a preprocessor guard. Do not pass it to API calls.
#define XR_KHR_vulkan_enable 1
#define XR_KHR_vulkan_enable_SPEC_VERSION 8
#define XR_KHR_VULKAN_ENABLE_EXTENSION_NAME "XR_KHR_vulkan_enable"
@@ -274,6 +281,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrGetVulkanGraphicsRequirementsKHR(
#ifdef XR_USE_GRAPHICS_API_D3D11
+// XR_KHR_D3D11_enable is a preprocessor guard. Do not pass it to API calls.
#define XR_KHR_D3D11_enable 1
#define XR_KHR_D3D11_enable_SPEC_VERSION 9
#define XR_KHR_D3D11_ENABLE_EXTENSION_NAME "XR_KHR_D3D11_enable"
@@ -311,6 +319,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrGetD3D11GraphicsRequirementsKHR(
#ifdef XR_USE_GRAPHICS_API_D3D12
+// XR_KHR_D3D12_enable is a preprocessor guard. Do not pass it to API calls.
#define XR_KHR_D3D12_enable 1
#define XR_KHR_D3D12_enable_SPEC_VERSION 9
#define XR_KHR_D3D12_ENABLE_EXTENSION_NAME "XR_KHR_D3D12_enable"
@@ -349,6 +358,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrGetD3D12GraphicsRequirementsKHR(
#ifdef XR_USE_PLATFORM_WIN32
+// XR_KHR_win32_convert_performance_counter_time is a preprocessor guard. Do not pass it to API calls.
#define XR_KHR_win32_convert_performance_counter_time 1
#define XR_KHR_win32_convert_performance_counter_time_SPEC_VERSION 1
#define XR_KHR_WIN32_CONVERT_PERFORMANCE_COUNTER_TIME_EXTENSION_NAME "XR_KHR_win32_convert_performance_counter_time"
@@ -372,6 +382,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrConvertTimeToWin32PerformanceCounterKHR(
#ifdef XR_USE_TIMESPEC
+// XR_KHR_convert_timespec_time is a preprocessor guard. Do not pass it to API calls.
#define XR_KHR_convert_timespec_time 1
#define XR_KHR_convert_timespec_time_SPEC_VERSION 1
#define XR_KHR_CONVERT_TIMESPEC_TIME_EXTENSION_NAME "XR_KHR_convert_timespec_time"
@@ -395,6 +406,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrConvertTimeToTimespecTimeKHR(
#ifdef XR_USE_PLATFORM_ANDROID
+// XR_KHR_loader_init_android is a preprocessor guard. Do not pass it to API calls.
#define XR_KHR_loader_init_android 1
#define XR_KHR_loader_init_android_SPEC_VERSION 1
#define XR_KHR_LOADER_INIT_ANDROID_EXTENSION_NAME "XR_KHR_loader_init_android"
@@ -409,6 +421,7 @@ typedef struct XrLoaderInitInfoAndroidKHR {
#ifdef XR_USE_GRAPHICS_API_VULKAN
+// XR_KHR_vulkan_enable2 is a preprocessor guard. Do not pass it to API calls.
#define XR_KHR_vulkan_enable2 1
#define XR_KHR_vulkan_enable2_SPEC_VERSION 2
#define XR_KHR_VULKAN_ENABLE2_EXTENSION_NAME "XR_KHR_vulkan_enable2"
@@ -488,6 +501,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrGetVulkanGraphicsRequirements2KHR(
#ifdef XR_USE_PLATFORM_EGL
+// XR_MNDX_egl_enable is a preprocessor guard. Do not pass it to API calls.
#define XR_MNDX_egl_enable 1
#define XR_MNDX_egl_enable_SPEC_VERSION 2
#define XR_MNDX_EGL_ENABLE_EXTENSION_NAME "XR_MNDX_egl_enable"
@@ -506,6 +520,7 @@ typedef struct XrGraphicsBindingEGLMNDX {
#ifdef XR_USE_PLATFORM_WIN32
+// XR_MSFT_perception_anchor_interop is a preprocessor guard. Do not pass it to API calls.
#define XR_MSFT_perception_anchor_interop 1
#define XR_MSFT_perception_anchor_interop_SPEC_VERSION 1
#define XR_MSFT_PERCEPTION_ANCHOR_INTEROP_EXTENSION_NAME "XR_MSFT_perception_anchor_interop"
@@ -529,6 +544,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrTryGetPerceptionAnchorFromSpatialAnchorMSFT(
#ifdef XR_USE_PLATFORM_WIN32
+// XR_MSFT_holographic_window_attachment is a preprocessor guard. Do not pass it to API calls.
#define XR_MSFT_holographic_window_attachment 1
#define XR_MSFT_holographic_window_attachment_SPEC_VERSION 1
#define XR_MSFT_HOLOGRAPHIC_WINDOW_ATTACHMENT_EXTENSION_NAME "XR_MSFT_holographic_window_attachment"
@@ -546,6 +562,7 @@ typedef struct XrHolographicWindowAttachmentMSFT {
#ifdef XR_USE_PLATFORM_ANDROID
+// XR_FB_android_surface_swapchain_create is a preprocessor guard. Do not pass it to API calls.
#define XR_FB_android_surface_swapchain_create 1
#define XR_FB_android_surface_swapchain_create_SPEC_VERSION 1
#define XR_FB_ANDROID_SURFACE_SWAPCHAIN_CREATE_EXTENSION_NAME "XR_FB_android_surface_swapchain_create"
@@ -568,6 +585,7 @@ typedef struct XrAndroidSurfaceSwapchainCreateInfoFB {
#ifdef XR_USE_PLATFORM_ML
+// XR_ML_compat is a preprocessor guard. Do not pass it to API calls.
#define XR_ML_compat 1
#define XR_ML_compat_SPEC_VERSION 1
#define XR_ML_COMPAT_EXTENSION_NAME "XR_ML_compat"
@@ -592,6 +610,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrCreateSpaceFromCoordinateFrameUIDML(
#ifdef XR_USE_PLATFORM_WIN32
+// XR_OCULUS_audio_device_guid is a preprocessor guard. Do not pass it to API calls.
#define XR_OCULUS_audio_device_guid 1
#define XR_OCULUS_audio_device_guid_SPEC_VERSION 1
#define XR_OCULUS_AUDIO_DEVICE_GUID_EXTENSION_NAME "XR_OCULUS_audio_device_guid"
@@ -614,6 +633,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrGetAudioInputDeviceGuidOculus(
#ifdef XR_USE_GRAPHICS_API_VULKAN
+// XR_FB_foveation_vulkan is a preprocessor guard. Do not pass it to API calls.
#define XR_FB_foveation_vulkan 1
#define XR_FB_foveation_vulkan_SPEC_VERSION 1
#define XR_FB_FOVEATION_VULKAN_EXTENSION_NAME "XR_FB_foveation_vulkan"
@@ -630,6 +650,7 @@ typedef struct XrSwapchainImageFoveationVulkanFB {
#ifdef XR_USE_PLATFORM_ANDROID
+// XR_FB_swapchain_update_state_android_surface is a preprocessor guard. Do not pass it to API calls.
#define XR_FB_swapchain_update_state_android_surface 1
#define XR_FB_swapchain_update_state_android_surface_SPEC_VERSION 1
#define XR_FB_SWAPCHAIN_UPDATE_STATE_ANDROID_SURFACE_EXTENSION_NAME "XR_FB_swapchain_update_state_android_surface"
@@ -646,6 +667,7 @@ typedef struct XrSwapchainStateAndroidSurfaceDimensionsFB {
#ifdef XR_USE_GRAPHICS_API_OPENGL_ES
+// XR_FB_swapchain_update_state_opengl_es is a preprocessor guard. Do not pass it to API calls.
#define XR_FB_swapchain_update_state_opengl_es 1
#define XR_FB_swapchain_update_state_opengl_es_SPEC_VERSION 1
#define XR_FB_SWAPCHAIN_UPDATE_STATE_OPENGL_ES_EXTENSION_NAME "XR_FB_swapchain_update_state_opengl_es"
@@ -670,6 +692,7 @@ typedef struct XrSwapchainStateSamplerOpenGLESFB {
#ifdef XR_USE_GRAPHICS_API_VULKAN
+// XR_FB_swapchain_update_state_vulkan is a preprocessor guard. Do not pass it to API calls.
#define XR_FB_swapchain_update_state_vulkan 1
#define XR_FB_swapchain_update_state_vulkan_SPEC_VERSION 1
#define XR_FB_SWAPCHAIN_UPDATE_STATE_VULKAN_EXTENSION_NAME "XR_FB_swapchain_update_state_vulkan"
@@ -695,6 +718,7 @@ typedef struct XrSwapchainStateSamplerVulkanFB {
#ifdef XR_USE_GRAPHICS_API_VULKAN
+// XR_META_vulkan_swapchain_create_info is a preprocessor guard. Do not pass it to API calls.
#define XR_META_vulkan_swapchain_create_info 1
#define XR_META_vulkan_swapchain_create_info_SPEC_VERSION 1
#define XR_META_VULKAN_SWAPCHAIN_CREATE_INFO_EXTENSION_NAME "XR_META_vulkan_swapchain_create_info"
diff --git a/thirdparty/openxr/include/openxr/openxr_reflection.h b/thirdparty/openxr/include/openxr/openxr_reflection.h
index b449c7099b..b9d45689a7 100644
--- a/thirdparty/openxr/include/openxr/openxr_reflection.h
+++ b/thirdparty/openxr/include/openxr/openxr_reflection.h
@@ -86,6 +86,8 @@ XR_ENUM_STR(XrResult);
_(XR_ERROR_LOCALIZED_NAME_INVALID, -49) \
_(XR_ERROR_GRAPHICS_REQUIREMENTS_CALL_MISSING, -50) \
_(XR_ERROR_RUNTIME_UNAVAILABLE, -51) \
+ _(XR_ERROR_EXTENSION_DEPENDENCY_NOT_ENABLED, -1000710001) \
+ _(XR_ERROR_PERMISSION_INSUFFICIENT, -1000710000) \
_(XR_ERROR_ANDROID_THREAD_SETTINGS_ID_INVALID_KHR, -1000003000) \
_(XR_ERROR_ANDROID_THREAD_SETTINGS_FAILURE_KHR, -1000003001) \
_(XR_ERROR_CREATE_SPATIAL_ANCHOR_FAILED_MSFT, -1000039001) \
@@ -135,10 +137,13 @@ XR_ENUM_STR(XrResult);
_(XR_ERROR_SPACE_NETWORK_REQUEST_FAILED_FB, -1000169003) \
_(XR_ERROR_SPACE_CLOUD_STORAGE_DISABLED_FB, -1000169004) \
_(XR_ERROR_PASSTHROUGH_COLOR_LUT_BUFFER_SIZE_MISMATCH_META, -1000266000) \
+ _(XR_ENVIRONMENT_DEPTH_NOT_AVAILABLE_META, 1000291000) \
_(XR_ERROR_HINT_ALREADY_SET_QCOM, -1000306000) \
_(XR_ERROR_NOT_AN_ANCHOR_HTC, -1000319000) \
_(XR_ERROR_SPACE_NOT_LOCATABLE_EXT, -1000429000) \
_(XR_ERROR_PLANE_DETECTION_PERMISSION_DENIED_EXT, -1000429001) \
+ _(XR_ERROR_FUTURE_PENDING_EXT, -1000469001) \
+ _(XR_ERROR_FUTURE_INVALID_EXT, -1000469002) \
_(XR_RESULT_MAX_ENUM, 0x7FFFFFFF)
#define XR_LIST_ENUM_XrStructureType(_) \
@@ -192,6 +197,9 @@ XR_ENUM_STR(XrResult);
_(XR_TYPE_ACTIONS_SYNC_INFO, 61) \
_(XR_TYPE_BOUND_SOURCES_FOR_ACTION_ENUMERATE_INFO, 62) \
_(XR_TYPE_INPUT_SOURCE_LOCALIZED_NAME_GET_INFO, 63) \
+ _(XR_TYPE_SPACES_LOCATE_INFO, 1000471000) \
+ _(XR_TYPE_SPACE_LOCATIONS, 1000471001) \
+ _(XR_TYPE_SPACE_VELOCITIES, 1000471002) \
_(XR_TYPE_COMPOSITION_LAYER_CUBE_KHR, 1000006000) \
_(XR_TYPE_INSTANCE_CREATE_INFO_ANDROID_KHR, 1000008000) \
_(XR_TYPE_COMPOSITION_LAYER_DEPTH_INFO_KHR, 1000010000) \
@@ -457,6 +465,14 @@ XR_ENUM_STR(XrResult);
_(XR_TYPE_FACE_TRACKER_CREATE_INFO2_FB, 1000287014) \
_(XR_TYPE_FACE_EXPRESSION_INFO2_FB, 1000287015) \
_(XR_TYPE_FACE_EXPRESSION_WEIGHTS2_FB, 1000287016) \
+ _(XR_TYPE_ENVIRONMENT_DEPTH_PROVIDER_CREATE_INFO_META, 1000291000) \
+ _(XR_TYPE_ENVIRONMENT_DEPTH_SWAPCHAIN_CREATE_INFO_META, 1000291001) \
+ _(XR_TYPE_ENVIRONMENT_DEPTH_SWAPCHAIN_STATE_META, 1000291002) \
+ _(XR_TYPE_ENVIRONMENT_DEPTH_IMAGE_ACQUIRE_INFO_META, 1000291003) \
+ _(XR_TYPE_ENVIRONMENT_DEPTH_IMAGE_VIEW_META, 1000291004) \
+ _(XR_TYPE_ENVIRONMENT_DEPTH_IMAGE_META, 1000291005) \
+ _(XR_TYPE_ENVIRONMENT_DEPTH_HAND_REMOVAL_SET_INFO_META, 1000291006) \
+ _(XR_TYPE_SYSTEM_ENVIRONMENT_DEPTH_PROPERTIES_META, 1000291007) \
_(XR_TYPE_PASSTHROUGH_CREATE_INFO_HTC, 1000317001) \
_(XR_TYPE_PASSTHROUGH_COLOR_HTC, 1000317002) \
_(XR_TYPE_PASSTHROUGH_MESH_TRANSFORM_INFO_HTC, 1000317003) \
@@ -478,6 +494,10 @@ XR_ENUM_STR(XrResult);
_(XR_TYPE_PLANE_DETECTOR_LOCATION_EXT, 1000429005) \
_(XR_TYPE_PLANE_DETECTOR_POLYGON_BUFFER_EXT, 1000429006) \
_(XR_TYPE_SYSTEM_PLANE_DETECTION_PROPERTIES_EXT, 1000429007) \
+ _(XR_TYPE_FUTURE_CANCEL_INFO_EXT, 1000469000) \
+ _(XR_TYPE_FUTURE_POLL_INFO_EXT, 1000469001) \
+ _(XR_TYPE_FUTURE_COMPLETION_EXT, 1000469002) \
+ _(XR_TYPE_FUTURE_POLL_RESULT_EXT, 1000469003) \
_(XR_TYPE_EVENT_DATA_USER_PRESENCE_CHANGED_EXT, 1000470000) \
_(XR_TYPE_SYSTEM_USER_PRESENCE_PROPERTIES_EXT, 1000470001) \
_(XR_STRUCTURE_TYPE_MAX_ENUM, 0x7FFFFFFF)
@@ -490,7 +510,7 @@ XR_ENUM_STR(XrResult);
#define XR_LIST_ENUM_XrViewConfigurationType(_) \
_(XR_VIEW_CONFIGURATION_TYPE_PRIMARY_MONO, 1) \
_(XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO, 2) \
- _(XR_VIEW_CONFIGURATION_TYPE_PRIMARY_QUAD_VARJO, 1000037000) \
+ _(XR_VIEW_CONFIGURATION_TYPE_PRIMARY_STEREO_WITH_FOVEATED_INSET, 1000037000) \
_(XR_VIEW_CONFIGURATION_TYPE_SECONDARY_MONO_FIRST_PERSON_OBSERVER_MSFT, 1000054000) \
_(XR_VIEW_CONFIGURATION_TYPE_MAX_ENUM, 0x7FFFFFFF)
@@ -504,10 +524,10 @@ XR_ENUM_STR(XrResult);
_(XR_REFERENCE_SPACE_TYPE_VIEW, 1) \
_(XR_REFERENCE_SPACE_TYPE_LOCAL, 2) \
_(XR_REFERENCE_SPACE_TYPE_STAGE, 3) \
+ _(XR_REFERENCE_SPACE_TYPE_LOCAL_FLOOR, 1000426000) \
_(XR_REFERENCE_SPACE_TYPE_UNBOUNDED_MSFT, 1000038000) \
_(XR_REFERENCE_SPACE_TYPE_COMBINED_EYE_VARJO, 1000121000) \
_(XR_REFERENCE_SPACE_TYPE_LOCALIZATION_MAP_ML, 1000139000) \
- _(XR_REFERENCE_SPACE_TYPE_LOCAL_FLOOR_EXT, 1000426000) \
_(XR_REFERENCE_SPACE_TYPE_MAX_ENUM, 0x7FFFFFFF)
#define XR_LIST_ENUM_XrActionType(_) \
@@ -566,6 +586,8 @@ XR_ENUM_STR(XrResult);
_(XR_OBJECT_TYPE_SPACE_USER_FB, 1000241000) \
_(XR_OBJECT_TYPE_PASSTHROUGH_COLOR_LUT_META, 1000266000) \
_(XR_OBJECT_TYPE_FACE_TRACKER2_FB, 1000287012) \
+ _(XR_OBJECT_TYPE_ENVIRONMENT_DEPTH_PROVIDER_META, 1000291000) \
+ _(XR_OBJECT_TYPE_ENVIRONMENT_DEPTH_SWAPCHAIN_META, 1000291001) \
_(XR_OBJECT_TYPE_PASSTHROUGH_HTC, 1000317000) \
_(XR_OBJECT_TYPE_PLANE_DETECTOR_EXT, 1000429000) \
_(XR_OBJECT_TYPE_MAX_ENUM, 0x7FFFFFFF)
@@ -1377,6 +1399,11 @@ XR_ENUM_STR(XrResult);
_(XR_PLANE_DETECTION_STATE_FATAL_EXT, 4) \
_(XR_PLANE_DETECTION_STATE_MAX_ENUM_EXT, 0x7FFFFFFF)
+#define XR_LIST_ENUM_XrFutureStateEXT(_) \
+ _(XR_FUTURE_STATE_PENDING_EXT, 1) \
+ _(XR_FUTURE_STATE_READY_EXT, 2) \
+ _(XR_FUTURE_STATE_MAX_ENUM_EXT, 0x7FFFFFFF)
+
#define XR_LIST_ENUM_XrHeadsetFitStatusML(_) \
_(XR_HEADSET_FIT_STATUS_UNKNOWN_ML, 0) \
_(XR_HEADSET_FIT_STATUS_NOT_WORN_ML, 1) \
@@ -1424,6 +1451,7 @@ XR_ENUM_STR(XrResult);
_(XR_COMPOSITION_LAYER_CORRECT_CHROMATIC_ABERRATION_BIT, 0x00000001) \
_(XR_COMPOSITION_LAYER_BLEND_TEXTURE_SOURCE_ALPHA_BIT, 0x00000002) \
_(XR_COMPOSITION_LAYER_UNPREMULTIPLIED_ALPHA_BIT, 0x00000004) \
+ _(XR_COMPOSITION_LAYER_INVERTED_ALPHA_BIT_EXT, 0x00000008) \
#define XR_LIST_BITS_XrViewStateFlags(_) \
_(XR_VIEW_STATE_ORIENTATION_VALID_BIT, 0x00000001) \
@@ -1573,6 +1601,10 @@ XR_ENUM_STR(XrResult);
_(XR_PERFORMANCE_METRICS_COUNTER_UINT_VALUE_VALID_BIT_META, 0x00000002) \
_(XR_PERFORMANCE_METRICS_COUNTER_FLOAT_VALUE_VALID_BIT_META, 0x00000004) \
+#define XR_LIST_BITS_XrEnvironmentDepthProviderCreateFlagsMETA(_)
+
+#define XR_LIST_BITS_XrEnvironmentDepthSwapchainCreateFlagsMETA(_)
+
#define XR_LIST_BITS_XrFoveationDynamicFlagsHTC(_) \
_(XR_FOVEATION_DYNAMIC_LEVEL_ENABLED_BIT_HTC, 0x00000001) \
_(XR_FOVEATION_DYNAMIC_CLEAR_FOV_ENABLED_BIT_HTC, 0x00000002) \
@@ -2152,6 +2184,73 @@ XR_ENUM_STR(XrResult);
_(getInstanceProcAddr) \
_(createApiLayerInstance) \
+/// Calls your macro with the name of each member of XrColor3f, in order.
+#define XR_LIST_STRUCT_XrColor3f(_) \
+ _(r) \
+ _(g) \
+ _(b) \
+
+/// Calls your macro with the name of each member of XrExtent3Df, in order.
+#define XR_LIST_STRUCT_XrExtent3Df(_) \
+ _(width) \
+ _(height) \
+ _(depth) \
+
+/// Calls your macro with the name of each member of XrSpheref, in order.
+#define XR_LIST_STRUCT_XrSpheref(_) \
+ _(center) \
+ _(radius) \
+
+/// Calls your macro with the name of each member of XrBoxf, in order.
+#define XR_LIST_STRUCT_XrBoxf(_) \
+ _(center) \
+ _(extents) \
+
+/// Calls your macro with the name of each member of XrFrustumf, in order.
+#define XR_LIST_STRUCT_XrFrustumf(_) \
+ _(pose) \
+ _(fov) \
+ _(nearZ) \
+ _(farZ) \
+
+/// Calls your macro with the name of each member of XrUuid, in order.
+#define XR_LIST_STRUCT_XrUuid(_) \
+ _(data) \
+
+/// Calls your macro with the name of each member of XrSpacesLocateInfo, in order.
+#define XR_LIST_STRUCT_XrSpacesLocateInfo(_) \
+ _(type) \
+ _(next) \
+ _(baseSpace) \
+ _(time) \
+ _(spaceCount) \
+ _(spaces) \
+
+/// Calls your macro with the name of each member of XrSpaceLocationData, in order.
+#define XR_LIST_STRUCT_XrSpaceLocationData(_) \
+ _(locationFlags) \
+ _(pose) \
+
+/// Calls your macro with the name of each member of XrSpaceLocations, in order.
+#define XR_LIST_STRUCT_XrSpaceLocations(_) \
+ _(type) \
+ _(next) \
+ _(locationCount) \
+ _(locations) \
+
+/// Calls your macro with the name of each member of XrSpaceVelocityData, in order.
+#define XR_LIST_STRUCT_XrSpaceVelocityData(_) \
+ _(velocityFlags) \
+ _(linearVelocity) \
+ _(angularVelocity) \
+
+/// Calls your macro with the name of each member of XrSpaceVelocities, in order.
+#define XR_LIST_STRUCT_XrSpaceVelocities(_) \
+ _(type) \
+ _(next) \
+ _(velocityCount) \
+ _(velocities) \
+
/// Calls your macro with the name of each member of XrCompositionLayerCubeKHR, in order.
#define XR_LIST_STRUCT_XrCompositionLayerCubeKHR(_) \
_(type) \
@@ -3231,10 +3330,6 @@ XR_ENUM_STR(XrResult);
_(enabled) \
_(changePending) \
-/// Calls your macro with the name of each member of XrUuidEXT, in order.
-#define XR_LIST_STRUCT_XrUuidEXT(_) \
- _(data) \
-
/// Calls your macro with the name of each member of XrEventDataSpatialAnchorCreateCompleteFB, in order.
#define XR_LIST_STRUCT_XrEventDataSpatialAnchorCreateCompleteFB(_) \
_(type) \
@@ -3857,12 +3952,6 @@ XR_ENUM_STR(XrResult);
_(amplitudeCount) \
_(amplitudes) \
-/// Calls your macro with the name of each member of XrExtent3DfFB, in order.
-#define XR_LIST_STRUCT_XrExtent3DfFB(_) \
- _(width) \
- _(height) \
- _(depth) \
-
/// Calls your macro with the name of each member of XrOffset3DfFB, in order.
#define XR_LIST_STRUCT_XrOffset3DfFB(_) \
_(x) \
@@ -4348,6 +4437,61 @@ XR_ENUM_STR(XrResult);
_(dataSource) \
_(time) \
+/// Calls your macro with the name of each member of XrEnvironmentDepthProviderCreateInfoMETA, in order.
+#define XR_LIST_STRUCT_XrEnvironmentDepthProviderCreateInfoMETA(_) \
+ _(type) \
+ _(next) \
+ _(createFlags) \
+
+/// Calls your macro with the name of each member of XrEnvironmentDepthSwapchainCreateInfoMETA, in order.
+#define XR_LIST_STRUCT_XrEnvironmentDepthSwapchainCreateInfoMETA(_) \
+ _(type) \
+ _(next) \
+ _(createFlags) \
+
+/// Calls your macro with the name of each member of XrEnvironmentDepthSwapchainStateMETA, in order.
+#define XR_LIST_STRUCT_XrEnvironmentDepthSwapchainStateMETA(_) \
+ _(type) \
+ _(next) \
+ _(width) \
+ _(height) \
+
+/// Calls your macro with the name of each member of XrEnvironmentDepthImageAcquireInfoMETA, in order.
+#define XR_LIST_STRUCT_XrEnvironmentDepthImageAcquireInfoMETA(_) \
+ _(type) \
+ _(next) \
+ _(space) \
+ _(displayTime) \
+
+/// Calls your macro with the name of each member of XrEnvironmentDepthImageViewMETA, in order.
+#define XR_LIST_STRUCT_XrEnvironmentDepthImageViewMETA(_) \
+ _(type) \
+ _(next) \
+ _(fov) \
+ _(pose) \
+
+/// Calls your macro with the name of each member of XrEnvironmentDepthImageMETA, in order.
+#define XR_LIST_STRUCT_XrEnvironmentDepthImageMETA(_) \
+ _(type) \
+ _(next) \
+ _(swapchainIndex) \
+ _(nearZ) \
+ _(farZ) \
+ _(views) \
+
+/// Calls your macro with the name of each member of XrEnvironmentDepthHandRemovalSetInfoMETA, in order.
+#define XR_LIST_STRUCT_XrEnvironmentDepthHandRemovalSetInfoMETA(_) \
+ _(type) \
+ _(next) \
+ _(enabled) \
+
+/// Calls your macro with the name of each member of XrSystemEnvironmentDepthPropertiesMETA, in order.
+#define XR_LIST_STRUCT_XrSystemEnvironmentDepthPropertiesMETA(_) \
+ _(type) \
+ _(next) \
+ _(supportsEnvironmentDepth) \
+ _(supportsHandRemoval) \
+
/// Calls your macro with the name of each member of XrPassthroughCreateInfoHTC, in order.
#define XR_LIST_STRUCT_XrPassthroughCreateInfoHTC(_) \
_(type) \
@@ -4483,12 +4627,6 @@ XR_ENUM_STR(XrResult);
_(next) \
_(flags) \
-/// Calls your macro with the name of each member of XrExtent3DfEXT, in order.
-#define XR_LIST_STRUCT_XrExtent3DfEXT(_) \
- _(width) \
- _(height) \
- _(depth) \
-
/// Calls your macro with the name of each member of XrPlaneDetectorBeginInfoEXT, in order.
#define XR_LIST_STRUCT_XrPlaneDetectorBeginInfoEXT(_) \
_(type) \
@@ -4539,6 +4677,36 @@ XR_ENUM_STR(XrResult);
_(vertexCountOutput) \
_(vertices) \
+/// Calls your macro with the name of each member of XrFutureCancelInfoEXT, in order.
+#define XR_LIST_STRUCT_XrFutureCancelInfoEXT(_) \
+ _(type) \
+ _(next) \
+ _(future) \
+
+/// Calls your macro with the name of each member of XrFuturePollInfoEXT, in order.
+#define XR_LIST_STRUCT_XrFuturePollInfoEXT(_) \
+ _(type) \
+ _(next) \
+ _(future) \
+
+/// Calls your macro with the name of each member of XrFutureCompletionBaseHeaderEXT, in order.
+#define XR_LIST_STRUCT_XrFutureCompletionBaseHeaderEXT(_) \
+ _(type) \
+ _(next) \
+ _(futureResult) \
+
+/// Calls your macro with the name of each member of XrFutureCompletionEXT, in order.
+#define XR_LIST_STRUCT_XrFutureCompletionEXT(_) \
+ _(type) \
+ _(next) \
+ _(futureResult) \
+
+/// Calls your macro with the name of each member of XrFuturePollResultEXT, in order.
+#define XR_LIST_STRUCT_XrFuturePollResultEXT(_) \
+ _(type) \
+ _(next) \
+ _(state) \
+
/// Calls your macro with the name of each member of XrEventDataUserPresenceChangedEXT, in order.
#define XR_LIST_STRUCT_XrEventDataUserPresenceChangedEXT(_) \
_(type) \
@@ -4644,6 +4812,9 @@ XR_ENUM_STR(XrResult);
_(XrEventDataReferenceSpaceChangePending, XR_TYPE_EVENT_DATA_REFERENCE_SPACE_CHANGE_PENDING) \
_(XrEventDataInteractionProfileChanged, XR_TYPE_EVENT_DATA_INTERACTION_PROFILE_CHANGED) \
_(XrHapticVibration, XR_TYPE_HAPTIC_VIBRATION) \
+ _(XrSpacesLocateInfo, XR_TYPE_SPACES_LOCATE_INFO) \
+ _(XrSpaceLocations, XR_TYPE_SPACE_LOCATIONS) \
+ _(XrSpaceVelocities, XR_TYPE_SPACE_VELOCITIES) \
_(XrCompositionLayerCubeKHR, XR_TYPE_COMPOSITION_LAYER_CUBE_KHR) \
_(XrCompositionLayerDepthInfoKHR, XR_TYPE_COMPOSITION_LAYER_DEPTH_INFO_KHR) \
_(XrCompositionLayerCylinderKHR, XR_TYPE_COMPOSITION_LAYER_CYLINDER_KHR) \
@@ -4873,6 +5044,14 @@ XR_ENUM_STR(XrResult);
_(XrFaceTrackerCreateInfo2FB, XR_TYPE_FACE_TRACKER_CREATE_INFO2_FB) \
_(XrFaceExpressionInfo2FB, XR_TYPE_FACE_EXPRESSION_INFO2_FB) \
_(XrFaceExpressionWeights2FB, XR_TYPE_FACE_EXPRESSION_WEIGHTS2_FB) \
+ _(XrEnvironmentDepthProviderCreateInfoMETA, XR_TYPE_ENVIRONMENT_DEPTH_PROVIDER_CREATE_INFO_META) \
+ _(XrEnvironmentDepthSwapchainCreateInfoMETA, XR_TYPE_ENVIRONMENT_DEPTH_SWAPCHAIN_CREATE_INFO_META) \
+ _(XrEnvironmentDepthSwapchainStateMETA, XR_TYPE_ENVIRONMENT_DEPTH_SWAPCHAIN_STATE_META) \
+ _(XrEnvironmentDepthImageAcquireInfoMETA, XR_TYPE_ENVIRONMENT_DEPTH_IMAGE_ACQUIRE_INFO_META) \
+ _(XrEnvironmentDepthImageViewMETA, XR_TYPE_ENVIRONMENT_DEPTH_IMAGE_VIEW_META) \
+ _(XrEnvironmentDepthImageMETA, XR_TYPE_ENVIRONMENT_DEPTH_IMAGE_META) \
+ _(XrEnvironmentDepthHandRemovalSetInfoMETA, XR_TYPE_ENVIRONMENT_DEPTH_HAND_REMOVAL_SET_INFO_META) \
+ _(XrSystemEnvironmentDepthPropertiesMETA, XR_TYPE_SYSTEM_ENVIRONMENT_DEPTH_PROPERTIES_META) \
_(XrPassthroughCreateInfoHTC, XR_TYPE_PASSTHROUGH_CREATE_INFO_HTC) \
_(XrPassthroughColorHTC, XR_TYPE_PASSTHROUGH_COLOR_HTC) \
_(XrPassthroughMeshTransformInfoHTC, XR_TYPE_PASSTHROUGH_MESH_TRANSFORM_INFO_HTC) \
@@ -4894,6 +5073,10 @@ XR_ENUM_STR(XrResult);
_(XrPlaneDetectorLocationEXT, XR_TYPE_PLANE_DETECTOR_LOCATION_EXT) \
_(XrPlaneDetectorLocationsEXT, XR_TYPE_PLANE_DETECTOR_LOCATIONS_EXT) \
_(XrPlaneDetectorPolygonBufferEXT, XR_TYPE_PLANE_DETECTOR_POLYGON_BUFFER_EXT) \
+ _(XrFutureCancelInfoEXT, XR_TYPE_FUTURE_CANCEL_INFO_EXT) \
+ _(XrFuturePollInfoEXT, XR_TYPE_FUTURE_POLL_INFO_EXT) \
+ _(XrFutureCompletionEXT, XR_TYPE_FUTURE_COMPLETION_EXT) \
+ _(XrFuturePollResultEXT, XR_TYPE_FUTURE_POLL_RESULT_EXT) \
_(XrEventDataUserPresenceChangedEXT, XR_TYPE_EVENT_DATA_USER_PRESENCE_CHANGED_EXT) \
_(XrSystemUserPresencePropertiesEXT, XR_TYPE_SYSTEM_USER_PRESENCE_PROPERTIES_EXT) \
_(XrEventDataHeadsetFitChangedML, XR_TYPE_EVENT_DATA_HEADSET_FIT_CHANGED_ML) \
@@ -5200,6 +5383,7 @@ XR_ENUM_STR(XrResult);
_(XR_META_automatic_layer_filter, 272) \
_(XR_META_touch_controller_plus, 280) \
_(XR_FB_face_tracking2, 288) \
+ _(XR_META_environment_depth, 292) \
_(XR_EXT_uuid, 300) \
_(XR_EXT_hand_interaction, 303) \
_(XR_QCOM_tracking_optimization_settings, 307) \
@@ -5213,9 +5397,13 @@ XR_ENUM_STR(XrResult);
_(XR_EXT_hand_tracking_data_source, 429) \
_(XR_EXT_plane_detection, 430) \
_(XR_OPPO_controller_interaction, 454) \
+ _(XR_EXT_future, 470) \
_(XR_EXT_user_presence, 471) \
+ _(XR_KHR_locate_spaces, 472) \
_(XR_ML_user_calibration, 473) \
_(XR_YVR_controller_interaction, 498) \
+ _(XR_EXT_composition_layer_inverted_alpha, 555) \
+ _(XR_KHR_maintenance1, 711) \
@@ -5291,6 +5479,14 @@ XR_ENUM_STR(XrResult);
_(NegotiateLoaderApiLayerInterface, LOADER_VERSION_1_0) \
+/// For every function defined by XR_VERSION_1_1 in this version of the spec,
+/// calls your macro with the function name and extension name.
+/// Trims the leading `xr` from the function name and the leading `XR_` from the feature name,
+/// because it is easy to add back but impossible to remove with the preprocessor.
+#define XR_LIST_FUNCTIONS_XR_VERSION_1_1(_) \
+ _(LocateSpaces, VERSION_1_1) \
+
+
/// For every function defined by XR_KHR_android_thread_settings in this version of the spec,
/// calls your macro with the function name and extension name.
/// Trims the leading `xr` from the function name and the leading `XR_` from the feature name,
@@ -5973,6 +6169,23 @@ XR_ENUM_STR(XrResult);
_(GetFaceExpressionWeights2FB, FB_face_tracking2) \
+/// For every function defined by XR_META_environment_depth in this version of the spec,
+/// calls your macro with the function name and extension name.
+/// Trims the leading `xr` from the function name and the leading `XR_` from the feature name,
+/// because it is easy to add back but impossible to remove with the preprocessor.
+#define XR_LIST_FUNCTIONS_XR_META_environment_depth(_) \
+ _(CreateEnvironmentDepthProviderMETA, META_environment_depth) \
+ _(DestroyEnvironmentDepthProviderMETA, META_environment_depth) \
+ _(StartEnvironmentDepthProviderMETA, META_environment_depth) \
+ _(StopEnvironmentDepthProviderMETA, META_environment_depth) \
+ _(CreateEnvironmentDepthSwapchainMETA, META_environment_depth) \
+ _(DestroyEnvironmentDepthSwapchainMETA, META_environment_depth) \
+ _(EnumerateEnvironmentDepthSwapchainImagesMETA, META_environment_depth) \
+ _(GetEnvironmentDepthSwapchainStateMETA, META_environment_depth) \
+ _(AcquireEnvironmentDepthImageMETA, META_environment_depth) \
+ _(SetEnvironmentDepthHandRemovalMETA, META_environment_depth) \
+
+
/// For every function defined by XR_QCOM_tracking_optimization_settings in this version of the spec,
/// calls your macro with the function name and extension name.
/// Trims the leading `xr` from the function name and the leading `XR_` from the feature name,
@@ -6028,6 +6241,15 @@ XR_ENUM_STR(XrResult);
_(GetPlanePolygonBufferEXT, EXT_plane_detection) \
+/// For every function defined by XR_EXT_future in this version of the spec,
+/// calls your macro with the function name and extension name.
+/// Trims the leading `xr` from the function name and the leading `XR_` from the feature name,
+/// because it is easy to add back but impossible to remove with the preprocessor.
+#define XR_LIST_FUNCTIONS_XR_EXT_future(_) \
+ _(PollFutureEXT, EXT_future) \
+ _(CancelFutureEXT, EXT_future) \
+
+
/// For every function defined by XR_ML_user_calibration in this version of the spec,
/// calls your macro with the function name and extension name.
/// Trims the leading `xr` from the function name and the leading `XR_` from the feature name,
diff --git a/thirdparty/openxr/include/openxr/openxr_reflection_parent_structs.h b/thirdparty/openxr/include/openxr/openxr_reflection_parent_structs.h
index 4241682e0b..50466abc1b 100644
--- a/thirdparty/openxr/include/openxr/openxr_reflection_parent_structs.h
+++ b/thirdparty/openxr/include/openxr/openxr_reflection_parent_structs.h
@@ -32,6 +32,7 @@ This file contains expansion macros (X Macros) for OpenXR structures that have a
_avail(XrCompositionLayerCylinderKHR, XR_TYPE_COMPOSITION_LAYER_CYLINDER_KHR) \
_avail(XrCompositionLayerEquirectKHR, XR_TYPE_COMPOSITION_LAYER_EQUIRECT_KHR) \
_avail(XrCompositionLayerEquirect2KHR, XR_TYPE_COMPOSITION_LAYER_EQUIRECT2_KHR) \
+ _avail(XrCompositionLayerPassthroughFB, XR_TYPE_COMPOSITION_LAYER_PASSTHROUGH_FB) \
_avail(XrCompositionLayerPassthroughHTC, XR_TYPE_COMPOSITION_LAYER_PASSTHROUGH_HTC) \
@@ -264,5 +265,18 @@ This file contains expansion macros (X Macros) for OpenXR structures that have a
+/// Like XR_LIST_ALL_STRUCTURE_TYPES, but only includes types whose parent struct type is XrFutureCompletionBaseHeaderEXT
+#define XR_LIST_ALL_CHILD_STRUCTURE_TYPES_XrFutureCompletionBaseHeaderEXT(_avail, _unavail) \
+ _impl_XR_LIST_ALL_CHILD_STRUCTURE_TYPES_XrFutureCompletionBaseHeaderEXT_CORE(_avail, _unavail) \
+
+
+// Implementation detail of XR_LIST_ALL_CHILD_STRUCTURE_TYPES_XrFutureCompletionBaseHeaderEXT()
+#define _impl_XR_LIST_ALL_CHILD_STRUCTURE_TYPES_XrFutureCompletionBaseHeaderEXT_CORE(_avail, _unavail) \
+ _avail(XrFutureCompletionEXT, XR_TYPE_FUTURE_COMPLETION_EXT) \
+
+
+
+
+
#endif
diff --git a/thirdparty/openxr/include/openxr/openxr_reflection_structs.h b/thirdparty/openxr/include/openxr/openxr_reflection_structs.h
index f973539cbb..e87e55b560 100644
--- a/thirdparty/openxr/include/openxr/openxr_reflection_structs.h
+++ b/thirdparty/openxr/include/openxr/openxr_reflection_structs.h
@@ -92,6 +92,9 @@ This file contains expansion macros (X Macros) for OpenXR structures.
_avail(XrEventDataReferenceSpaceChangePending, XR_TYPE_EVENT_DATA_REFERENCE_SPACE_CHANGE_PENDING) \
_avail(XrEventDataInteractionProfileChanged, XR_TYPE_EVENT_DATA_INTERACTION_PROFILE_CHANGED) \
_avail(XrHapticVibration, XR_TYPE_HAPTIC_VIBRATION) \
+ _avail(XrSpacesLocateInfo, XR_TYPE_SPACES_LOCATE_INFO) \
+ _avail(XrSpaceLocations, XR_TYPE_SPACE_LOCATIONS) \
+ _avail(XrSpaceVelocities, XR_TYPE_SPACE_VELOCITIES) \
_avail(XrCompositionLayerCubeKHR, XR_TYPE_COMPOSITION_LAYER_CUBE_KHR) \
_avail(XrCompositionLayerDepthInfoKHR, XR_TYPE_COMPOSITION_LAYER_DEPTH_INFO_KHR) \
_avail(XrCompositionLayerCylinderKHR, XR_TYPE_COMPOSITION_LAYER_CYLINDER_KHR) \
@@ -321,6 +324,14 @@ This file contains expansion macros (X Macros) for OpenXR structures.
_avail(XrFaceTrackerCreateInfo2FB, XR_TYPE_FACE_TRACKER_CREATE_INFO2_FB) \
_avail(XrFaceExpressionInfo2FB, XR_TYPE_FACE_EXPRESSION_INFO2_FB) \
_avail(XrFaceExpressionWeights2FB, XR_TYPE_FACE_EXPRESSION_WEIGHTS2_FB) \
+ _avail(XrEnvironmentDepthProviderCreateInfoMETA, XR_TYPE_ENVIRONMENT_DEPTH_PROVIDER_CREATE_INFO_META) \
+ _avail(XrEnvironmentDepthSwapchainCreateInfoMETA, XR_TYPE_ENVIRONMENT_DEPTH_SWAPCHAIN_CREATE_INFO_META) \
+ _avail(XrEnvironmentDepthSwapchainStateMETA, XR_TYPE_ENVIRONMENT_DEPTH_SWAPCHAIN_STATE_META) \
+ _avail(XrEnvironmentDepthImageAcquireInfoMETA, XR_TYPE_ENVIRONMENT_DEPTH_IMAGE_ACQUIRE_INFO_META) \
+ _avail(XrEnvironmentDepthImageViewMETA, XR_TYPE_ENVIRONMENT_DEPTH_IMAGE_VIEW_META) \
+ _avail(XrEnvironmentDepthImageMETA, XR_TYPE_ENVIRONMENT_DEPTH_IMAGE_META) \
+ _avail(XrEnvironmentDepthHandRemovalSetInfoMETA, XR_TYPE_ENVIRONMENT_DEPTH_HAND_REMOVAL_SET_INFO_META) \
+ _avail(XrSystemEnvironmentDepthPropertiesMETA, XR_TYPE_SYSTEM_ENVIRONMENT_DEPTH_PROPERTIES_META) \
_avail(XrPassthroughCreateInfoHTC, XR_TYPE_PASSTHROUGH_CREATE_INFO_HTC) \
_avail(XrPassthroughColorHTC, XR_TYPE_PASSTHROUGH_COLOR_HTC) \
_avail(XrPassthroughMeshTransformInfoHTC, XR_TYPE_PASSTHROUGH_MESH_TRANSFORM_INFO_HTC) \
@@ -342,6 +353,10 @@ This file contains expansion macros (X Macros) for OpenXR structures.
_avail(XrPlaneDetectorLocationEXT, XR_TYPE_PLANE_DETECTOR_LOCATION_EXT) \
_avail(XrPlaneDetectorLocationsEXT, XR_TYPE_PLANE_DETECTOR_LOCATIONS_EXT) \
_avail(XrPlaneDetectorPolygonBufferEXT, XR_TYPE_PLANE_DETECTOR_POLYGON_BUFFER_EXT) \
+ _avail(XrFutureCancelInfoEXT, XR_TYPE_FUTURE_CANCEL_INFO_EXT) \
+ _avail(XrFuturePollInfoEXT, XR_TYPE_FUTURE_POLL_INFO_EXT) \
+ _avail(XrFutureCompletionEXT, XR_TYPE_FUTURE_COMPLETION_EXT) \
+ _avail(XrFuturePollResultEXT, XR_TYPE_FUTURE_POLL_RESULT_EXT) \
_avail(XrEventDataUserPresenceChangedEXT, XR_TYPE_EVENT_DATA_USER_PRESENCE_CHANGED_EXT) \
_avail(XrSystemUserPresencePropertiesEXT, XR_TYPE_SYSTEM_USER_PRESENCE_PROPERTIES_EXT) \
_avail(XrEventDataHeadsetFitChangedML, XR_TYPE_EVENT_DATA_HEADSET_FIT_CHANGED_ML) \
diff --git a/thirdparty/openxr/src/common/platform_utils.hpp b/thirdparty/openxr/src/common/platform_utils.hpp
index 35369a1477..d047c17d9d 100644
--- a/thirdparty/openxr/src/common/platform_utils.hpp
+++ b/thirdparty/openxr/src/common/platform_utils.hpp
@@ -47,7 +47,7 @@
#define XR_ARCH_ABI "aarch64"
#elif (defined(__ARM_ARCH) && __ARM_ARCH >= 7 && (defined(__ARM_PCS_VFP) || defined(__ANDROID__))) || defined(_M_ARM)
#define XR_ARCH_ABI "armv7a-vfp"
-#elif defined(__ARM_ARCH_5TE__)
+#elif defined(__ARM_ARCH_5TE__) || (defined(__ARM_ARCH) && __ARM_ARCH > 5)
#define XR_ARCH_ABI "armv5te"
#elif defined(__mips64)
#define XR_ARCH_ABI "mips64"
@@ -91,8 +91,6 @@ namespace detail {
static inline char* ImplGetEnv(const char* name) { return getenv(name); }
-static inline int ImplSetEnv(const char* name, const char* value, int overwrite) { return setenv(name, value, overwrite); }
-
static inline char* ImplGetSecureEnv(const char* name) {
#ifdef HAVE_SECURE_GETENV
return secure_getenv(name);
@@ -162,12 +160,6 @@ static inline std::string PlatformUtilsGetSecureEnv(const char* name) {
static inline bool PlatformUtilsGetEnvSet(const char* name) { return detail::ImplGetEnv(name) != nullptr; }
-static inline bool PlatformUtilsSetEnv(const char* name, const char* value) {
- const int shouldOverwrite = 1;
- int result = detail::ImplSetEnv(name, value, shouldOverwrite);
- return (result == 0);
-}
-
#elif defined(XR_OS_APPLE)
static inline std::string PlatformUtilsGetEnv(const char* name) {
@@ -188,12 +180,6 @@ static inline std::string PlatformUtilsGetSecureEnv(const char* name) {
static inline bool PlatformUtilsGetEnvSet(const char* name) { return detail::ImplGetEnv(name) != nullptr; }
-static inline bool PlatformUtilsSetEnv(const char* name, const char* value) {
- const int shouldOverwrite = 1;
- int result = detail::ImplSetEnv(name, value, shouldOverwrite);
- return (result == 0);
-}
-
static inline bool PlatformGetGlobalRuntimeFileName(uint16_t major_version, std::string& file_name) {
return detail::ImplTryRuntimeFilename("/usr/local/share/openxr/", major_version, file_name);
}
@@ -337,19 +323,10 @@ static inline std::string PlatformUtilsGetSecureEnv(const char* name) {
return envValue;
}
-// Sets an environment variable via UTF8 strings.
-// The name is case-sensitive.
-// Overwrites the variable if it already exists.
-// Returns true if it could be set.
-static inline bool PlatformUtilsSetEnv(const char* name, const char* value) {
- const std::wstring wname = utf8_to_wide(name);
- const std::wstring wvalue = utf8_to_wide(value);
- BOOL result = ::SetEnvironmentVariableW(wname.c_str(), wvalue.c_str());
- return (result != 0);
-}
-
#elif defined(XR_OS_ANDROID)
+#include <sys/system_properties.h>
+
static inline bool PlatformUtilsGetEnvSet(const char* /* name */) {
// Stub func
return false;
@@ -365,11 +342,6 @@ static inline std::string PlatformUtilsGetSecureEnv(const char* /* name */) {
return {};
}
-static inline bool PlatformUtilsSetEnv(const char* /* name */, const char* /* value */) {
- // Stub func
- return false;
-}
-
// Intended to be only used as a fallback on Android, with a more open, "native" technique used in most cases
static inline bool PlatformGetGlobalRuntimeFileName(uint16_t major_version, std::string& file_name) {
// Prefix for the runtime JSON file name
@@ -385,6 +357,37 @@ static inline bool PlatformGetGlobalRuntimeFileName(uint16_t major_version, std:
return false;
}
+
+// Android system properties are sufficiently different from environment variables that we are not re-using
+// PlatformUtilsGetEnv for this purpose
+static inline std::string PlatformUtilsGetAndroidSystemProperty(const char* name) {
+ std::string result;
+ const prop_info* pi = __system_property_find(name);
+ if (pi == nullptr) {
+ return {};
+ }
+
+#if __ANDROID_API__ >= 26
+ // use callback to retrieve > 92 character sys prop values, if available
+ __system_property_read_callback(
+ pi,
+ [](void* cookie, const char*, const char* value, uint32_t) {
+ auto property_value = reinterpret_cast<std::string*>(cookie);
+ *property_value = value;
+ },
+ reinterpret_cast<void*>(&result));
+#endif // __ANDROID_API__ >= 26
+ // fallback to __system_property_get if no value retrieved via callback
+ if (result.empty()) {
+ char value[PROP_VALUE_MAX] = {};
+ if (__system_property_get(name, value) != 0) {
+ result = value;
+ }
+ }
+
+ return result;
+}
+
#else // Not Linux, Apple, nor Windows
static inline bool PlatformUtilsGetEnvSet(const char* /* name */) {
@@ -402,11 +405,6 @@ static inline std::string PlatformUtilsGetSecureEnv(const char* /* name */) {
return {};
}
-static inline bool PlatformUtilsSetEnv(const char* /* name */, const char* /* value */) {
- // Stub func
- return false;
-}
-
static inline bool PlatformGetGlobalRuntimeFileName(uint16_t /* major_version */, std::string const& /* file_name */) {
// Stub func
return false;